Trouble with accessing Firebase Cloud Firestore - javascript

I am using Cloud Firestore and am having some problems accessing a value in a simple database.
Here is how the database is structured
This is the code I am using to access the "basementER-status" field in the database.
//current status value is pulled from database
function getRawStatus ()
{
return db.collection("rooms").doc("roomsDoc").get().then(function(doc) {
console.log(doc.data());
console.log(doc.data().basementER-status);
return doc.data().basementER-status;
});
}
For the first console.log, this is printed to the console:
{1ER-status: 0, 2ER-status: 0, basementER-status: 0}
1ER-status: 0
2ER-status: 0
basementER-status: 0
__proto__: Object
This is the correct doc that needs to be brought from the database, so I know that part of my code is right.
However, the second console.log prints NaN to the console.
What is happening here? I don't understand. I've accessed fields like this before in cloud firestore and it has always worked.

For your second console.log statement, you are trying to access a specific parameter in an object, so you should use this:
console.log(doc.data()["basementER-status"]);
For more info, go here: firestore adding data link

The problem is because of the way JavaScript parses your statement. This line:
console.log(doc.data().basementER-status);
Is actually performing a mathematical subtraction between doc.data().basementER and the value of the variable status. That's not what you want.
If you want the value of a field with JavaScript operators or other special characters in it, you will have to use a different syntax:
const data = doc.data();
console.log(data['basementER-status']);
The square brackets let you provide an arbitrary string to look up the name property in the object.

Related

Firestore onSnapshot with "where" and "orderBy" not matching any documents

I am struggling to figure out why the following code produces the "doc.empty" console log. The screenshot shows what I see in Cloud Firestore's data console. My abbreviated code is below. In my example, I have the following variables:
dataset = '202203aam'
custnum = '19930'
firestoredb.collection('sold').doc(dataset).collection('sold').where('custnum', '==', parseInt(custnum)).orderBy('lot', 'asc').onSnapshot(function(doc){
if (doc.empty){
console.log('doc.empty');
} else {
doc.forEach(function(doc){
//code here
});
}
});
Why would this not match my data?
The problem is here:
.where('custnum', '==', parseInt(custnum))
The screenshot shows that your custnum field has a string value, yet you are explicitly passing a numeric value in the condition. Strings and numeric values are never the same in the database, so the condition doesn't match the document you how.
To make the query work, make sure you pass the value as the same type that you've stored in the database.

Firebase Update using a String Object

I am making a simple program to log in tasks, when someone updates the field, I would like to update the document with only what was updated, thus I made a string generator that would create the parameters :
function fireBaseUpdateStringMaker(update_field,update_val)
{
return '\''+update_field+'\''+' : '+'\''+update_val+'\''+','
}
which creates a field as such : 'update_field' : 'update_val'
Using this directly in my code works well if I follow the documentation from Firebase
db.collections('this_collection').doc('this_doc').update({ 'update_field' : 'update_val' })
but if I pass this to the update as a variable then it doesnt work
var updater = 'update_field' : 'update_val' ;
db.collections('this_collection').doc('this_doc').update({ updater })
I saw answers from other questions mentioned using square notation but using it here gives an error " ':' expected" , not putting the square notation creates a new field in my db called "updater" with content " 'update_field' : 'update_val' ".
Is there anyway to do this or should I simply just update every value everytime I'm editing a document? I'm quite new to NOSQL/Firebase so this is still not too intuitive for me. Any help is appreciated. thank you.
What you're doing isn't valid use of Firestore APIs. You can't compose a string of field/value pairs - the document data must be a JavaScript object. You have to populate a JavaScript object with the names of the fields a properties, along with their associated values.
This should work:
db.collection('this_collection').doc('this_doc').update({
field: "value"
})
If you want to compose the object separately, do so as normal in JavaScript:
const data = { field: "value" }
db.collection('this_collection').doc('this_doc').update(data)

How to increment a map value in a Firestore array

