How to query by date on cloud function? - node.js

I'm trying to query some data that is between two dates, but nothing seems to work. These are my dates, I saw on a post that the division by 1000 should work , but it's not. Ive tried firebase.firestore but says firebase it's not defined but I don't know how to simply reference to firebase.firestore.Timestamp
let now = new Date()
let yesterday = Math.round((new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1).getTime())/1000)
now = Math.round(now.getTime()/1000)
This is my query attempt. Nothing works and it just returns my empty array because the query has no data to iterate through the forEach.
let snapshot = await db.collection('appointments')
.where('status', '==', 'Pending')
.where('startDate', '<=', now)
.where('startDate', '>', yesterday)
.get().then(docs => {
docs.forEach(snapshot => {
console.log(snapshot.id, '=>', snapshot.data());
console.log(snapshot.data()['doctor']);
doctor_mail.push(snapshot.id)
doctor_mail.push(snapshot.data())
});
return doctor_mail
}).
catch(err => {
return res.send(err)
});
console.log(doctor_mail)
res.send(snapshot)

The query is not returning anything because there is nothing to return. If you are querying for a string representing a date and the data is a timestamp in the Firestore, so in order to know that, it would be need to check a sample document to compare, since in the first part of your question you mentioned that you want to get a Firestore timestamp, you can do it with this code:
const timestamp = db.FieldValue.serverTimestamp();
//if you want it as a date object
const date = timestamp.toDate();
As per what you asked in the comments, for getting the value of Today and Yesterday in Timestamp you can do the following:
var todayTimestamp = timestamp.now();
var date = new Date().setDate(date.getDate() - 1);
var yesterdayTimestamp = timestamp.fromDate(date);
And convert them back to date so you can operate them if needed, you can check more details on the Timestamp in this Documentation

Okay, the thing here was that I was wrongly calling db as const db = firebase.firestore.
Instead I just had to go for:
const db = admin.firestore()

Related

Firebase Firestore cloud functions: filtering documents by date conditions in node.js

