Access Firebase Child Node in Node.js - Firebase Cloud Functions - javascript

Here is how my Firebase Schema is laid out:
I am able to index everything except _geoloc: into my Algolia Index with this code:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Authenticate to Algolia Database.
// TODO: Make sure you configure the `algolia.app_id` and `algolia.api_key` Google Cloud environment variables.
const algoliasearch = require('algoliasearch');
const client = algoliasearch(functions.config().algolia.app_id, functions.config().algolia.api_key);
// Name fo the algolia index for content.
const ALGOLIA_POSTS_INDEX_NAME = 'businessSearch';
exports.indexentry = functions.database.ref('/businessSearch/{uid}/').onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
const firebaseObject = Object.assign({}, event.data.val(), {
functions.database.ref('/businessSearch/{uid}/_geoloc').onWrite(event =>{
})
objectID: event.params.uid,
});
index.saveObject(firebaseObject); // .then or .catch as well
index.addObject
});
How can I index the _geoloc: child node into my Algolia Index with Node.js?
I heard somewhere that this is not possible to index a nested object with Object.assign, but I just wanted to see if it was.

There is a timing issue when the database is written and what gets indexed by Algolia. The code included in my question does work with nested objects.

Related

How to create a document with a subcollection within Firestore cloud function?

Hi I'd like to create the following document with the private subcollection, via a cloud function trigger:
This is what I have so far, but have no idea how to actually set a subcollection as well.
"use strict";
Object.defineProperty(exports, "__esModule", {value: true});
// Cloud Functions for Firebase SDK to create Cloud Functions + set up triggers.
const functions = require("firebase-functions");
// The Firebase Admin SDK to access Firestore.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
// Listen to .onCreate trigger
exports.createProfile = functions.auth.user().onCreate((user) => {
return admin.firestore().doc(`/users/${user.uid}`).set({
firstName: null,
lastName: null,
preferredName: null,
});
});
A reference to a doc can be formed by alternating collection and doc references. Going a little overboard to spell it out...
const usersCollectionRef = firestore().collection('users')
const userRef = usersCollectionRef.doc(user.uid);
const privatesCollectionRef = userRef.collection('private');
const accountRef = privatesCollectionRef.doc('account');
return accountRef.set(...)
// or, the other extreme lines-of-code-wise
const accountRef = firestore().doc(`/users/${user.uid}/private/account`)
return accountRef.set(...)
Note that you'll only be able to have one doc at a time named "account" in the "private" collection. Not sure about the semantics of your app, but the user's email inside that doc might be a better doc name.

Cloud functions cron job: How to manage the nested collection's data

I want to trigger the scheduled task to the nested collection data using cloud functions cron job.
What I want to achieve is, if the timestamp value in "limit" field is later than the current time, "status" field will be added with the value "expired" in the nested collection.
The parent collection name is "OrdersUser" and the child collection name is "Orders".
And I want to manage the documents in "Orders" collection.
The scheduled task runs every one minute.
After I deployed, I got an error in the firebase console.
id is not defined.
I thought I could use wild card in the cloud functions, so I used {id} to refer the nested documents. But I couldn't.
I want to query all the data in ”Orders” collection.
How can I fix this issue?
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
const db = admin.firestore()
const ref = functions.firestore
exports.statusOrdersUser = functions.runWith( { memory: '2GB' }).pubsub.schedule('* * * * *').onRun((context) => {
// Consistent timestamp
const now = admin.firestore.Timestamp.now();
// Query all documents ready to perform
//id is not defined.
const queryOrdersUser = db.collection('OrdersUser').doc({id}).collection('Orders').where('limit', '<=', now)
return queryOrdersUser.get()
.then(querySnapshot => {
if (querySnapshot.empty) {
return null;
} else {
const promises = []
querySnapshot.forEach(doc => {
promises.push(doc.ref.update({ status: 'expired' }));
});
return Promise.all(promises);
}
});
})
Firestore does not support wildcard operations for document IDs. If you want to query a subcollection, you need to provide the specific ID of the document where it is nested. It's not clear how your function is supposed to know exactly which subcollection to query, since it doesn't receive any arguments or have any context.
If you want to query all of the subcollections named "Order", no matter where they are nested anywhere in your database, you can use a collection group query for that.