I have a firestore firebase database , in which I have a collection users
there is an array in the collection and in the array there is a map
in map there is a field qty.. I want to increment that qty value..
using increment doesnt help as the qty is inside a array index
db.collection("users").doc(checkId).update({
myCart: firebase.firestore.FieldValue.arrayUnion({
qty: firebase.firestore.FieldValue.increment(1),
}),
this is the error Output =>
Uncaught (in promise) FirebaseError: Function FieldValue.arrayUnion() called with invalid data. FieldValue.increment() can only be used with update() and set()
My answer below won't work, given that the qty is in an array. The only way to update an item in an array is to read the entire document, update the item in the array, and then write the entire array with the updated item back to the document.
An alternative would be to use a map instead of an array, and then update the qty using the approach outlined in my (old, and non-working) answer below 👇
You need to specify the full path to the field you're trying to update. So I think in your case, that'll be:
db.collection("users").doc(checkId).update({
"myCart.0.qty": firebase.firestore.FieldValue.increment(1)
}),
The field you want to update is embedded in an array. In this case, you can't use FieldValue.increment(), since it's not possible to call out an array element as a named field value.
What you'll have to do instead is read the entire document, modify the field in memory to contain what you want, and update the field back into the document. Also consider using a transaction for this if you need to update to be atomic.
(If the field wasn't part of an array, you could use FieldValue.increment().)
As of today (29-04-2020)... this is tested by me.
Suppose my data structure is like this:
collection: Users
Any document: say jdfhjksdhfw
It has a map like below
map name: UserPageVisits
map fields: field1,field2,field3 etc
Now we can increment the number field in the map like below:
mapname.field1 etc...
That is use the dot operator to access the fields inside the map just like you would do to an object of javascript.
JAVA Code (Android), update the field using transactions so they can complete atomically.
transaction.update(<documentreference object>,"UserPageVisits.field1",FieldValue.increment(1));
I have just pushed a version of my app which uses this concept and it's working.
Kudos !!
My Best Regards
Previous answers helped me as well, but dont forget about the "merge" property!!! Otherwise it will overwrite your entire array, losing other fields.
var myIndex = 0;
const userRef = db.collection('users').doc(checkId);
return userRef.update({
'myCart.${myIndex}.qty': admin.firestore.FieldValue.increment(1)
}, {
merge: true
});

Creating a 'truly global' integer variable using Web API Storage

I have two html documents, and I want them to share a variable and its value.
In lieu of 'truly global' variables in JavaScript, I've tried employing Web API Storage:
var number = parseInt(localStorage.setItem('num',0));
Storage is always a string, so I try parsing into an integer seemingly incorrectly (rather than showing up as 0 in my program, it shows up as 'NaN'). I can only assume that my syntax's wrong?
I want this variable to be increaseable (+1 every time the user clicks on something) but have not yet figured this out. I then want to retrieve its value in the second html document. But due to the initial misstep in attempting to parse the storage value I cannot yet attempt these methods.
It doesn't work like that because localStorage.setItem returns undefined. The way to do it would be:
// define variable
var num = 0;
// set to storage
localStorage.setItem('num', num);
// get from storage
num = parseInt(localStorage.getItem('num'));
// write-back incremented
localStorage.setItem('num', num + 1);
localStorage.setItem stores the value but doesn't return anything - or in other words returns undefined. Trying to parse undefined as a number returns NaN.
Note that localStorage does not work across files when viewing files in your browser from file://, you'll need to run a minimal server to test your code.

How can I get the key as well as the value when using db.js to query IndexedDB?

I have an IndexedDB of changes. I add an item like this, and then log the result to check the key has been created successfully:
_this._idb.add('steps', step).done(function (items) {
var item = items[0];
_logger.log("ADDED STEP", { id: item.__id__, step: item }, "CT");
});
The output from this is as expected:
...as you can see, the id has been added to the object when it is stored.
However, when I query the db to getback a list of objects, using this code:
this._idb.steps.query('timestamp').bound(start, end).execute().done(function (results) {
_logger.log("Results", results, "CT");
}
I don't get the id as part of the object that is returned:
... and the lack of id makes updating and deleting impossible.
How can I get the id of the item when I query indexed db using db.js - or am I approaching this in the wrong way, and is there something else I should be doing?
(Note: I'm using TypeScript to compile the JS, but I don't think that's especially relevant to this question)
This is expected behaviour, you're only going to get the __id__ property if you don't define a keyPath in your db schema.
Because there's no keyPath defined the value is not associated with it in indexeddb, it's only added to the resulting object after it has been added, because at that point in time we know the auto-incremented value that IndexedDB has assigned to it.
Since the value isn't really part of the object I don't have any way to assign it to the object when it comes out during a query, maybe I could use the position in the array but that's more likely to be wrong than right.
If you want the ID to be persisted against the object then you need to define a keyPath as part of the object store schema and the property will be added to the resulting object and available and it will be on the object returned from a query.
Disclaimer - I wrote db.js
Looking at the source, __id__ is only defined when your keyPath is null in the add() method. From what I'm seeing, you'll never see this in a query() response.
In IDB null keyPaths are allowed only when using auto-incrementing ("out-of-line") keys. So if you're getting the object back, it should have an auto-incrementing key on it or some other keyPath.
The __ prefix in JavaScript usually means the developer intended it to be a "private" property. I'm guessing this is for internal use and you shouldn't be counting on this in your application code.
Consider using explicit, so-called "in-line" keys on your object store.
The goal of db.js is easy and simple to use. Your is advanced use case.

Categories

Resources