FieldValue.increment is not a function - google-cloud-firestore

I tried for hours to update the users score stored in cloud firestore in my app using cloud functions but I am getting this error and I can't figure out how to fix this problem.
this is my code :
const auth = require('firebase/auth');
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const FieldValue = require('firebase-admin').firestore.FieldValue;
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.addPoints = functions.firestore
.document(`users/{user}`)
.onCreate(async (snap, context) => {
const invitingFriendId = snap.data().invitingFriendId;
const invitingFriendRef = db.collection('users').doc(invitingFriendId);
return invitingFriendRef.update("points", db.FieldValue.increment(50));
});

It looks like you're trying to use the functions SDK to query Cloud Firestore. That's not going to work at all. The function SDK is just used for declare function triggers. You need to use the Firebase Admin SDK to actually make the query when the function triggers.
Require firebase-admin in the most simple way:
const admin = require('firebase-admin');
Then make the query with it:
admin.firestore().documet("...").update(...);
FieldValue increment can be referenced like this:
admin.firestore.FieldValue.increment()

I am writing for CloudCode too. It is good to understand that firestore variables do not necessarily point to the same thing as they are just names.
A. The firestore here is used to access the Firestore database and set the data. However, this firestore does not contain the FieldValue:
// The Firebase Admin SDK
const admin = require('firebase-admin')
admin.initializeApp()
//const firestore db, comes after initialize app
const firestore = admin.firestore()
B. Instead, FieldValue is contained, not in admin.firestore() which is the firestore above, but by the firestore object here:
const FieldValue = require('firebase-admin').firestore.FieldValue
C. Thus, completing the above, you can now use the two different firestore variables that we have, code B for getting the FieldValue and A for updating the database
// Cloud Functions and setup triggers.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp()
//const firestore, comes after initialize app
const firestore = admin.firestore()
const FieldValue = admin.firestore.FieldValue
exports.createdLikesTrigger = functions.firestore
.document(`likes/{uid}/posts-liked/{postId}`)
.onCreate(async (snap, context) => {
const uid = context.params.uid
const postId = context.params.postId
const likeDocument = snap.data()
const date = likeDocument.when
const authorUid = likeDocument.author
try{
//increment post count
const increment = FieldValue.increment(1);
await firestore.collection('posts').doc(postId).update({likes: increment})
catch(e){
console.log("Error in incrementing likes: ", e)
}
})

I was seeing the same error message, then managed to get this working with Firebase Functions after updating Firebase Functions to the latest version.
npm install firebase-functions#latest firebase-admin#latest --save
npm install -g firebase-tools
https://firebase.google.com/docs/functions/get-started#set-up-node.js-and-the-firebase-cli

I bumped into the same problem when trying to call decrement while updating a document in a firebase function.
Then I saw these two API docs
https://firebase.google.com/docs/reference/admin/node/admin.firestore.FieldValue
https://firebase.google.com/docs/reference/js/firebase.firestore.FieldValue
Unlike firebase.firestore.FieldValue the admin version doesn't have increment/decrement methods. not sure why is that the case.
So instead I'm first reading the value using get() and then subtracting with an update().

Related

Perform simple queries in Cloud Firestore - how can i filter all through select?

I need to to filter data through queries from Firestore, but how can I also get all of them(in my case all states in firestore)? What should be useState value in this case? I'm a newbie :) Thank you for your help, I really appreciate it.
//WHAT useState VALUE SHOULD I USE TO GET ALL STATES IN DATABASE?
const (city, setCity) = useState("CA");
const citiesRef = collection(db, "cities");
const q = query(citiesRef, where("state", "==", city));
Tried to search in firestore docs and google.
you need to use getDocs() method provided by firebase as follows
here, q is your query ( const q = query(collection(db, "cities"), where(....));
add following code -
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data())
});
you can refer to this link
As you wanted to use useState, You can pass an empty string ” “ or Null value and use != instead of = operator in the where clause.
And use getDocs() to retrieve all documents as mentioned by #Prathmesh
Here is the complete code:
const (city, setCity) = useState(" ");
const citiesRef = collection(db, "cities");
const q = query(citiesRef, where("state", "!=", city));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});

(Typescript) Cleanest way to provide my own typings for firestore snapshot?

interface Doc {
foo: {
bar: number;
};
}
const docSnapshot = await docRef.get();
const bar = docSnapshot.get("foo.bar") as Doc["foo"]["bar"];
I know the type of my data, and this is how I'm casting it when retrieving from firestore snapshot. It's a little verbose. Is there a simpler way to say "I know the snapshot data is Doc"?
If you want to assume that the entire snapshot is an object structured like Doc, this is very common:
const docSnapshot = await docRef.get();
const doc = docSnapshot.data() as Doc;
const bar = doc.foo.bar;
So, instead of trying to reach into a specific field using get('a.b'), just use data() to get the entire document object object outright, and use it like you would any other JavaScript object with nested properties.

