Loop through collection and find documents where name equals in firebase functions - node.js

I am trying to make as much logic in the firebase cloud functions, but not sure how to! Here is what I am trying to accomplish:
const myGroups = admin.firestore().collection('groups').find({"playerId":userId});
I know that above is not correct, but i cant seem to find a solution for this. I would like to get all my groups into an array or something and send them back to my app.
I am not sure if this is wise?!? But think that it would be easier to add or change to the script from one place, instead of all devices.

Hi you need to use firestore compound queries you can find the documentation here: https://firebase.google.com/docs/firestore/query-data/queries
so in your case the code will look something like:
const myGroups = await admin.firestore().collection('groups').where("playerId", "==", userId}).get();

You can use get() on the CollectionReference to get all documents.
const myGroups = await admin.firestore().collection('groups').get()
return myGroups.docs.map(group => ({id: group.id, ...group.data()}))
Additionally, if you are trying to find groups where playerId is userId. Then you can use where() clause to narrow search results:
const myGroups = await admin.firestore()
.collection('groups')
.where("playerId", "==", userId)
.get()
You can read more about queries in the documentation.

Related

How to query multiple documents from multiple nested subcollections in firebase firestore?

I have 3 subcollections nested under one another under one single main collection.
I want to get all the documents under 'colection3' for each document in 'collection2' for each document in 'collection1'
I want to query something like -
admin.firestore().collection('collection1').doc('FOR ALL DOCS IN COLLECTION 1').collection('collection2').doc('FOR ALL DOCS IN COLLECTION 2').collection('collection3').get()
My question is, can I make such query ? Will following query work ?
collection('collection1/*/collection2/*/collection3')
Is this a valid path? What does "*" indicates?
I tried something like this,
const baseRef = admin.firestore().collection(`collection1/*/collection2/*/collection3`);
const querySnap = baseRef.get()
It returned me a querySnapshot but when I tried to loop through this querySnapShot, it didn't print anything
querySnap.forEach(doc => console.log(doc.id))
output was nothing.
I was expecting that doc Ids should get printed in the console.
This can be achieved with collection group level queries.
The collectionGroup option returns all documents within the collection group you have created.
const allDocsQuery = admin.firestore()
.collectionGroup('collection1');
await allDocsQuery.get().then((snap) => {
// do operations with your data
});
Note that you will need to create a collection group in order to use this - it will throw an error with a link to the creation page, but you can also do it by following the documentation in the link above.

Get documents from firestore based on timestamp field

I am trying to get all the documents with a dateStart timestamp field that is less than 24 hours in the future using a collectionGroup query. However, it would not be ideal to get all documents and test it for each document, as the documents can accumulate over time.
I have tried to do the following, but it gives an error:
const currentDate = moment();
const snap = db.collectionGroup('appointments').where(moment.duration(currentDate.diff(moment(new Date('dateStart')))).asHours() < 25).get()
.then(function(querySnapshot){
querySnapshot.forEach(function(doc){
console.log(doc.id);
});
});
The error I am getting is the following:
Error: Value for argument "fieldPath" is not a valid field path. Paths can only be specified as strings or via a FieldPath object.
I am not sure if it is even possible to add logic like this in the where clause of the query, or if I am just doing this the wrong way. I am currently using the moment library, but this isn't mandatory for the solution. I do think it is the easiest way to get the difference between date objects.
Does anyone have an idea to get this done without looping through all the documents in the collection?
You are doing something very wired.
The where function should be:
.where('dateStart', '<', next24HoursDate)
So try something like this:
const next24HoursDate = moment(new Date()).add(24, 'hours');
const snap = db.collectionGroup('appointments')
.where('dateStart', '<', next24HoursDate)
.get()
.then( function(querySnapshot) {
querySnapshot.forEach( function(doc) {
console.log(doc.id);
});
});

How to do a query with every result of a query?

