I am using a where clause on my firestore query to filter based on an attribute type and then ordering them using the orderBy to order them by time.
const tradesRef = firebase
.firestore()
.collection("cex-trades")
.orderBy("time", "desc")
.limit(16);
useEffect(() => {
if (filter === "5m") {
tradesRef
.where("type", "==", "fifteenMinutes")
.get()
.then((collections) => {
const tradesData = collections.docs.map((trade) => trade.data());
const lastDoc = collections.docs[collections.docs.length - 1];
const firstDoc = collections.docs[collections.docs.length - 16];
setTrades(tradesData);
setFirstTrade(firstDoc);
setLastTrades(lastDoc);
})
.catch((error) => {
console.error(error);
});
}
}, [filter]);
When I run my code, I am getting this error:
FirebaseError: The query requires an index. You can create it here: https://console.firebase.google.com/v1/r/project/whaletracer-432e8/firestore/indexes?create_composite=ClRwcm9qZWN0cy93aGFsZXRyYWNlci00MzJlOC9kYXRhYmFzZXMvKGRlZmF1bHQpL2NvbGxlY3Rpb25Hcm91cHMvY2V4LXRyYWRlcy9pbmRleGVzL18QARoICgR0eXBlEAEaCAoEdGltZRACGgwKCF9fbmFtZV9fEAI
I followed the link and created an index as followed:
After creating the index, I am still getting the same error. Any idea how to solve the issue?
So, i want to query some data from firestore.
this is my data structure
so, the collection is Modules, then i now have 2 documents but it will be 75 or something. Then in that document i want to get the specific document which has a specific LessonId (In this example '2')
How do i query this?
this is wat i tries but it's not working for me
async function getModuleData() {
let ModuleData = await firebase
.firestore()
.collection('Modules')
.where('Lessons', 'array-contains', {LessonId: 2})
.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
})
});
} getModuleData()
when i do this
async function getModuleData() {
let ModuleData = await firebase
.firestore()
.collection('Modules')
.where('Title', '==', 'Leven vanuit verlossing')
.get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.data())
})
});
} getModuleData()
it just works so it's something with my where statement i guess?
To use array-contains with an array of objects, you need to pass the complete object you are looking for in that array.
For example,
const lessonObj = {
Title: "Leven vanuit verlossing",
Description: "the desc",
...allTheOtherFieldsAsIs
}
firebase.firestore().collection("Modules").where("Lessons", "array-contains", lessonObj)
You should ideally use a sub-collection to store lessons in a module. Then you can easily query lessons using the following query:
const db = firebase.firestore()
const lessonsSnapshot = await db.collection("Modules")
.doc("moduleID")
.collection("Lessons")
.where("Title", "==", "Leven vanuit verlossing")
.get()
console.log(lessonsSnapshot.docs[0].data())
As Dharmaraj answered, the array-contains operator performs a complete match, so it only returns documents where the array contains the exact value you specified.
If you only want to filter on lesson IDs, I'd recommend adding an additional field to each document with just the lesson IDs. You can then filter on that field with:
firebase
.firestore()
.collection('Modules')
.where('LessonsIDs', 'array-contains', 2)
I've a collection called users, inside each document's users have a collection called monthlies and I want get it.
This is the structure:
At now, I tried get it using:
var getUsers = async function() {
var db = firebase.firestore()
var users = await firebase
.firestore()
.collection("users")
.get();
return users
}
var getMonthlyByUserId = async function () {
var users = await getUsers()
users.forEach(element => {
var monthlies = element.collection('monthlies').get()
console.log(monthlies.docs.map(doc => doc.data()))
})
}
But it prints nothing. The goal is iterate of all documents' monthlies of the collection.
In addition to the problem that Doug pointed out (you need to use the ref property of the QueryDocumentSnapshot), you need to take into account that the get() method is asynchronous.
So doing
users.forEach(snapshot => {
var monthlies = snapshot.ref.collection('monthlies').get()
console.log(monthlies.docs.map(doc => doc.data()))
})
will not work.
If you cannot use a collection group query (for example, let's imagine that your getUsers() function only returns a subset of all the users, e.g. all users of a given country) you could use Promise.all() as follows:
var getMonthlyByUserId = async function () {
const users = await getUsers();
const promises = [];
users.forEach(snapshot => {
promises.push(snapshot.ref.collection('monthlies').get());
});
const monthlies = await Promise.all(promises);
monthlies.forEach(snapshotArray => {
console.log(snapshotArray.docs.map(doc => doc.data()));
});
}
OR you could use the technique described in this article on how to use async/await inside a forEach().
In your code, element is a QueryDocumentSnapshot type object. It doesn't have a method called collection(), so I would expect your code will crash with an error in the log.
If you want to reference a subcollection organized under a document represented by QueryDocumentSnapshot, you should build upon its ref property:
users.forEach(snapshot => {
var monthlies = snapshot.ref.collection('monthlies').get()
console.log(monthlies.docs.map(doc => doc.data()))
})
Alternatively, if you just want to query all documents in all subcollections called "monthly", you can simplify that with a single collection group query.
I want to query a firestore database for document id. Currently I have the following code:
db.collection('books').where('id', '==', 'fK3ddutEpD2qQqRMXNW5').get()
I don't get a result. But when I query for a different field it works:
db.collection('books').where('genre', '==', 'biography').get()
How is the name of the document id called?
I am a bit late, but there is actually a way to do this
db.collection('books').where(firebase.firestore.FieldPath.documentId(), '==', 'fK3ddutEpD2qQqRMXNW5').get()
This might be useful when you're dealing with firebase security rules and only want to query for the records you're allowed to access.
Try this:
db.collection('books').doc('fK3ddutEpD2qQqRMXNW5').get()
(The first query is looking for an explicit user-set field called 'id', which probably isn't what you want.)
You can use the __name__ key word to use your document ID in a query.
Instead of this db.collection('books').doc('fK3ddutEpD2qQqRMXNW5').get() you can write
db.collection('books').where('__name__', '==' ,'fK3ddutEpD2qQqRMXNW5').get().
In this case you should get an array of length 1 back.
The firebase docs mention this feature in the rules documentation. https://firebase.google.com/docs/reference/rules/rules.firestore.Resource
June, 2021
The new v9 modular sdk is tree-shakeable and results in smaller compiled apps. It is recommended for all new Firestore apps.
import { doc, getDoc } from "firebase/firestore";
const snap = await getDoc(doc(db, 'books', 'fK3ddutEpD2qQqRMXNW5'))
if (snap.exists()) {
console.log(snap.data())
}
else {
console.log("No such document")
}
This is based on the example from the firestore docs
import { doc, getDoc } from "firebase/firestore";
const docRef = doc(db, "cities", "SF");
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
console.log("Document data:", docSnap.data());
}
else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
You could make this into a helper function
async function getDocument (coll, id) {
const snap = await getDoc(doc(db, coll, id))
if (snap.exists())
return snap.data()
else
return Promise.reject(Error(`No such document: ${coll}.${id}`))
}
getDocument("books", "fK3ddutEpD2qQqRMXNW5")
You can get a document by its id following this pattern:
firebase
.firestore()
.collection("Your collection")
.doc("documentId")
.get()
.then((docRef) => { console.log(docRef.data()) })
.catch((error) => { })
While everyone is telling to use .get(), which is totally reasonable but it's not always the case.
Maybe you want to filter data based on id (using a where query for example).
This is how you do it in Firebase v9 modular SDK:
import {collection, documentId} from 'firebase/firestore'
const booksRef = collection('books')
const q = query(booksRef, where(documentId(), '==', 'fK3ddutEpD2qQqRMXNW5'))
Currently only working way for Cloud Functions if you really need to use this way:
// Import firebase-admin
import * as admin from "firebase-admin";
// Use FieldPath.documentId()
admin.firestore.FieldPath.documentId()
const targetUser = await db.collection("users").where(admin.firestore.FieldPath.documentId() "==", "givenId").get();
Simpler way of this is directly using ID value thru path as there is only one document with given document ID:
const targetUser = await db.doc("users/"+ "givenId").get();
However, you may really need to use it if you are matching given IDs array to the Firebase collection like this:
const admin = require("firebase-admin");
const arr = ["id1", "id2"];
const refArr = arr.map(id => admin.firestore().collection("media").doc(id));
const m = await admin
.firestore()
.collection("media")
.where(admin.firestore.FieldPath.documentId(), "in", refArr)
.get();
This last example is from this discussion
If you are looking for more dynamic queries with a helper function, you can simply try this.
import { db} from '#lib/firebase';
import {query, collection, getDocs ,documentId } from "firebase/firestore";
const getResult = async (_value) => {
const _docId = documentId()
const _query = [{
field: _docID,
operator: '==',
value: _value
}]
// calling function
const result = await getDocumentsByQuery("collectionName", qColl)
console.log("job result: ", result)
}
// can accept multiple query args
const getDocumentsByQuery = async (collectionName, queries) => {
const queryArgs = [];
queries.forEach(q => {
queryArgs.push(
where(q.field, q.operator, q.value)
);
});
const _query = query(collection(db, collectionName), ...queryArgs);
const querySn = await getDocs(_query);
const documents = [];
querySn.forEach(doc => {
documents.push({ id: doc.id, ...doc.data() });
});
return documents[0];
};
From Firestore docs for Get a document.
var docRef = db.collection("cities").doc("SF");
docRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
This is the first link that came up when I was looking to solve it in the Golang SDK, so I'll add my solution in case anyone else is looking for it:
package main
import (
"context"
"fmt"
"log"
"cloud.google.com/go/firestore"
firebase "firebase.google.com/go/v4"
"google.golang.org/api/option"
)
type (
Car struct {
ID string
Name string `firestore:"name"`
Make string `firestore:"make"`
Price float64 `firestore:"make"`
}
)
func main() {
ctx := context.Background()
// Use a service account
options := option.WithCredentialsFile("PATH/TO/SERVICE/FILE.json")
// Set project id
conf := &firebase.Config{ProjectID: "PROJECT_NAME"}
// Initialize app
app, err := firebase.NewApp(ctx, conf, options)
if err != nil {
log.Fatal(err)
}
// Get firestore client
client, err := app.Firestore(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Close()
collectionRef := client.Collection("CAR_COLLECTION")
// firestore.DocumentID == "__name__"
docSnap, err := collectionRef.Where(firestore.DocumentID, "==", collectionRef.Doc("001")).Get(ctx)
if err != nil {
log.Fatal(err)
}
// Unmarshall item
car := Car{}
docSnap.DataTo(&car)
car.ID = docSnap.Ref.ID
// Print car list
fmt.Println(car)
}
Just to clear confusion here
Remember, You should use async/await to fetch data whether fetching full collection or a single doc.
async function someFunction(){
await db.collection('books').doc('fK3ddutEpD2qQqRMXNW5').get();
}
I try to put a listener on Firebase that will replicate a value in the matching element in Firestore.
exports.synchronizeDelegates = functions.database.ref(`delegates/{userId}/activities`).onUpdate((event) => {
const userKey = event.data.ref.parent.key
console.log("User Key:" + userKey)
return admin.database().ref(`delegates/${userKey}/email`).once('value', snapshot => {
let email = snapshot.val()
console.log("Exported Email:" + email)
const userRef = admin.firestore().collection('users')
const firestoreRef = userRef.where('email', "==", email)
firestoreRef.onSnapshot().update({ activities: event.data.toJSON() })
}).then(email => {
console.log("Firebase Data successfully updated")
}).catch(err => console.log(err))
}
)
This function is able to retrieve and locate the elemnt needed to target the right document in firestore, but the .update()function still error firestoreRef.update is not a function
I try several ways to query but I still have this error.
How to properly query then update a document in this scenario?
The onSnapshot() method of Query introduces a persistent listener that gets triggered every time there's a new QuerySnapshot available. It keeps doing this until the listener is unsubscribed. This behavior is definitely not what you want. Also, there's no update() method on QuerySnapshot that your code is trying to call.
Instead, it looks like you want to use get() to fetch a list of documents that match your query, then update them all:
exports.synchronizeDelegates = functions.database.ref(`delegates/{userId}/activities`).onUpdate((event) => {
const userId = event.params.userId
console.log("User Key:" + userKey)
return admin.database().ref(`delegates/${userId}/email`).once('value', snapshot => {
let email = snapshot.val()
console.log("Exported Email:" + email)
const usersRef = admin.firestore().collection('users')
const query = usersRef.where('email', "==", email)
const promises = []
query.get().then(snapshots => {
snapshots.forEach(snapshot => {
promises.push(snapshot.ref.update(event.data.val()))
})
return Promise.all(promises)
})
}).then(email => {
console.log("Firebase Data successfully updated")
}).catch(err => console.log(err))
}
Note that I rewrote some other things in your function that were not optimal.
In general, it's a good idea to stay familiar with the Cloud Firestore API docs to know what you can do.