I need to filter documents in Firestore by date within a cloud function using node.js. I'd like to return only those documents that have a timestamp (or date string) more than 30 days from the current date in a field named last_used. At present, not all documents have this field. One method I have thought about employing is writing a trigger function to give each document a last_used field and set the timestamp to 01/01/2022, run this once and then the scheduled function can update the field when required with the current date. The scheduled function currently selects a player document at random each day, moves the required fields into the current-live-player collection and updates the last_used field for the selected player document within available-players.
Context: I'm building a game in which a player document is selected at random each day from the available-players collection and placed into the current-live-player collection. current-live-player is made up of just this one document and updated each day at 00:00 London time. I only want Firestore to select a player document from available-players that meets my aforementioned condition: the date within last_used is more than 30 days prior to the current date.
This is what I have so far:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { firestore } = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
// Potential trigger function to give all player objects a last_used field
exports.addLastUsedToAll = functions.https.onRequest((request, response) => {
// Logic to add a last_used field with a date of 01/01/2022
});
// Select a player function that runs every day at 00:00 London time
exports.pickCurrentLivePlayer = functions.pubsub.schedule("0 0 * * *")
.timeZone("Europe/London")
.onRun(async () => {
// select a random player from available players (uses the auto-generated IDs)
// ifelse catches rare cases that could cause an error
const availablePlayers = db.collection("available-players");
const key = availablePlayers.doc().id;
availablePlayers.where(admin.firestore.FieldPath.documentId(), '>=', key).limit(1).get()
.then(snapshot => {
if(snapshot.size > 0) {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
console.log('snapshot.size > 0 ', doc.id, '=>', doc.data());
// replace live player
const newPlayerData = doc.data();
const app_first_name = newPlayerData.app_first_name;
const uid = newPlayerData.uid;
db.collection("live-player").doc("current-live-player").set({app_first_name: app_first_name, uid: uid});
// set selected player's last_used value to current time
db.collection("available-players").doc(uid).update({
last_used: admin.firestore.Timestamp.now()
})
});
}
else {
const player = availablePlayers.where(admin.firestore.FieldPath.documentId(), '<', key).limit(1).get()
.then(snapshot => {
snapshot.forEach(doc => {
console.log(doc.id, '=>', doc.data());
console.log('snapshot.size > 0 ', doc.id, '=>', doc.data().name);
// replace live player
const newPlayerData = doc.data();
const app_first_name = newPlayerData.app_first_name;
const uid = newPlayerData.uid;
db.collection("live-player").doc("current-live-player").set({app_first_name: app_first_name, uid: uid});
// set selected player's last_used value to current time
db.collection("available-players").doc(uid).update({
last_used: admin.firestore.Timestamp.now()
})
});
})
.catch(err => {
console.log('Error getting documents', err);
});
}
})
.catch(err => {
console.log('Error getting documents', err);
});
return null;
});
I hope that all makes sense, any help would be greatly appreciated. I know that it can be tricky to convert timestamps and then filter by date in javascript, but I'd like to use the Firestore timestamp to ensure that the functions are always running off the server time.
Edit:
In response to Frank van Puffelen's answer, the timestamp calculations are now working and each document in available players now has a last_used field populated with a timestamp. I now need to query available_players to show those documents in which the timestamp is more than 30 days ago. The issue now is that I cannot query the collection before running my random selection. The order should be: 1) calculate today's date minus 30 days; 2) query available_players for all documents with a timestamp in the last_used field more than 30 days in the past; 3) generate a key from this filtered selection; 4) query the filtered selection to give me one random document and process the rest of the update logic etc. It falls down where the filtered data is now a query snapshot rather than a collection reference and the code I have no longer runs (at least I think that's the issue!). The code worked perfectly before attempting to add this 'filter by date' functionality. I've provided a minimal repro here:
// calculate time interval
const now = admin.firestore.Timestamp.now();
const intervalInMillis = 30 * 24 * 60 * 60 * 1000;
const cutoffTime = admin.firestore.Timestamp.fromMillis(now.toMillis() - intervalInMillis);
// get collectionReference and filter to those more than 30 days ago
const allPlayers = db.collection("available-players");
const availablePlayers = await allPlayers.where('last_used', '<=', cutoffTime).get();
// generate key
const key = availablePlayers.doc().id;
// select a random player from the filtered selection
availablePlayers.where(admin.firestore.FieldPath.documentId(), '>=', key).limit(1).get()
.then(snapshot => {
..etc etc
I somehow need to be able to query the data to filter it by date and then run the random selection chunk of code.
To get the players that were last used more than 30 days ago would be something like this:
const availablePlayers = db.collection("available-players");
const now = admin.firestore.Timestamp.now();
const intervalInMillis = 30 * 24 * 60 * 60 * 1000;
const cutoffTime = admin.firestore.Timestamp.fromMillis(now.toMillis() - intervalInMillis);
const query = availablePlayers.where(last_used, "<=", cutoffTime);

Firebase query not applying "where"

Im trying to do a multiple filter system. So I need to be able to add a "where" depending the filters that I got.
When I try to add the "where" like this, the snapshot has the correct houses.
const houses = db
.collection("Houses")
.where("ventRent", "==", "Sell")
.limit(paginaSize);
const snapshot = await houses.get();
But when I try to add the "where" like this, the snapshot has every house, as if it didn't do the "where".
const houses = db
.collection("Houses")
.limit(paginaSize);
if(filters) {
if(filters.selectCR) {
if(filters.selectCR === "Selling") {
houses.where("ventRent", "==", "Sell");
} else {
houses.where('ventRent', '==', "Rent");
}
}
}
const snapshot = await houses.get();
I've tried changing to let, var and const and nothing. I've change the '' to "" or vice versa and nothing too.
I think Im doing it in the correct way, but I have no idea why is not working
Every time you call where it returns a new query. So you have to assign the result of houses.where(...) to a variable, typically back to `houses.
So for example:
houses = houses.where("ventRent", "==", "Sell");
Then at the end when you call houses.get(), you're calling it on the completed query.

Mongoose, NodeJS & Express: Sorting by column given by api call

I'm currently writing a small API for a cooking app. I have a Recipe model and would like to implement sorting by columns based on the req Parameter given.
I'd like to sort by whatever is passed in the api call. the select parameter works perfectly fine, I can select the columns to be displayed but when I try to sort anything (let's say by rating) the return does sort but I'm not sure what it does sort by.
The code i'm using:
query = Recipe.find(JSON.parse(queryStr));
if(req.query.select){
const fields = req.query.select.split(',').join(' ');
query = query.select(fields);
}
if(req.query.sort){
const sortBy = req.query.sort.split(',').join(' ');
query = query.sort({ sortBy: 1 });
} else {
query = query.sort({ _id: -1 });
}
The result, when no sorting is set: https://pastebin.com/rPLv8n5s
vs. the result when I pass &sort=rating: https://pastebin.com/7eYwAvQf
also, when sorting my name the result is also mixed up.
You are not using the value of sortBy but the string "sortBy". You will need to create an object that has the rating as an object key.
You need the sorting object to look like this.
{
rating: 1
}
You can use something like this so it will be dynamic.
if(req.query.sort){
const sortByKey = req.query.sort.split(',').join(' ');
const sortByObj = {};
sortByObj[sortByKey] = 1; // <-- using sortBy as the key
query = query.sort(sortByObj);
} else {
query = query.sort({ _id: -1 });
}

Firestore - How to find the count of documents that can be fetched using a query without really fetching all documents? [duplicate]

This question already has answers here:
Cloud Firestore collection count
(29 answers)
Closed 2 years ago.
Actually I want to create a Admin Panel for my website and I need to show the total number of users that signed up today(or in the last week). I want to fetch the number of user documents that were created in the users collection today. I have a createdOn field in every user document.
Can I do something like this:
admin.firestore()
.collection('user')
.where('createdOn', '>', <today's midnight timestamp here>)
.count()
Like is there any hack/solution for fetching count of documents without actually fetching them.
Since there is no COUNT()in Firestore (as yet) you are forced to query your documents to calculate or maintain the total number of records globally or based on arguments.
There is an extension to do just that but I had trouble configuring it.
I personaly use a simple Cloud functions that I know is not completely bulletproof but good enough to provide me with a dbutils collection where I store the latest count of any collection. I also maintain count subcollections to store the number of records for the same collection but with query parameters. Quite necessary to propose a REST API...
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
export const dbutils = functions
.firestore.document('/{collection}/{id}')
.onWrite((change, context) => {
const db = admin.firestore()
const docRef = `dbutils/${context.params.collection}`
let value = 0
if (!change.before.exists) {
value += 1 // new record
} else if (!change.after.exists) {
value -= 1 // deleted record
}
// ignore last case which is update
if (value !== 0) {
db.doc(docRef)
.set(
{
count: admin.firestore.FieldValue.increment(value),
updatedAt: Date.now(),
},
{ merge: true },
)
.then(res => deleteCollection(db, `${docRef}/counts`, 500))
.catch(err => console.log(err))
}
return null
})
const deleteCollection = (
db: admin.firestore.Firestore,
collectionPath: string,
batchSize: number,
) => {
const collectionRef = db.collection(collectionPath)
const query = collectionRef.orderBy('__name__').limit(batchSize)
return new Promise((resolve, reject) => {
deleteQueryBatch(db, query, batchSize, resolve, reject)
})
}
I'm confident we'll get a COUNT() sooner or later!

MongoDB: How to get timestamp and ObjectID from newest document in collection?

I would like to get last created documents in collection and return their objectID and timestamps. For example if yesterday I created 10 documents I would like to return them with db.collection and then
const lastTimeStamp = will be the timestamp from the last created element
const lastTimeStampArray = will be array of timestamps from yesterdays records
const lastObjectId = will be ObjectID of last created document
const lastObjectIdsArray = array of last objectIds
I am using:
MongoDB's _id field has info about date stored in itself. The timestamp is contained in the first 4 bytes of a mongoDB id.
You can use ObjectId.getTimestamp() function to get time from _id of a document.
Sorting on an _id field that stores ObjectId values is roughly equivalent to sorting by creation time.
For you question:
// To get lastTimeStamp
db.collection.find().sort({ '_id': -1}).limit(1).forEach(
function(doc){
lastTimeStamp = doc._id.getTimestamp();
}
)
// to get lastObjectId
db.collection.find().sort({ '_id': -1}).limit(1).forEach(
function(doc){
lastObjectId = doc._id;
}
)
Now, to get all records inserted yesterday might be a bit of hard work. You need to extract all records inserted yesterday and from that you need to extract information you need.
// to get lastTimeStampArray and lastObjectIdsArray
var yesterdayStart = new Date();
yesterdayStart.setDate(yesterdayStart.getDate() - 1);
yesterdayStart.setHours(0,0,0,0);
var startId = Math.floor(yesterdayStart.getTime() / 1000).toString(16) + "0000000000000000";
var yesterdayEnd = new Date();
yesterdayEnd.setDate(yesterdayEnd.getDate() - 1);
yesterdayEnd.setHours(23,59,59,999);
var endId = Math.floor(yesterdayEnd.getTime() / 1000).toString(16) + "0000000000000000";
var lastTimeStampArray = [];
var lastObjectIdsArray = [];
db.collection("records")
.find( { _id: {
$gte: ObjectId(startId),
$lte: ObjectId(endId)
}
}
).forEach(
function(doc){
lastObjectIdsArray.push(doc._id);
lastTimeStampArray.push(doc._id.getTimestamp());
});
These are mongo shell commands you can write your node.js accordingly.
You can get last inserted record with timestamp using the following:
db.collection.find().sort({ '_id': -1 }).limit(1).forEach(
function(doc){
print("record:"+doc._id.getTimestamp());
})
_id is the Mongodb objectID

Resources