I'm trying to build an application, using MongoDB and Node.JS. I have 3 models: User, Ride, Participating.
Participating contains a userID and a rideID. It is almost as with a SQL logic: Participating links the two others models.
I'd like to, using a userID, return every Ride thanks to Participating Model
I tried to use a forEach, as the first request returns an array.
router.get('/getAllRide/:userID',function(req,res){
let userID = req.params.userID
let return = []
Participating.find({_idUser: userID })
.then(participating => {
participating.forEach(element => {
Ride.find({_id: element._id})
.exec()
.then(ride => {
retour.push(ride)})
});
res.status(200).json(return)
});
At the end of this code, the array return is empty, while it is supposed to contain every Ride whose _id is in an entity Participating.
OK, there are a couple of issues here:
return is a keyword. You probably shouldn't be using it as a variable name.
Database calls are asynchronous. forEach loops are synchronous. This means that you're immediately going to be returning retour (which looks undefined).
Mongoose has tools to populate nested relationships -- it's best not to do it in application code. Even if you are doing this in application code, it's likely best not to iterate over your results & do new finds -- instead, it's better to construct a single find query that returns all of the new documents you need.
If you did want to do this in application code, you'd want to either use async/await or Promise.all:
const toReturn = [];
const findPromises = participating.map(element => {
return Ride.find({_id: element._id})
.exec()
.then(result => toReturn.push(result)
});
return Promise.all(findPromises).then(() => res.status(200).json(toReturn));
(note: rather than using Promise.all, if you're using Bluebird you could instead use Promise.map.

Sails.js: How to find records based on values in associated collection?

I'm looking for a way to make sub-query using Sails.js Waterline. The record has an association with Charge model.
I would expect something like that to work, but that doesn't seem to be supported:
var topRecords = await Record.find({'charge.paid':true});
The closest I got was this:
var topRecords = await Record.find().populate('charge',{paid:true});
but the problem is that it still returns all the Records regardless, just doesn't populate the records that do not match populate where statement.
The reason why I can't search for charges first is that I want to sort the data based on one of the values in Record.
You can fetch the Charges then use .map to get the records from there.
const topCharges = await Charge.find({ paid: true }).populate('records');
const topRecords = topCharges.map(charge => charge.record);

In Cloud function how can i join from another collection to get data?

I am using Cloud Function to send a notification to mobile device. I have two collection in Firestore clientDetail and clientPersonalDetail. I have clientID same in both of the collection but the date is stored in clientDetail and name is stored in clientPersonal.
Take a look:
ClientDetail -- startDate
-- clientID
.......
ClientPersonalDetail -- name
-- clientID
.........
Here is My full Code:
exports.sendDailyNotifications = functions.https.onRequest( (request, response) => {
var getApplicants = getApplicantList();
console.log('getApplicants', getApplicants);
cors(request, response, () => {
admin
.firestore()
.collection("clientDetails")
//.where("clientID", "==", "wOqkjYYz3t7qQzHJ1kgu")
.get()
.then(querySnapshot => {
const promises = [];
querySnapshot.forEach(doc => {
let clientObject = {};
clientObject.clientID = doc.data().clientID;
clientObject.monthlyInstallment = doc.data().monthlyInstallment;
promises.push(clientObject);
});
return Promise.all(promises);
}) //below code for notification
.then(results => {
response.send(results);
results.forEach(user => {
//sendNotification(user);
});
return "";
})
.catch(error => {
console.log(error);
response.status(500).send(error);
});
});
}
);
Above function is showing an object like this
{clienId:xxxxxxxxx, startDate:23/1/2019}
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID.
What should do ?
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name .
Can Anybody please Help.?
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID. What should do ?
Unfortunately, there is no JOIN clause in Firestore. Queries in Firestore are shallow. This means that they only get items from the collection that the query is run against. There is no way to get documents from two top-level collection in a single query. Firestore doesn't support queries across different collections in one go. A single query may only use properties of documents in a single collection.
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name.
So the most simple solution I can think of is to first query the database to get the clientID. Once you have this id, make another database call (inside the callback), so you can get the corresponding name.
Another solution would be to add the name of the user as a new property under ClientDetail so you can query the database only once. This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoQSL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase realtime database but same rules apply to Cloud Firestore.
Also, when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
The "easier" solution would probably be the duplication of data. This is quite common in NoSQL world.
More precisely you would add in your documents in the ClientDetail collection the value of the client name.
You can use two extra functions in this occasion to have your code clear. One function that will read all the documents form the collection ClientDetail and instead of getting all the fields, will get only the ClientID. Then call the other function, that will be scanning all the documents in collection ClientPersonalDetail and retrieve only the part with the ClientID. Compare if those two match and then do any operations there if they do so.
You can refer to Get started with Cloud Firestore documentation on how to create, add and load documents from Firestore.
Your package,json should look something like this:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"firebase-admin": "^6.5.1"
}
}
I have did a little bit of coding myself and here is my example code in GitHub. By deploying this Function, will scan all the documents form one Collection and compare the ClientID from the documents in the other collection. When it will find a match it will log a message otherwise it will log a message of not matching IDs. You can use the idea of how this function operates and use it in your code.

Resources