I am using two different queries to order my data and putting a where clause. I need a composite index. I am also using limitToLast
const tradesRef = firebase
.firestore()
.collection("trades")
.orderBy("time", "asc")
.where("type", "==", "fiveMinutes")
.limit(15);
I have created a composite index such as:
I am still getting this error:
The fields indexed seem to be correct but you are not running a collection group query so have you tried setting the query scope to just "Collection" instead of "Collection group"?
Also try deleting current index and creating new index directly using the link in error.
As mentioned in the stackoverflow Answer :
Since you're querying on two fields (status and createDate), there
needs to be a composite index on those two fields. Indices on
individual fields are automatically created, but composite indexes are
only created when you ask for them. The error message should contain a
link directly to the console to complete that task. If that's not the
case, you can create it here.
For more information related to how firestore manages indexes, you can refer to the documentation.
Related
I have this field for the collection in users with either 2 status of condition1 and condition2. And I also wanted to order this in descending order. I tried it with this but it will display that I first need to create an index.
The status is in string. And the createdAt is in timestamp
const userRef = collection(db, "users");
const q = query(
ordersRef,
where("status", "==", "Condition1"),
orderBy("createdAt", "desc")
);
const querySnapshot = await getDocs(q);
Also, is it necessary to filter this with Firebase or I could just filter using Javascript? However, I would still like to know how I can use indexing here.
If you attempt the query above without first creating the required index, Cloud Firestore returns an error message containing a link you can follow to create the missing index.
This happens any time you attempt a query not supported by an index. You can also define and manage composite indexes which store a sorted mapping of all the documents in a collection, based on an ordered list of fields to index manually by using the console or by using the Firebase CLI.
Cloud Firestore does not automatically create composite indexes like it does for single-field indexes because of the large number of possible field combinations. Instead, Cloud Firestore helps you identify and create required composite indexes as you build your app. Follow the generated link to the Firebase console, review the automatically populated info, and click Create. Indexes can take a few minutes to build, depending on the size of the query.
After you create them, you can see your indexes and their status in the Composite Indexes section. If they're still building, the Firebase console includes a building status bar.
I have a Firestore collection named channels, and I'd like to get the list of channels based on an array of IDs and order it by the createdAt field, this is my function :
const getChannels = () => {
const q = query(
collection(db, "channels"),
where(documentId(), "in", [
"F0mnR5rNdhwSLPZ57pTP",
"G8p6TWSopLN4dNHJLH8d",
"wMWMlJwa3m3lYINNjCLT",
]),
orderBy("createdAt")
);
const unsubscribe = onSnapshot(q, (snapshot) => {
snapshot.docs.map((doc) => {
console.log(doc.data());
});
});
return unsubscribe;
};
But I'm getting this error
FirebaseError: inequality filter property and first sort order must be the same: __name__ and createdAt.
It only works if I orderBy documentId().
I'm aware there is a limitation in the docs about this, but I'm wondering if there is a workaround for this type of situation.
Also the answer for this question isn't working anymore I guess.
The title of your question indicates that you are trying to use where and orderBy for different fields. But note that you are using documentId() in the where condition to filter, which is not a field in the Firestore document.
So if you filter is based on documentId(), you can use only documentId() in orderBy() clause, that also in ascending order because currently Firestore does not support sorting in descending order of documentId() which is mentioned in this answer.
Let’s take a look at the following examples -
const data=await db.collection("users").where(admin.firestore.FieldPath.documentId(),"in",["104","102","101"]).orderBy(admin.firestore.FieldPath.documentId()).get();
The above will work and sort the documents based on documentId() after filtering based on documentId().
But it is not relevant to apply an orderBy() clause based on the documentId(), because without applying the orderBy() clause also yields the same result as, by default, Firestore query gives documents in ascending order of documentId(). That means the following also yields the same result -
const data=await db.collection("users").where(admin.firestore.FieldPath.documentId(),"in",["104","102","101"]).get();
Now Firestore doesn’t support to sort in descending order of documentId() which means the following will not work -
const data=await db.collection("users").where(admin.firestore.FieldPath.documentId(),"in",["104","102","101"]).orderBy(admin.firestore.FieldPath.documentId(),"desc").get();
This will ask to create an index -
The query requires an index. You can create it here:
But if you go there to create an index it will say -
__name__ only indexes are not supported.
Now let's come to your query. What you are trying to do is to filter based on documentId() and then orderBy() based on createdAt field which is not possible and it will give the following error-
inequality filter property and first sort order must be the same.
You may think to use two orderBy() clauses, something like this -
const data=await db.collection("users").where(admin.firestore.FieldPath.documentId(),"in",["104","102","101"]).orderBy(admin.firestore.FieldPath.documentId()).orderBy(“createdAt”
).get();
Which will not work and give the following error
order by clause cannot contain more fields after the key
I am not sure of your use case but it’s not a great idea to filter based on documentId(). If it is required to filter based on documentId(), I would suggest creating a field in the Firestore document which will contain the documentIds and filter based on that.
Now considering the title of the question, yes it is possible to use where() and orderBy() clauses for different fields in Firestore. There are some limitations and you need to stick to that -
If you include a filter with a range comparison (<, <=, >, >=), your first ordering must be on the same field.
const data=await db.collection("users").where(“number”,">=", “101”).orderBy(“createdAt”).get();
The above query doesn't work.
const data=await db.collection("users").where(“number”,">=", “101”).orderBy(“number”).get();
The above query works and you can still use further orderBy() on different fields, something like following -
const data=await db.collection("users").where(“number”,">=", “101”).orderBy(“number”).orderBy(“createdAt”).get();
You cannot order your query by any field included in an equality (=) or in clause.
const data=await db.collection("users").where(“number”,"in",["104","102","101"]).orderBy(“number”).get();
const data=await db.collection("users").where(“number”,"==", “101”).orderBy(“number”).get();
The above two don’t work.
Firestore's speed and efficiency comes almost ENTIRELY from it's use of indexes. Inequalities (INCLUDING in and not-in) are accomplished by sorting by the index, and using the value as a "cut-off" - thus REQUIRING (whether you want it or not) the orderby() to be on the same field as the inequality.
The "answer not working anymore" was never really working in the first place, as the above shows. If you aren't trying to paginate, do the obvious and "filter" by the document ID's and sort on the client.
BUT...
...more importantly, it is ALMOST NEVER useful nor performant to use documentId's to select from the database, unless you both copy it to a field, AND are looking for a SPECIFIC id. In almost all cases, it would be FAR better to use a query on another field (however you got the list of documentId's in the first place), then orderBy. Yes, the inequality/orderBy is a limitation, but it's there for a reason.
Going forward, an important design decision is to understand what questions you want your data to answer, and design your entire database schema to support those queries - this is the fundamental nature of NoSQL.
Problem:The other link that you have shared before perfectly works and the only solutions available is to create an index. However the reason you are not able to do a where and order with the above example is because you cannot create an index with the document id and createdAt.
Solution: To do so add the document id as one of the field say docID in the document then create an index with the fields docID and createdAt. This should be working for you.
Note: I have not physically tested this. Will update once I have checked it
I am storing the firebase cloud messaging registration tokens for the users in the firestore users collection and whenever I'm sending a notification, I'm deleting the tokens which are unused. I'm doing this on the cloud function with admin sdk. The code I'm using is-
let removeRegistrationTokens = async function (userId: string, tokens: string[]) {
await db.collection('users')
.doc(userId)
.update("tokens",admin.firestore.FieldValue.arrayRemove(tokens)
.catch(logError)
}
But this is not working and no value is being deleted from the array. I checked the tokens array and the value passed is correct. If I simply change the code to
.update("tokens",admin.firestore.FieldValue.arrayRemove(tokens[0])
This is deleting the first element in the array from the tokens array in firestore. As per the documentation it should delete all the elements passed to it in the array.
If your document contains an array field, you can use arrayUnion() and arrayRemove() to add and remove elements. arrayUnion() adds elements to an array but only elements not already present. arrayRemove() removes all instances of each given element.
Any help on why could it be happening?
arrayUnion and arrayRemove don't accept array arguments. If you want to pass multiple values, they need to be specified as individual arguments. You could try using the JavaScript spread operator to expand that array into multiple args.
arrayRemove(...tokens)
See also the API documentation for arrayRemove.
I have a firestore firebase database , in which I have a collection users
there is an array in the collection and in the array there is a map
in map there is a field qty.. I want to increment that qty value..
using increment doesnt help as the qty is inside a array index
db.collection("users").doc(checkId).update({
myCart: firebase.firestore.FieldValue.arrayUnion({
qty: firebase.firestore.FieldValue.increment(1),
}),
this is the error Output =>
Uncaught (in promise) FirebaseError: Function FieldValue.arrayUnion() called with invalid data. FieldValue.increment() can only be used with update() and set()
My answer below won't work, given that the qty is in an array. The only way to update an item in an array is to read the entire document, update the item in the array, and then write the entire array with the updated item back to the document.
An alternative would be to use a map instead of an array, and then update the qty using the approach outlined in my (old, and non-working) answer below 👇
You need to specify the full path to the field you're trying to update. So I think in your case, that'll be:
db.collection("users").doc(checkId).update({
"myCart.0.qty": firebase.firestore.FieldValue.increment(1)
}),
The field you want to update is embedded in an array. In this case, you can't use FieldValue.increment(), since it's not possible to call out an array element as a named field value.
What you'll have to do instead is read the entire document, modify the field in memory to contain what you want, and update the field back into the document. Also consider using a transaction for this if you need to update to be atomic.
(If the field wasn't part of an array, you could use FieldValue.increment().)
As of today (29-04-2020)... this is tested by me.
Suppose my data structure is like this:
collection: Users
Any document: say jdfhjksdhfw
It has a map like below
map name: UserPageVisits
map fields: field1,field2,field3 etc
Now we can increment the number field in the map like below:
mapname.field1 etc...
That is use the dot operator to access the fields inside the map just like you would do to an object of javascript.
JAVA Code (Android), update the field using transactions so they can complete atomically.
transaction.update(<documentreference object>,"UserPageVisits.field1",FieldValue.increment(1));
I have just pushed a version of my app which uses this concept and it's working.
Kudos !!
My Best Regards
Previous answers helped me as well, but dont forget about the "merge" property!!! Otherwise it will overwrite your entire array, losing other fields.
var myIndex = 0;
const userRef = db.collection('users').doc(checkId);
return userRef.update({
'myCart.${myIndex}.qty': admin.firestore.FieldValue.increment(1)
}, {
merge: true
});
I get this error once I added the Where method, but it works without the Where clause. In collection every document has Boolean called "status".
db.firestore().collection('jobs').where("status","==",true).orderBy("createDate").limit(10).get().then(querySnapshot =>{
})
})
All Help is Appreciated. Thanks!
Since you're querying on two fields (status and createDate), there needs to be a composite index on those two fields. Indices on individual fields are automatically created, but composite indexes are only created when you ask for them.
The error message should contain a link directly to the console to complete that task. If that's not the case, you can create it here.
Firestore query on more than two fields requires a composite index on that two filed.
So you have to create composite index status and createDate.You will get the error like following when you don't have an index.
ERROR Error: The query requires an index. You can create it here: https://console.firebase.google.com/project/admin-e8a7b/database/firestore/indexes?create_index=EgR0ZW1wGgcKA3VpZBACGg0KCXN0YXJ0ZWRBdBADGgwKCF9fbmFtZV9fEAM
at new FirestoreError (vendor.bundle.js:19925)
So when you click this link in error index will be created automatically.
You can also manually create index from firebase console
To combine the equality operator (==) with a range comparison (<, <=, >, or >=), make sure to create a custom index.