How do you get the wildcard auto id parent of a document snapshot firestore onCreate function?

I have looked at similar answers to similar questions but the answers don't specifically apply.
I am seeking the simple document parent auto-id in a firestore onCreate function in js?
The firestore function log reads function returned undefined for documentiD
How do you reference the documentID ?
Firestore Log result :sendMailtransaction Function returned undefined, expected Promise or value
//Send the transaction email
exports.sendMailtransaction = functions.firestore
.document('Companys/{companyid}/Transaction/{transactionid}')
.onCreate((snap, context) => {
const transDocument = functions.firestore.document('Companys/{companyid}/Transaction/{transactionid}');
const documentiD = transDocument.documentID;
const mailOptions = {
from: 'L App<report#sample.com>', // You can write any mail Address you want this doesn't effect anything,
to: snap.data().companyemailcf, // This mail address should be filled with any mail you want to read it,
bcc: 'admin#l.com',
subject: 'L Record, New Tranaction ',
html: `<h1>Confirmed Transaction</h1>
<p>
<b>Ref: </b>${documentiD}<br>
<b>Datetime: </b>${snap.data().datetime}<br>
<b>User: </b>${snap.data().user}<br>
<b>Vehicle: </b>${snap.data().vehicle}<br>
</p>`
};
}
You don't need to query the created document to receive it in a function as you already have the document in the onCreate parameters: snapshot is the first parameter.
Example:
export const eventCreated = functions.firestore
.document('/events/{eventId}')
.onCreate(snapshot => {
console.log("this is the new document id: ", snapshot.id)
console.log("Document content:", snapshot.data())
})
In your case, use snap.id
Firebase Functions guide is here
The snapshots reference is explained here

Getting document fields after 'onCreate' google cloud function (firestore)

I've written a google cloud function which fires every time a new user document is added to my firestore database.
The tricky (I hoped) part is done, but now i'm struggling to access the actual fields in the document that was created. It's a simple user document with an "email" field.
Here is my function. What do I need to replace email with?
exports.sendWelcomeEmail = functions.firestore
.document('users/{userId}')
.onCreate((snapshot, context) => {
sendEmail(snapshot.email)
});
you can find your user data in
snapshot.data()
exports.sendWelcomeEmail = functions.firestore
.document('users/{userId}')
.onCreate((snapshot, context) => {
let user = snapshot.data();
sendEmail(user.email)
});

Mongoose Error - Mongoose models with same model name

I am working on a NodeJs application and I am using mongoose node package.
Sample Code
I am using following method to create dynamic collections and these collections sometimes fail to persist the data in database -
const Mongoose = require("mongoose");
const Schema = new Mongoose.Schema({
// schema goes here
});
module.exports = function (suffix) {
if (!suffix || typeof suffix !== "string" || !suffix.trim()) {
throw Error("Invalid suffix provided!");
}
return Mongoose.model("Model", Schema, `collection_${suffix}`);
};
I am using this exported module to create dynamic collections based on unique ids passed as suffix parameter. Something like this (skipping unnecessary code) -
const saveData = require("./data-service");
const createModel = require("./db-schema");
// test 1
it("should save data1", function (done) {
const data1 = [];
const response1 = saveData(request1); // here response1.id is "cjmt8litu0000ktvamfipm9qn"
const dbModel1 = createModel(response1.id);
dbModel1.insertMany(data1)
.then(dbResponse1 => {
// assert for count
done();
});
});
// test 2
it("should save data2", function (done) {
const data2 = [];
const response2 = saveData(request2); // here response2.id is "cjmt8lm380006ktvafhesadmo"
const dbModel2 = createModel(response2.id);
dbModel2.insertMany(data2)
.then(dbResponse2 => {
// assert for count
done();
});
});
Problem
The issue is, test 2 fails! It the insertmany API results in 0 records failing the count assert.
If we swap the the order of the tests, test 1 will fail.
If I run the two tests separately, both will pass.
If there are n tests, only first test will pass and remaining will fail.
Findings
I suspected the mongoose model creation step to be faulty as it is using the same model name viz. Model while creating multiple model instances.
I changed it to following and the tests worked perfectly fine in all scenarios -
return Mongoose.model(`Model_${suffix}`, Schema, `collection_${suffix}`);
Questions
This leaves me with following questions -
Am I following correct coding conventions while creating dynamic collections?
Is suspected code the actual cause of this issue (should the model name also be unique)?
If yes, why is it failing? (I followed mongoose docs but it doesn't provide any information regarding uniqueness of the model name argument.
Thanks.
I you are calling insertMany method on dbModel1, where you variable is declared to dbModel2.
Change your test 2 from:
dbModel1.insertMany(data2)
.then(dbResponse1 => {
// assert for count
done()
});
To:
dbModel2.insertMany(data2)
.then(dbResponse1 => {
// assert for count
done()
});