How to update a document when a document is added to a specified collection in cloud firestore?

I have 2 collections in my firestore (global and local) and when I add a doc to local I need to update a field in the global doc by 1
Below is the code I have for the same. I am very new to this so I might have some syntactical error as well, please do highlight if you find any.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello world");
}); // For testing, even this is not being deployed
exports.updateGlobal = functions.firestore
.document("/local/{id}")
.onCreate((snapshot, context) => {
console.log(snapshot.data());
return admin
.firebase()
.doc("global/{id}")
.update({
total: admin.firestore.FieldValue.increment(1),
});
});
The Terminal says "function failed on loading user code"
Before this, it showed something along the lines of "admin is undefined" or "cannot access firestore of undefined" which I'm unable to replicate now.
This is a part of a react app which has normal firestore working through firebase npm module
Any other info needed regarding the issue I'll edit the question accordingly, thank you so much for the help.
In addition to loading the firebase-functions and firebase-admin modules, you need to initialize an admin app instance from which Cloud Firestore changes can be made, as follows:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//...
I see another problem in your CF. You need to use the context object to get the value of id.
exports.updateGlobal = functions.firestore
.document("/local/{id}")
.onCreate((snapshot, context) => {
const docId = context.params.id;
return admin
.firebase()
.doc("global/" + docId)
.update({
total: admin.firestore.FieldValue.increment(1),
});
});
You can also use template literals as follows:
return admin
.firebase()
.doc(`global/${docId}`)
//...

Where should i declare a new firebase database instance?

When using const db = firebase.database(), does it matter where I declare this in a cloud function script?
For example, index.ts which contains all of my cloud functions, should I declare it at the top, or in each individual function?
const db = firebase.database()
export const functionA = functions.https.onCall(async (data, context) => {
// use db here
});
export const functionB = functions.https.onCall(async (data, context) => {
// use db here
});
OR
export const functionA = functions.https.onCall(async (data, context) => {
const db = firebase.database()
});
export const functionB = functions.https.onCall(async (data, context) => {
const db = firebase.database()
});
Or does this not matter?
The first approach creates the db instance when the code loads.
The second approach creates the db instance when the code runs.
Neither is pertinently better than the other, and the firebase.database() operation is very lightweight so it's likely to make little difference in practice.
What does make a difference is whether you load the database SDK to begin with. Some of the SDKs in Firebase are quite big, and not each Cloud Function needs all SDKs. So a common trick to speed up load/cold-start times is to move the require('firebase-...') statements into the body of the Cloud Function(s) that require them.
Normally, if you want to interact, from a Cloud Function, with the Realtime Database you just need to initialize the Admin SDK and get the Database service for the default app (or a given app), as explained here in the doc.
So you would do something like:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Get the Database service for the default app
const db = admin.database();
To get more details on what Frank explains about Cold Start in his answer, you should read the following article: https://medium.com/firebase-developers/organize-cloud-functions-for-max-cold-start-performance-and-readability-with-typescript-and-9261ee8450f0
However in your case, since your two Cloud Functions use the Admin SDK, there shouldn't be any difference between the two approaches presented in your question, as Frank mentioned.

How to read Firebase Database entry in Firebase Cloud Functions' index.js?

I am trying to create device to device push notifications for an iOS app using Firebase Cloud functions. I want to trigger an event whenever a new child is created in database at reference '/user-notifications/{notificationRecipientUid}/{challengeId}'. Here is my index.js code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendWinLoseOrTieNotification = functions.database.ref('/user-notifications/{notificationRecipientUid}/{challengeId}').onWrite(event => {
...
const challengeId = event.params.challengeId;
functions.database.ref('/challenges/' + challengeId).once('value').then(function(snapshot) {
const challengerUid = snapshot.val().challengerUid;
...
});
});
When a new child is added in the database at that location, I get this error, "TypeError: functions.database.ref(...).once is not a function", in Firebase Console's Functions Logs. So there is no 'once' method available
on ref like in web api:
firebase.database().ref('...').once('value').then(function(snapshot) {
...
});
My question is: How to read an existing database value inside index.js?
Well, the solution is to use admin instead of firebase like so:
admin.database().ref('...').once('value').then(function(snapshot) {
...
});

Categories

Resources