How to update DocumentReference? - node.js

I tried to update DocumentReference but could not do it.
update() method fails. How to use it? (How to pass argument?)
firebase-admin version is 6.3.0.
#google-cloud/firestore version is 0.19.0.
❯ firebase functions:shell
i functions: Preparing to emulate functions.
Warning: You're using Node.js v8.14.0 but Google Cloud Functions only supports v6.11.5.
✔ functions: sampleFunc
firebase > const admin = require('firebase-admin');
firebase > admin.initializeApp();
firebase > let ref = admin.firestore().collection("users").doc('edqupYQhzqV1ODjEpoJn');
firebase > let updates = { email: 'xxx#yyy.zzz' };
firebase > ref.update(updates).then(value => console.log(value) );
Error: Update() requires either a single JavaScript object or an alternating list of field/value pairs that can be followed by an optional precondition. Argument "dataOrField" is not a valid Document. Input is not a plain JavaScript object.
at WriteBatch.update (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:359:23)
at DocumentReference.update (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/reference.js:387:14)
Update
Document has already been created, so get() works.
firebase > ref.get().then(snapshot => console.log(snapshot.data()));
Errors also occur in set() as well.
firebase > ref.set({email: 'aaa#bbb.ccc'}, {merge: true}).then(value => console.log(value));
Error: Argument "data" is not a valid Document. Input is not a plain JavaScript object.
at Validator.(anonymous function).values [as isDocument] (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/validate.js:99:27)
at WriteBatch.set (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:232:25)
at DocumentReference.set (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/reference.js:349:27)

Following the example you posted, you are trying to update a document that does not exist. In this case you should create it first.
If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.
From Google's Documentation:
var cityRef = db.collection('cities').doc('BJ');
var setWithOptions = cityRef.set({
capital: true
}, {merge: true});
Reference: Firestore Add Data

I've never tried using the Admin SDK in the functions:shell like you do in your example. However I can easily reproduce your error. I guess that the functions:shell somehow tampers with your variable updates. When i log updates undefined is concatenated in output
firebase > console.log(updates)
{ email2: 'xxx#yyy.zzz' }
undefined
If you put your code in a JS file, initialize your app with a service account and run in with node it will most likely work just fine!
Read more about how to use Service Account

create an object first then assign your values to a field/property on that object like:
var myObj = {};
myObj["myfield"] = myvalue;
now pass as the 2nd argument of .set or .update.

Related

Firebase Admin SDK with Cloude Functions Confusion

So after I have written a good part of my app, I am now becoming maximally confused regarding the use of the admin sdk in cloud functions regarding firestore.
I only want to query, read and write data from the cloud function environment correctly. Which documentation do I have to use, how do I correctly initialize the "Admin SDK" and implement the corresponding methods and functions?
It seems like I have mixed up v9 and v10 and even by reading the docs I still can't find a red thread, on how to use it correctly.
I am currently importing and initializing like that.
const functions = require("firebase-functions");
const { initializeApp } = require("firebase-admin/app");
const admin = require("firebase-admin");
const app = initializeApp();
when I initialize like that, and would like to work with firestore. There are different options which I do not understand - for example.
const userRef = admin.firestore().collection("users").doc(data.userID);
enables me to access "collection"
However when using
const userRef = admin.firestore
I am only able to choose admin.firestore.CollectionGroup and admin.firestore.CollectionReference, which are classes? What is up with that?
Furthermore this approach seems to be outdated as this site of the docs (which I only recently came to know), says that I should use a modular approach, as I would on the client side, with.
import { getFirestore } from 'firebase-admin/firestore'
getFirestore();
So far so good. Now when I take a look at the docs, I am led to this page. The question I ask myself there are, what are External Api Re-Exports? By clicking on any of the referenced functions I get redirected to this, which contains the reference for the nodejs client and also subgroups called firestore admin client, as well as FirestoreAdmin. Neither of those subgroups contain anything which has something to do with querying a collection. There is a section about collections and querying which displays examples of asynchronous programming with .then, but from what I heard it is generally more beneficial to use async/await?
In addition to that the quickstart guide, recommends an initialization like this.
const {Firestore} = require('#google-cloud/firestore');
// Create a new client
const firestore = new Firestore();
Furthermore the firebase docs, seem to have a dedicated documentation on how to use the Admin SDK, with the realtime database here but not how to use it with firestore?
I am just so confused, as to which documentation to use, and I couldn't find any examples how to do standard operations. I guess I also lack fundamental understanding about the Admin SDK itself and its integration in firebase.
The code I have written now is working, however I think it is not "right" from a documentation point of view.
exports.createUserDoc = functions.auth.user().onCreate((user) => {
const userRef = admin.firestore().collection("users").doc(user.uid);
const userData = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
tokens: 0,
};
return userRef.set(userData);
});
exports.setWriteTimestamp = functions.https.onCall((data, context) => {
//in the if check below, retrieve the corresponding pool document and check whether the "open" field is true
const poolRef = admin.firestore().collection("pools").doc(data.slug);
const poolDoc = poolRef.get().then((doc) => {
if (doc.exists && doc.data().open) {
return poolRef.update({
writeTimestamp: admin.firestore.FieldValue.serverTimestamp(),
});
} else {
return null;
}
});
});
Thank you for your help.

