This question already has answers here:
How to use array-contains operator with an array of objects in Firestore?
(2 answers)
Firestore - query where filter of object in array field
(1 answer)
Closed 10 days ago.
I need to write a query for Firestore which will filter records based on id in the array, so I have a collection of "assignments" and each assignment has records more or less like this:
{
"name" :"abc",
"status": "accepted",
"assignedAt": "timestamp",
"createdAt": "timestamp",
"recipients": [
{
id: "123",
name: "recipient1"
},
{
id: "456",
name: "recipient2"
}
]
}
I wish to write a query that will check if user.id exists as an id in the recipients array, except that I also filter out some statuses but this part works, so far I have something like this:
assignmentCollection
// .where('recipients', 'array-contains', user!.id)
.where('status', 'not-in', recipientAssignmentSkippableStatuses)
.orderBy('status', 'desc')
.orderBy('assignedAt', 'desc')
.orderBy('createdAt', 'asc')
I tried to use something called "array-contains" but:
a) it didn't work
b) it cause error that cannot be used together with "not-in"
EDIT: I found out that array-contains doesn't work as I need to pass the whole object for that, thing is that at this moment I know only the ID of the user so I need to find some workaround
I found out that array-contains doesn't work as I need to pass the whole object for that, the thing is that at this moment I know only the ID of the user so I need to find some workaround.
Yes, in order to return documents that contain a particular object in the array, you need to pass the entire object as an argument, not only a single field.
If you need to filter based on the user ID, then you should consider creating an additional array that only contains user IDs. Then you'll be able to call:
.where('userIds', 'array-contains', user!.id)
Related
This question already has answers here:
How to use array-contains operator with an array of objects in Firestore?
(2 answers)
Closed last year.
This is my query:
ref.where(
"tags",
"array-contains-any",
[{ tagTitle: 'webdev' }],
)
I know that this query works because if I try it with a 'simple' query, it works.
For example:
ref.where(
"type",
"==",
"post",
)
However, is there anything else that I have to change than the query, because right now, this is the only thing that I have changed?
This is my firestore collection:
As you can see, there is a tags field, and I want to select the tagTitle field.
I want to show all the documents with that query, this has worked with the second example. (the type == post) one.
But right now I don't get an error, but it also doesn't show in my website.
Only if you pass both the id and the tagTitle will it work.
ref.where('tags', 'array-contains-any', [{ id: 480170, tagTitle: 'webdev' }]);
I have a field named cartons in a document which is an array of maps.
**each document in a collection
**document-1
{ lotNo: 'alg-100',
cartons:[
{cartonNo:01, trackingNo:'a'},
{cartonNo:02, trackingNo:'b'},
{cartonNo:03, trackingNo:'c'},
{cartonNo:04, trackingNo:'a'}
]
}
**document-2
{ lotNo: 'alg-101',
cartons:[
{cartonNo:01, trackingNo:'a'},
{cartonNo:02, trackingNo:'b'},
{cartonNo:03, trackingNo:'c'},
{cartonNo:04, trackingNo:'a'}
]
}
what I need is only the carton object where trackingNo='a' from each document. As far I know I can't get partial data from a firestore document(either get the full document on not). So to get the carton object of similar trackingNo from different documents I am assuming get both of the documents then filter the data in client-side. Or is there any better way? what could be the best possible solution for achieving only the carton that has a similar trackingNo(as an array) from different documents ( without changing the data structure as my app is heavily relying on this particular data structure)?
Unfortunately you can't index based on properties inside of an array. In the Realtime Database, properties of an array could be indexed as cartons.<index>.trackingNo (e.g. cartons.0.trackingNo), but if you queried this, you would only get documents that contain the requested tracking number as their first carton entry. To get all results, you would need to query again for each subsequent index - cartons.1.trackingNo, cartons.2.trackingNo, and so on.
If the data is as simple as you have shown, the best option would be to tweak your data structure slightly so that you also store a list of tracking numbers in the given lot. This will allow you to perform array-contains queries on a trackingNos property.
{
lotNo: 'alg-101',
cartons: [
{ cartonNo: 01, trackingNo: 'a' },
{ cartonNo: 02, trackingNo: 'b' },
{ cartonNo: 03, trackingNo: 'c' },
{ cartonNo: 04, trackingNo: 'a' }
],
trackingNos: [
'a',
'b',
'c'
]
}
If your data has been simplified to be posted here, you might be better off with a restructure of your database where each carton is a member of the lot document's subcollection called cartons (i.e. .../lots/alg-101/cartons/01) and combined with a collection group query.
How can I get a collection where the query should be applicable to an array inside the document.
Document example: I would like to know how to query the document where the brands are fiat and seat
{
"name":"test 1",
"brands":[
{
"brand":{
"id":1,
"name":"Fiat",
"slug":"fiat",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
},
{
"brand":{
"id":2,
"name":"Seat",
"slug":"seat",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
},
{
"brand":{
"id":3,
"name":"Mercedes",
"slug":"mercedes",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
},
{
"brand":{
"id":4,
"name":"Yamaha",
"slug":"yamaha",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
}
]
}
I have tried something like:
.collection("motors")
.where("brands.slug", "array-contains-any", ["fiat", "seat"])
but this is not working I cannot figure out by the documentation how to get this.
When using the array-contains-any operator, you can check the values of your array against the value of a property of type String and not an array. There is currently no way you can use array-contains-any operator on an array. There are two options, one would be to create two separate fields and create two separate queries or, been only a document, you can get the entire document and filter the data on the client.
Edit:
What #FrankvanPuffelen has commented is correct, I made some research and I found that we can check against any type and even complex types, not just against strings, as mentioned before. The key to solving this issue is to match the entire object, meaning all properties of that object and not just a partial match, for example, one of three properties.
What you are trying to achieve is not working with your current database structure because your slug property exists in an object that is nested within the actual object that exists in your array. A possible solution might also be to duplicate some data and add only the desired values into an array and use the array-contains-any operator on this new creatded array.
This question already has answers here:
Firestore - Nested query
(2 answers)
Closed 2 years ago.
I want to store data in following format:
{
"chatName": "Football",
"chatMembers":
[
{
"userId": "nSWnbKwL6GW9fqIQKREZENTdVyq2",
"name": "Niklas"
},
{
"userId": "V3QONGrVegQBnnINYHzXtnG1kXu1",
"name": "Timo"
},
]
}
My goal is to get all chats, where the signed in user with a userId is in the chatMembers list. If the userId of the signed in user is not in the chatMembers property, then that chat should be ignored. Is this possible?
If this is not possible, how can i achive this with subcollections?
My development language is dart, but you can also post solutions in other languages.
My current attempt is this, but this is not working:
_firestore.collection(collectionName).where("chatMembers.userId", isEqualTo: userId).snapshots()
Since August 2018 there is the new array_contains operator which allows filtering based on array values. The doc is here: https://firebase.google.com/docs/firestore/query-data/queries#array_membership
It works very well with arrays of string. However, I think it is not possible to query for a specific property of an object stored in the array. One workaround is to query for the entire object, as follows (in Javascript). Of course this may not be feasible in every situation....
var db = firebase.firestore();
var query = db.collection('chatDocs').where("chatMembers", "array-contains", { userId: "xyz", userName: "abc" });
Renaud Tarnec's, which complete, doesn't work in every case. Situations where only one or not all of the fields are known won't return the expected documents. However, by restructuring the data in the document, a query can be made to work where only one field is known, like a user identifier (uid).
Here's the data structure inside one of the document:
{
"members": {
"user4": {
"active": true,
"userName": "King Edward III",
"avatar": "www.photos.gov/animeGirl5.png"
},
"user7": {
"active": true,
"userName": "Dave K.",
"avatar": "www.photos.gov/gunsAmericanFlag.png"
}
}
Here's the query:
uid = 'user4';
collectionQuery = collectionReference.where(`members.${uid}.active`,"==", true);
In this example, "user4" represents a user who may or may not be in a group. This will return all documents in the collection where the uid "user4" is an active member. This works by only needing to know the UID of the member and works without needing to know their name or avatar uri ahead of time.
I want select from Firestore collection just articles written NOT by me.
Is it really so hard?
Every article has field "owner_uid".
Thats it: I JUST want to write equivalent to "select * from articles where uid<>request.auth.uid"
TL;DR: solution found already: usages for languages/platforms: https://firebase.google.com/docs/firestore/query-data/queries#kotlin+ktx_5
EDIT Sep 18 2020
The Firebase release notes suggest there are now not-in and != queries. (Proper documentation is now available.)
not-in finds documents where a specified field’s value is not in a specified array.
!= finds documents where a specified field's value does not equal the specified value.
Neither query operator will match documents where the specified field is not present. Be sure the see the documentation for the syntax for your language.
ORIGINAL ANSWER
Firestore doesn't provide inequality checks. According to the documentation:
The where() method takes three parameters: a field to filter on, a comparison operation, and a value. The comparison can be <, <=, ==, >, or >=.
Inequality operations don't scale like other operations that use an index. Firestore indexes are good for range queries. With this type of index, for an inequality query, the backend would still have to scan every document in the collection in order to come up with results, and that's extremely bad for performance when the number of documents grows large.
If you need to filter your results to remove particular items, you can still do that locally.
You also have the option of using multiple queries to exclude a distinct value. Something like this, if you want everything except 12. Query for value < 12, then query for value > 12, then merge the results in the client.
For android it should be easy implement with Task Api.
Newbie example:
FirebaseFirestore db = FirebaseFirestore.getInstance();
Query lessQuery = db.collection("users").whereLessThan("uid", currentUid);
Query greaterQuery = db.collection("users").whereGreaterThan("uid", currentUid);
Task lessQuery Task = firstQuery.get();
Task greaterQuery = secondQuery.get();
Task combinedTask = Tasks.whenAllSuccess(lessQuery , greaterQuery)
.addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//This is the list of "users" collection without user with currentUid
}
});
Also, with this you can combine any set of queries.
For web there is rxfire
This is an example of how I solved the problem in JavaScript:
let articlesToDisplay = await db
.collection('articles')
.get()
.then((snapshot) => {
let notMyArticles = snapshot.docs.filter( (article) =>
article.data().owner_uid !== request.auth.uid
)
return notMyArticles
})
It fetches all documents and uses Array.prototype.filter() to filter out the ones you don't want. This can be run server-side or client-side.
Updating the answer of Darren G, which caused "TypeError: Converting circular structure to JSON". When we perform the filter operation, the whole firebase object was added back to the array instead of just the data. We can solve this by chaining the filter method with the map method.
let articles = []
let articlesRefs = await db.collection('articles').get();
articles = articlesRefs.docs
.filter((article) => article.data.uid !== request.auth.uid) //Get Filtered Docs
.map((article) => article.data()); //Process Docs to Data
return articles
FYI: This is an expensive operation because you will fetching all the articles from database and then filtering them locallly.
Track all user id in a single document (or two)
filter unwanted id out
Use "where in"
var mylistofidwherenotme = // code to fetch the single document where you tracked all user id, then filter yourself out
database.collection("articles").where("blogId", "in", mylistofidwherenotme)
let query = docRef.where('role','>',user_role).where('role','<',user_role).get()
This is not functioning as the "not equal" operation in firestore with string values
You can filter the array of objects within the javascript code.
var data=[Object,Object,Object] // this is your object array
var newArray = data.filter(function(el) {
return el.gender != 'Male';
});