Firestore Conundrum! Using .where() For Arrays And Strings
I am attempting to use the .where() functionality of firestore to detect if a certain string is in an array in the database. I have tried manipulating the first parameter of the function by adding brackets and other things to signify parts of an array to no avail.
//in this section I am getting the "yourLikes" integer (unrelated to the problem)
var liks = [];
var tempLiks = []
db.collection("users").where("uid", "==", uid)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
tempLiks = doc.data().yourLikes;
// I am using a setTimeout() function to allow firebase to
// process the previous query and give me the doc.data().yourLikes for tempLiks
setTimeout(function(){
//Right here, the "usersLiked" data is an array, with a bunch of different values.
// I am trying to see if one of those values is doc.data().uid from my previous query. (I think) this is the problem.
db.collection("memeInfo").where("usersLiked", "==", doc.data().uid)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
for (var i = 0; i < tempLiks.length; i++) {
liks.push([tempLiks[i],doc.data().likes])
console.log(liks)
}
})
})
},500)
If anyone has a solution to querying to see if individual values are in arrays without using a for loop forever (I will have possibly thousands of values in the usersLiked array), that would be much appreciated.
Answer
You should use the array_contains
operator to filter based on array values, see https://firebase.google.com/docs/firestore/query-data/queries#array_membership.
Therefore you have to adapt your code as follows:
//....
db.collection("memeInfo").where("usersLiked", "array-contains", doc.data().uid)
.get()
.then(function(querySnapshot) {...})
Extra remark: using setTimeout()
for managing the asynchronous aspect of the queries to Firestore is not a correct approach (there is no assurance that the query will be done in less than 500 ms). You should manage the asynchronicity through the promises returned by the get()
method. In particular, since you are firing several queries in parallel (through your loops), you need to use Promise.all()
.
UPDATE following your comment. You could use Promise.all()
as follows:
var liks = [];
var tempLiks = [];
db.collection('users')
.where('uid', '==', uid)
.get()
.then(function(querySnapshot) {
// Here the Promise returned by the get() method is fulfilled, so you do have the results of the query and you do'nt need to use setTimeout
var queries = [];
querySnapshot.forEach(function(doc) {
tempLiks = doc.data().yourLikes;
//We push each query to the queries array
queries.push(
db
.collection('memeInfo')
.where('usersLiked', '==', doc.data().uid)
.get()
);
});
//Then we call Promise.all()
return Promise.all(queries);
})
.then(function(results) {
//As explained in the doc "The returned promise is fulfilled with an array containing all the values of the iterable passed as argument (the queries array)."
//So result is an array of QuerySnapshots, since the get() method returns a QuerySnapshot
//Do whatever you want with the array
console.log(results);
results.forEach(function (querySnapshot) {
querySnapshot.forEach(function (doc) {
//.....
});
});
});
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM