- Release: v0.0.2
- Published time: 10/22/2023
Fire is a open source query language that enables 10x faster and simpler NoSQL query writing.
Play around with the IDE, Fire Thrower!
Fire is a simple, easy-to-learn query language, explicitly designed for interacting with NoSQL databases, akin to the role SQL plays for relational databases. It facilitates seamless connections with various NoSQL databases, including Google Cloud Firestore, MongoDB, and AWS DynamoDB, using a unified syntax.
Fire’s syntax blends programming elements with SQL functions, ensuring that anyone familiar with languages like JavaScript, Python, Java, etc., can learn and use Fire with minimal hassle and without needing to constantly refer to online resources to find the right SQL syntax.
Fire Thrower, the dedicated Integrated Development Environment (IDE) for Fire, enables users to execute Fire queries without the necessity of creating a distinct program solely for querying. Currently, Fire Thrower is in a closed beta phase and is restricted to a pre-designated database in Google Cloud Firestore that we have provided.
In essence, Fire and Fire Thrower are designed to make database interaction more accessible and intuitive, lowering the barriers and making it simpler to work with different databases. The goal is to provide a universal approach to querying languages, facilitating easier navigation through various NoSQL databases with a consistent and intuitive syntax.
- In Fire Thrower's top bar, click the settings button (represented by a gear icon), located adjacent to the documentation button (indicated by a help icon).
- A side panel will emerge.
- Activate the 'Use third-party Cloud Firestore' toggle.
- Navigate to your Firebase Console, then select Project Settings > General > Your Apps > Web App > firebaseConfig, and copy your Firebase credentials.
- Paste these credentials into Fire Thrower and hit the 'Save' button.
Fire Thrower ensures the security of your Firebase credentials by storing and encrypting them directly in your browser. This local storage is exclusively for enabling Fire Thrower's connection to your Firestore database.
For testing purposes, we furnish a sample database schema. Find the structure below for reference.
{
"cities": {
"london": {
"country": "UK",
"has_lakes": true,
"neighboring_cities": ["Chelsea", "Oxford", "Cambridge"],
"population": 123123123
},
"seattle": {
"country": "USA",
"has_lakes": true,
"neighboring_cities": ["San Francisco", "Los Angeles", "Oakland"],
"population": 1500000
},
"tokyo": {
"country": "Japan",
"has_lakes": false,
"neighboring_cities": ["Osaka", "Nagoya", "Kyoto"],
"population": 2000000
}
}
}
This example helps illustrate how your data is organized within the database, offering an intuitive starting point for your explorations.
<collection>.get();
Fire
cities.get();
JavaScript
import { collection, getDocs } from "firebase/firestore";
const querySnapshot = await getDocs(collection(db, "cities"));
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.<document>[].get();
Fire
cities.[tokyo, seattle].get();
JavaScript
import { collection, documentId, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where(documentId(), "in", ["tokyo", "seattle"])
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.<document>.get();
Fire
cities.tokyo.get();
JavaScript
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "cities", "tokyo");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
} else {
console.log("No such document!");
}
<collection>.where(<field> == <value>).get();
Fire
cities.where(population == 1500000).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("population", "==", 1500000)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> != <value>).get();
Fire
cities.where(population != 1500000).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("population", "!=", 1500000)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> > <value>).get();
Fire
cities.where(population > 1500000).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("population", ">", 1500000)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> >= <value>).get();
Fire
cities.where(population >= 1500000).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("population", ">=", 1500000)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> < <value>).get();
Fire
cities.where(population < 1500000).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("population", "<", 1500000)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> <= <value>).get();
Fire
cities.where(population <= 1500000).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("population", "<=", 1500000)
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> in <value>[]).get();
Fire
cities.where(country in ['UK', 'Japan']).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("country", "in", ["UK", "Japan"])
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<field> out <value>[]).get();
Fire
cities.where(country out ['USA', 'Germany']).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("country", "not-in", ["USA", "Germany"])
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<value> in <field>[]).get();
Fire
cities.where('English' in spoken_languages).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("spoken_languages", "array-contains", "English")
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.where(<value>[] in <field>[]).get();
Fire
cities.where(['English','German'] in spoken_languages).get();
JavaScript
import { collection, getDocs, query, where } from "firebase/firestore";
const q = query(
collection(db, "cities"),
where("spoken_languages", "array-contains-any", ["English", "German"])
);
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data());
});
<collection>.<document>.rename(<value>);
Fire
cities.tokyo.rename('osaka');
JavaScript
import { doc, getDoc, setDoc, deleteDoc } from "firebase/firestore";
const docRef = doc(db, "cities", "tokyo");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const docData = docSnap.data();
const newDocRef = doc(db, "cities", "osaka");
await newDocRef.setDoc(docData);
await deleteDoc(docRef);
} else {
console.log("Document not found");
}
<collection>.<document>.<field>.rename(<value>);
Fire
cities.tokyo.country.rename('nation');
JavaScript
import { doc, getDoc, deleteField, updateDoc } from "firebase/firestore";
const docRef = doc(db, "cities", "tokyo");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
const docData = docSnap.data();
if(!docData.has("country")) {
console.log("Field not found");
} else {
const fieldValue = docData["country"];
await updateDoc(docRef, {
country: deleteField(),
nation: fieldValue
}
}
} else {
console.log("Document not found");
}
The output will be presented in Fire’s pair (key-value) format, a JSON-like format. We have introduced the '->' symbol to provide a clear distinction between the ID and the data. Also, to enhance readability, we have also eliminated curly brackets '{ }' at the top level, using square brackets as the primary notation for pairs and lists.
Output in Fire
london -> [
neighbor_cities -> ['chelsea', 'oxford', 'cambridge'],
population -> 123123123,
country -> 'uk',
has_lakes -> true
],
seattle -> [
population -> 1500000,
has_lakes -> true,
neighbor_cities -> ['san francisco', 'los angeles', 'oakland'],
country -> 'usa'
],
tokyo -> [
neighbor_cities -> ['osaka', 'nagoya', 'kyoto'],
population -> 2000000,
country -> 'japan',
has_lakes -> false,
birth_rate -> 0.89
]
JSON Output in JSON
"london": {
"neighbor_cities": ["chelsea", "oxford", "cambridge"],
"population": 123123123,
"country": "uk",
"has_lakes": true
},
"seattle": {
"population": 1500000,
"has_lakes": true,
"neighbor_cities": ["san francisco", "los angeles", "oakland"],
"country": "usa"
},
"tokyo": {
"neighbor_cities": ["osaka", "nagoya", "kyoto"],
"population": 2000000,
"country": "japan",
"has_lakes": false,
"birth_rate": 0.89
}
- WithValues
- code: Z001
- message: No message, but it means the process succeeded with values.
- WithoutValues
- code: Z002
- message: No message, but it means the process succeeded without values.
- DocumentRenamed
- code: Z003
- message: Success: Document $docId in collection $collectId has been successfully renamed to $newName.
- FieldRenamed
- code: Z004
- message: Success: Field $fieldId in $docId, collection $collectId has been successfully renamed to $newName.
- CollectionInvalidError
- code: G001
- message: CollectionInvalidError: Collection id is not found or invalid.
- DocumentInvalidError
- code: G002
- message: DocumentInvalidError: Document id is not found or invalid.
- InvalidConditionOperatorError
- code: G003
- message: InvalidConditionOperatorError: Condition does not contain any of
>=
,<=
,>
,<
,==
,in
,out
.
- UnsupportedConditionFormatError
- code: G004
- message: UnsupportedConditionFormatError: Condition contains unsupported formats.
- ImcompleteConditionError
- code: G005
- message: ImcompleteConditionError: Either field, operator, or value is missing.
- ValueNullError
- code: G006
- message: ValueNullError: Value cannot be null.
- NullInFieldArrayError
- code: G007
- message: NullInFieldArrayError: Elements in field array cannot be null.
- NullInValueArrayError
- code: G008
- message: NullInValueArrayError: Elements in value array cannot be null.
- DocumentNotFoundError
- code: G009
- message: DocumentNotFoundError: The document does not exist.
- FieldNotFoundError
- code: G010
- message: FieldNotFoundError: The field does not exist.
-
InvalidInputError
- code: R001
- message: InvalidInputError: The input does not match any parsers in Fire Interpreter. Please check your syntax if whether it is correct.
-
UnknownTypeError
- code: R002
- message: UnknownTypeError: The variable type is unknown.
-
FirebaseError
- code: R003
- message:
const title = 'FirebaseError:'; errorMessage == null ? '$title No error messages available' : '$title $errorMessage';
-
RuntimeError
- code: R004
- message:
const title = 'RuntimeError:'; errorMessage == null ? '$title No error messages available' : '$title $errorMessage';
- RenameValueInvalidError
- code: P001
- message: RenameValueInvalidError: Rename value is invalid.
- FieldAlreadyExistsError
- code: P002
- message: FieldAlreadyExistsError: The field already exists.
- DocumentAlreadyExistsError
- code: P003
- message: DocumentAlreadyExistsError: The document already exists.