How to access a Google Firestore collection from within a Cloud Function referencing a different Firestore collection?

The following (from a React site) is not working . I've been in the docs for hours without success. Any ideas?
import firebase = require("../../node_modules/firebase");
import * as functions from "firebase-functions";
exports.onSomeCollectionCreate = functions
.firestore
.document("some-collection/{someCollectionId}")
.onCreate(async(snap, context) => {
firebase
.firestore()
.collection("another-collection/{anotherCollectionId}")
.add({ some: "data" });
}
);
Some terminal feedback:
⚠ functions[onSomeCollectionCreate(region)]: Deployment error.
Thank you for reading.
In a Cloud Function, you should use the Admin SDK in order to interact with the Firebase services, see the doc for more details.
The following should therefore work:
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Cloud Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.onSomeCollectionCreate = functions
.firestore
.document("some-collection/{someCollectionId}")
.onCreate(async(snap, context) => {
return admin. // note the return
.firestore()
.collection("another-collection")
.add({ some: "data" });
}
);
Note two additional points:
You should not pass to the collection() method a string with a slash (/), since Collection references must have an odd number of segments.
Note that we return the Promise returned by the add() method. See the doc here for more details on this key point.

Firebase function, increment a value

I have a firebase cloud function that should increment the value of a field when a new document is created. The function executes successfully as I can see this within the firebase logs, but it doesn't increment the value of the field.
exports.onFileAdded = functions.firestore.document("files/{id}").onCreate(async (change, context) => {
const file = change.data();
const hub = await getByCollectionAndId('hubs', file.hubId);
Firebase.firestore().collection('teams').doc(hub.teamId).set({tileCount: Firebase.database.ServerValue.increment(1)}, {merge: true});
});
As there are no errors, and the function executes successfully, what am I doing wrong?
The problem is:
Firebase.database.ServerValue.increment(1)
You're using the operator to increment a value on the Realtime Database, but you are using it on Cloud Firestore. While both databases are part of Firebase, they're completely separate, and the API for one doesn't apply to the other.
To fix the problem, use the increment operator for Firestore:
firebase.firestore.FieldValue.increment(1)
Also see my answer here: How to increment existing number field in Cloud Firestore

Accessing Value from Key via Change (onWrite)

I am trying to access the value of a key(node) from the firebase realtime database. I am using the onWrite trigger which passes the change paramters, which holds snapshot of the database before the trigger and after the trigger. Therefore with the after trigger's snapshot I wish to place the key of a node into a constant variable although when I try I recieve a TypeError.
Type Error Bug:
TypeError: change.after.ref.child(...).val is not a function
at exports.sendNotification.functions.database.ref.onWrite (/user_code/index.js:13:64)`
Thus meaning that .val() cannot be used in the way I have tried to use it, although I cannot see any other information to see how to handle this.
Relevant Segment of Code:
'user-strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase); // load the firebase-functions and firebase-admin modules, and initialize an admin app instance from which Realtime Database changes can be made.
exports.sendNotification = functions.database.ref("/Notifications/{user_id}/{notification_type}/{sender_id}").onWrite((change, context) => {
const user_id = context.params.user_id;
const notification_type = context.params.notification_type;
const sender_id = context.params.sender_id;
//Check if the notification is still to be sent (true not false)
const notficationValid = change.after.ref.child(sender_id).val();
console.log(`Notification Valid: `, notficationValid);
I expected the notificationValid variable to hold the value of 'true'.
Firebase Database
The .ref in your code is not needed:
change.after.child(sender_id).val()
Calling .ref on a DataSnapshot gives you back a DatabaseReference, which doesn't have a val() method.

Do wildcards exist for Firebase Fuctions on Storage?

I've been using successfully Firebase Functions to perform operations on files I've been uploading to Firebase Storage.
What I currently want to do is access the folder into which I'm shoving data by using wildcards, with the same method of implementation as how it works for the Realtime Database + Firestore. However, whenever I try to access these parameters, they exclusively return null.
Is this not possible?
Here's an example of my code:
exports.generateThumbnail = functions.storage.object('user-photos/{userId}/{publicOrPrivate}').onChange(event => {
const privateOrPublic = event.params.publicOrPrivate;
const isPrivate = (event.params.publicOrPrivate == "private");
const userId = event.params.userId;
console.log("user id and publicOrPrivate are", userId, privateOrPublic);
//"user id and publicOrPrivate are undefined undefined"
}
Thanks!
There is currently no wildcard support for Cloud Storage triggers. You have to check the path of the file that changed to figure out if you want to do anything with it.

Resources