According to the documentation here, it should be possible get an id for a not-yet-created firestore document, add it the object to be saved, and then persist it like this:
// Add a new document with a generated id.
var newCityRef = db.collection("cities").doc();
// later...
newCityRef.set(data);
In my application, I follow this pattern with the following code:
async createNewProject(projectObject : Project) : Promise<boolean> {
let doc = this.firestore.collection("projects").doc();
projectObject.id = doc.ref.id;
try {
await doc.set(projectObject);
} catch(err) {
console.error(err);
return false;
}
return true;
}
When it runs though, i get an error in the console:
FirebaseError: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: undefined
Can anybody shed any light? I've seen other folks on her referencing this method (using the doc method with no parameters as the solution to the problem, yet others are seeing this error. Is this some kind of API on the Angular implementation of the API, or a problem at the firebase side of things (or did I do something wrong?)
The doc() method returns a DocumentReference, which does not have a ref property but has an id one. Therefore you need to do:
projectObject.id = doc.id;
instead of
projectObject.id = doc.ref.id;
I'm probably misunderstanding exactly what's being returned by the members object here, but I'm attempting to test a deafen command within my bot - the documentation for discord.js states that the Message object contains a Mentions property that holds all the valid mentions within a sent message, from there, I can drill down a bit further and get to the Members property from within Mentions - which returns a collection of GuildMembers that have been mentioned in the message - which is what I want. The problems arises when I attempt to grab those GuildMember objects from within the collection that I get back - I'm not quite sure what I'm doing wrong, but I get back errors. The code I have is as follows:
module.exports = {
name: 'deafen',
cooldown: 5,
description: 'Deafens mentioned user or users.',
args: true,
execute(message) {
const taggedMembers = message.mentions.members;
for(const member of taggedMembers) {
member.setDeaf(true)
.then(() => console.log(`Deafened ${member.displayName}`))
.catch(console.error);
}
},
};
and I get back this error in my terminal window:
TypeError: member.setDeaf is not a function
at Object.execute (/home/tai/dev/FutabaBot/commands/admin/deafen.js:10:20)
at Client.<anonymous> (/home/tai/dev/FutabaBot/FutabaBot.js:80:17)
at Client.emit (events.js:210:5)
at MessageCreateHandler.handle (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/packets/handlers/MessageCreate.js:9:34)
at WebSocketPacketManager.handle (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/packets/WebSocketPacketManager.js:105:65)
at WebSocketConnection.onPacket (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/WebSocketConnection.js:333:35)
at WebSocketConnection.onMessage (/home/tai/dev/FutabaBot/node_modules/discord.js/src/client/websocket/WebSocketConnection.js:296:17)
at WebSocket.onMessage (/home/tai/dev/FutabaBot/node_modules/ws/lib/event-target.js:120:16)
at WebSocket.emit (events.js:210:5)
at Receiver.receiverOnMessage (/home/tai/dev/FutabaBot/node_modules/ws/lib/websocket.js:789:20)
I'm not sure if it's necessary, but I can post the other pieces of code that relate to this - and the documentation for discord.js can be found here
The message.mentions.members returns a Collection of GuildMembers and a Snowflake (some unique identifier. A Collection is an extension of the Map class, where a map is an array with each item having a key and a value attribute.
When looping over a map, like you're doing with for(const member of taggedMembers), each item (or member in your case) is a key and a value pair. Then you're trying to call the setDeaf method on that pair, which as you've seen doesn't work.
What you want is to only loop over the values in the collection. This can easily be done with a .forEach. I'll add some example code down below:
taggedMembers.forEach((member) => {
member.setDeaf(true)
.then(() => console.log(`Deafened ${member.displayName}`))
.catch(console.error);
});
Give it a go and let me know if you run into any more issues.
EDIT: As slothiful pointed out in the comments, using a .forEach won't catch any Promise rejections even after attaching a .catch(). To fix this you can use the for ... of loop but destructuring the pair. This can be seen in the example below:
for (let [, member] of taggedMembers) {
member.setDeaf(true)
.then(() => console.log(`Deafened ${member.displayName}`))
.catch(console.error);
}
I have a project to add currency details into the firestore database and my project is doing with ionic 3
Whenever I add a new document to the collection a trigger function onCreate() will execute and update the document named 'updated'.
But the trigger function always showing an error.
Error: Invalid use of type "undefined" as a Firestore argument.
at Object.exports.customObjectError.val [as customObjectError] (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/validate.js:164:14)
at Function.encodeValue (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/document.js:808:20)
at Function.encodeFields (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/document.js:678:36)
at Function.fromObject (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/document.js:218:55)
at WriteBatch.set (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/write-batch.js:291:39)
at DocumentReference.set (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/reference.js:419:8)
at Object.<anonymous> (/user_code/lib/index.js:28:10)
at next (native)
at /user_code/lib/index.js:7:71
at __awaiter (/user_code/lib/index.js:3:12)
sombody please help..
i have spent lot of time on it.
Here is the code :
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
admin.initializeApp();
exports.createCurrency = functions.firestore
.document('Exchange/{ExchangeId}')
.onCreate( async (snap, context) => {
const id: string = snap.data().id;
const branchName: string = snap.data().branchName;
const currencyName: string = snap.data().currencyName;
const buyingRate : string = snap.data().buyingRate;
const sellingRate : string = snap.data().sellingRate;
const newUser= admin.
firestore()
.doc(`Exchange/updated`)
.set({
id : id,
branchName : branchName,
currencyName : currencyName,
sellingRate : sellingRate,
buyingRate :buyingRate
});
return newUser;
});
The error message is this:
Invalid use of type "undefined" as a Firestore argument.
You can see in your stack trace that this happens when you call set() with an object on a DocumentReference. It turns out that one of the values you're passing in the object is undefined. Check each of the values that you're passing and make sure all of them have an actual value:
.set({
id : id,
branchName : branchName,
currencyName : currencyName,
sellingRate : sellingRate,
buyingRate :buyingRate
});
It's impossible to tell which one it is from the error message, so you'll have to print them all out do something to check each and every one of them.
When you .set an object but one of the fields in the object is undefined you will get this error.
The problem is that when you use console.log to display the object it does not show the undefined variables so it is difficult to trace.
Use the following instead of console.log to find the element that is causing the problem.
const util = require('util');
console.log(util.inspect(myObject, {showHidden: false, depth: null}));
This will give you an output as follows:
{ origin: 'AMS',
destination: undefined,
duration: 94,
carrier: 'KL',
flight_number: '2977',
departure: '2019-06-11T15:34:00',
arrival: '2019-06-11T17:08:00',
type: 'flight' }
Understanding the issue:
The following error message is relatively clear
Invalid use of type "undefined" as a Firestore argument.
This means, that in your set or update reference method, you've passed an argument, which had value of undefined, which is by definition not a valid Firestore argument.
Fixing the issue:
Personally I find everything much easier to understand with an example.
Let's say my function editStafferPoolPermissions errors with the "Invalid use of type "undefined" as a Firestore argument" error message. Which is defined the following way:
export const editStafferPoolPermissions(data: {
businessId: string,
stafferId: string,
poolPermissions: string[],
areCustomPermissions: boolean,
wage: number,
}) => //...
To find out which argument (or even arguments) it is exactly
Open your Firebase developer console and open the "Functions" tab.
Select the logs tab
Filter out the exact name of the function and check the arguments passed
This allow us to see, which arguments were passed and which weren't.
As you can see, the wage parameter is missing in the oncall invocation of my https cloud function, causing the error to crash. This means I either forgot to pass or am passing the wage parameter incorrectly.
Obviously the undefined argument will wary depending on how your function is defined but hopefully this should be enough for you to get the gist of how to trace and fix the issue. It usually boils down to two options, you either forgot to pass it altogether or are passing it incorrectly from the front-end (or the data is incorrectly structured)
What if I want to allow undefined (optional) arguments?
What most of the answers on internet don't tackle, is the scenario where we might actually leave an argument undefined on purpose.
I actually had a lot of trouble finding this for the longest time, where I had to resort to writing a very shoddy looking cloud funciton full of nested ifs, when I wanted to create one, that would also allow optional parameters as undefined and simply ignore them if they aren't passed.
To continue from our previous example, let's say we changed the wage argument to optional, i.e.
wage?: number
// ... other params
So now, if we call the editStafferPoolPermissions cloud functions, it shouldn't matter whether the wage is passed or not.
Luckily, as of May 29 2020, there has been added a new argument to the SetOptions called ignoreUndefinedProperties, which allows you to simply ignore undefined parameters.
For example, the inside of my editStafferPoolPermissions could look something like this.
await firestore.collection('staffers').doc(stafferId).set({
poolPermissions,
areCustomPositions,
wage,
}, { ignoreUndefinedProperties: true })
Troubleshooting legacy firebase versions
Given this newly added argument is relatively recent and even in my work I was on relatively older codebase which for legacy reasons could not have the most up-to-date firebase version, so for this reason, I needed to create a polyfill, which would mimic the ignoreUndefinedProperties argument.
I've created the following function:
export const ignoreUndefinedSet = async (
// depending on your namespace
// & /types version, you can use firebase.firestore.<name> instead
reference: FirebaseFirestore.DocumentReference,
data: FirebaseFirestore.DocumentData,
options?: FirebaseFirestore.SetOptions,
checkNestedObjects = true,
) => {
const isPlainObject = (val: unknown) =>
typeof val === 'object' && val !== null &&
!(val instanceof Date) && !Array.isArray(val)
const keepDefinedProperties = (
obj: FirebaseFirestore.DocumentData,
nestedCheck = true,
) =>
Object.entries(data).reduce(
(result, [key, value]) => (
value === undefined
? result
: (nestedCheck && isPlainObject(value))
? Object.assign(result, { [key]: keepDefinedProperties(value) })
: Object.assign(result, { [key]: value })
),
{}
)
const onlyDefinedProperties = keepDefinedProperties(data, checkNestedObjects)
await reference.set(onlyDefinedProperties, { ...options })
}
So with my polyfill, you can use ignore the undefined properties even in older firebase versions. In fact it actually might be useful even on newer ones, because it allows you to decide if you want to ignore the undefined properties only at the object root level, or also at potentially nested object properties.
So essentially these two statements are equivalent
await reference.set(data, { ignoreUndefinedProperties: true })
// newer firebase version
await ignoreUndefinedSet(reference, data) // my polyfill
Note, you can also pass other SetOptions or disable the nested objects check
await ignoreUndefinedSet(reference, data, { merge: true }, false)
// does ignoreUndefinedProperties only at root level and uses the merge method
I am trying to access the properties of a returned MongoDB (mongoose) find.
If I try to console log the whole object, I can see it all. But if I try to log a property, I get undefined. The object is there!
function getAll () {
let d = q.defer();
User.find({}, function (err, docs) {
if (err) {
d.reject(err);
}
for(let user of docs) {
console.log(user); // This works!
console.log(user.email); // This returns undefined!
}
d.resolve();
});
return d.promise;
}
Any idea?
I also tried to use JSON.parse in case it was stringified (just to make sure) but it wasn't.
UPDATE
So seems like I can access the result using user._doc.email.
But what causes this? I don't remember having to do this before.
If a field in your document shows up when you console.log the whole document, but not when you directly access that field, it means the field is missing in the model's schema definition.
So add email to the schema of User.
I am running this command in my terminal
$mongo <database> -u user -p pw update.js
The update.js includes this code
var cursor = db.resources.find({
user: "dummy0"
});
print('cursor:', cursor);
var oldMatter = cursor.values.green;
My terminal output and the error I get is as follows
cursor: DBQuery: scrounge.resources -> { "user" : "dummy0" }
E QUERY TypeError: Cannot read property 'green' of undefined
What is the environment / API / driver I am using?
What is the correct Syntax to access the value?
Have you tried analyzing the cursor returned? db.resources.find will returns an object wrapped in an iterable cursor/array, so you can't just go about accessing properties of your object on the cursor variable - cursor.values.green will not work.
Try using findOne instead like so:
var obj = db.resources.findOne({
user: "dummy0"
});
This returns an object not wrapped as an iterable cursor/array so assuming it finds an object, you should be able to do
obj.values.green;