Emulator database onUpdate update.after.ref incorrect - node.js

I use the emulator for functions and realtime database, and have a test onUpdate function attached to a secondary database (not the default one).
const admin = require("firebase-admin");
admin.initializeApp();
const ref = admin.app().database("https://non-default-database.firebaseio.com").ref();
exports.test = functions
.region("europe-west1")
.database.instance("non-default-database")
.ref("test/test")
.onUpdate(async (update) => {
let before = update.before.val();
let after = update.after.val();
console.log(`Beforeval: ${before} .:. Afterval: ${after}`);
console.log(update.after.ref.toString());
if (after !== before) { //avoid infinite loop
console.log(
"Before set: ",
(await update.after.ref.once("value")).val(),
(await ref.child(`test/test`).once("value")).val()
);
await update.after.ref.set(false);
console.log(
"After set: ",
(await update.after.ref.once("value")).val(),
(await ref.child(`test/test`).once("value")).val()
);
}
});
Yes, all the function does is set the value it is attached to to false, and spew some info to the console.
If the value at 'test/test' equals false to begin with and we set it to true, this is the console output:
Beforeval: false .:. Afterval: true
http://localhost:9000/test/test
Before set: false true
After set: false false
In the first line we can see that the values before and after are what we expect.
The second output line seems okay, but it looks like it is missing ?ns=non-default-database (first hint).
The third line should be the value as read from the database before setting. First using the update.after.ref reference and second using the root reference of the secondary database. We would expect both to return true since we just set it to be true. However, the first one is false (second hint).
The fourth and last line gives us the same as the previous one but after setting. Here we do get what we expect again.
When we look at the database, the value is still true.
Now let's change the function to not set it to false but to the string foobar, thus await update.after.ref.set(false); becomes await update.after.ref.set("foobar"); and let's look at the console output when we change the value from false to true again:
Beforeval: false .:. Afterval: true
http://localhost:9000/test/test
Before set: false true
After set: foobar true
The first three lines are the same, but hey, look at the fourth line!
The value when reading from update.after.ref is indeed set to the string foobar, but the value when reading from the root ref is still true (third hint).
When we look in the database webpage of the emulator we see that the value is still true.
Something else did change, there is a new database (that we probably missed before) named 'fake-server' that I never made. That database is completely empty though (fourth hint).
Okay let's look at the values using the REST API.
Going to http://localhost:9000/test/test.json gives us null.
Going to http://localhost:9000/test/test.json?ns=non-default-database gives us true.
Going to http://localhost:9000/test/test.json?ns=fake-server gives us null.
If we change await update.after.ref.set(false); to await ref.child('test/test').set(false); it does all work perfectly.
My conclusion from all of this is that when using a secondary server and emulating triggers on those, the update variable passed in by onUpdate forgets to use that second database, uses some non-existing one, which in some way makes the fake-server database appear but doesn't reference the secondary database.

Related

Cloud Function ArrayRemove not deleting array value, but function runs without error

My cloud function is not deleting value from the array it is suppose to. It gets all the values correctly as I see in my debug console, but the value in the array is still there.
My code statement is as follows:
for (const id of membersUnderStructure){
const ele = context.params.structure;
console.log(`Deleting ${ele} From ${id}`);
await db.collection(`TeamMember`)
.doc(id)
.update({teamStructIds: admin.firestore.FieldValue.arrayRemove(ele)})
I know the problem is with the admin.firestore.... line as if I put a constant value there, it does update fine. My console shows the correct value of 'ele' and 'id' as well, so the values are correct. The function executes 'ok' without any error but does NOT delete the value of 'ele' from the 'teamStructIds' array.
I am not sure what am I doing wrong here?
Solution:
The Array is of 'Number' and it is trying to delete a String of 'ele'.
I just changed to parseInt(context.parama.structure) and it works now.

NodeJS | Mongoose not updating value in DB

In array I'm updating a value. I am setting member approved value to true then I console.log all object and it shows the array with users updated approved status to true. But value is not saved to MongoDB after save I still see that approved value is false. This is the code:
userIndex = isInMembersArray.members.findIndex(user => user.username === requestedMember);
isInMembersArray.members[userIndex].approved = true;
console.log(isInMembersArray) // shows data with updated user in members array
isInMembersArray.save();
Use isInMembersArray.markModified("user.approved "); before isInMembersArray.save();
You need to explicitly call markModified() when you update a subpath of a mixed path.
Per Docs
Since Mixed is a schema-less type, you can change the value to anything else you like, but Mongoose loses the ability to auto detect and save those changes. To tell Mongoose that the value of a Mixed type has changed, you need to call doc.markModified(path), passing the path to the Mixed type you just changed.

firebase Starting point was already set

I use firebase admin and realtime database on node.js
Data look like
When I want to get data where batch = batch-7, I was doing
let batch = "batch-7";
let ref = admin.database().ref('qr/');
ref.orderByChild("batch").equalTo(batch).on('value', (snapshot) =>
{
res.json(Object.assign({}, snapshot.val()));
ref.off();
});
All was OK!
But now i should create pagination, i.e. I should receive data on 10 elements depending on the page.
I use this code:
let page = req.query.page;// num page
let batch = req.params.batch;// batch name
let ref = admin.database().ref('qr/');
ref.orderByChild("batch").startAt(+page*10).limitToFirst(10).equalTo(batch)
.on('value', (snapshot) =>
{
res.json(Object.assign({}, snapshot.val()));
ref.off();
});
But I have error:
Query.equalTo: Starting point was already set (by another call to startAt or equalTo)
How do I get data in the amount of N, starting at position M, where batch equal my batch
You can only call one startAt (and/or endAt) OR equalTo. Calling both is not possible, nor does it make a lot of sense.
You seem to have a general misunderstanding of how startAt works though, as you're passing in an offset. Firebase queries are not offset based, but work purely on the value, often also referred to as an anchor node.
So when you want to get the data for a second page, and you order by batch, you need to pass in the value of batch for the anchor node; first item that you want to be returned. This anchor node is typically the last item of the previous page, since you don't know the first item of the next page yet. And for this anchor node, you need to know the value of the item you order on (batch) and usually also its key (if/when there may be multiple nodes with the same value for batch).
It also means that you usually request one item more than you need, which is the anchor node.
So when you request the first page, you should track the key/batch of the last node:
var lastKey, lastValue;
ref.orderByChild("batch").equalTo(batch).limitToFirst(10).on('value', (snapshot) => {
snapshot.forEach((child) => {
lastKey = child.key;
lastValue = child.child('batch').value();
})
})
Then when you need the second page, you do a query like that:
ref.orderByChild("batch").start(lastValue, lastKey).endAt(lastValue+"\uf8ff").limitToFirst(11).on('value', (snapshot) => {
snapshot.forEach((child) => {
lastKey = child.key;
lastValue = child.child('batch').value();
})
})
There's one more trick above here: I use startAt instead of equalTo, so that we can get pagination working. But it then uses endAt to ensure we still end at the correct item, by using the last known Unicode character as the last batch value to return.
I'd also highly recommend checking out some of the previous questions on pagination with the Firebase Realtime Database.

How to limit .once('value) in firebase-admin node.js

How do I limit .once('value') in firebase-admin?
Code:
async function GetStuff(limit, page){
const data = await ref.limitToFirst(parseInt(limit)).once('value')
return data.val();
}
I wanted to create a page system, where a it sends request for a limited amount of data, and the user can change the page to get different data, but for some reason, I can't get it to work.
The code above only gets the first 20(when limit is 20), but how can I make it start at 20, so I can make this page feature.
I thought:
Code:
async function GetStuff(limit, page){
const data = await ref.startAt(limit*page).limitToFirst(parseInt(limit)).once('value')
return data.val();
}
You might want to review the relevant documentation. It looks like you're trying to pass the offset of a child to startAt, but that's not how startAt works. It accepts the actual value of the child to start at. Pagination by offset index is not supported.
The way you use startAt is typically passing the last sorted value retrieved by the prior query (or, if you don't want to retrieve that value again, 1 + that value, or a string that is lexically greater than the last string received. As such, some data sets might actually be difficult to paginate if they have the same sorted value repeated many times.

Nodejs node-sqlite3 run callback not working

I am trying to perform a delete of a row in sqlite db using nodejs and node-sqlite3 package.
When I run the delete command, and manually check the entries, I can see that the query successfully deleted that row but I cant seem to write the code that confirms this.
This is the query
db.run("DELETE FROM Table1 WHERE id=? AND username=?", [id, user], function(error) {
console.log(error);
});
Regardless of a wrong or right input, it outputs null to the console. If the right details are given, it deletes it and prints null, if wrong id and user are given, it still prints null.
Any ideas on what might be wrong?
Thanks
To my prevoius question, the problem was that I've used fat arrow for callback declaration. From javascript documentation I've discovered that in arrow function (fat arrow ), this has lexical scope and so this result undefined and not valued as in library documentation said. Using otherwise anonimous function, this is bounded in dynamic scope and so this.changes is valued.
Now, with code as below, is ok:
var sql = 'update recipes set stars = stars + 1 where id = ?';
db.run(sql,
[
1 // id = 1 execute update - if id = 11111 do nothing
], function(err) {
if(err)
throw err;
console.log("VALUE CHANGES: " + this.changes + " - " + util.inspect(this, { showHidden: false, depth: null }));
if(this.changes == 1)
console.log("WORK DONE");
else
console.log("NOTHING DONE");
});
Here more explanations: https://github.com/mapbox/node-sqlite3/issues/606
There is nothing wrong in the node and node-sqlite3 behaviour here.
Here are two parts to explain first regarding node and other regarding Sqlite.
Node
Your callback is getting called after execution of the statement. So nothing wrong here, since your callback is getting called (as proved by 'null' as output).
Sqlite
Delete query in Sqlite deletes if condition given in where clause evaluates to true, otherwise nothing is deleted.
Referring from node-sqlite3 documentation's Database#run api:
callback (optional): If given, it will be called when an error occurs
during any step of the statement preparation or execution, and after
the query was run. If an error occurred, the first (and only)
parameter will be an error object containing the error message. If
execution was successful, the first parameter is null.
So, in your case query execution succeeds without any error, resulting in error argument to callback function null as you see in output.
Further, if you want to check if any row was actually removed, you can use changes property as mentioned in the documentation:
If execution was successful, it contains two properties named "lastID"
and "changes" which contain the value of the last inserted row ID and
the number of rows affected by this query respectively. Note that
"lastID" only contains valid information when the query was a
successfully completed INSERT statement and "changes" only contains
valid information when the query was a successfully completed UPDATE
or DELETE statement. In all other cases, the content of these
properties is inaccurate and should not be used. The .run() function
is the only query method that sets these two values; all other query
methods such as .all() or .get() don't retrieve these values.
Hope it helps...
I had similar problem, callbacks just would not fire. Period. The problem was that elsewhere I was calling process.exit(1), so my code was exiting before the the callbacks had a chance to return.
Search for process.exit, that may (or may not) save you hours of debugging and blaming sqlite :)
Off the topic: What bugs my mind is why they all-cap ID in lastID. It's not like it's an abbreviation like SQL or USA. It stands for Identification, which is one word.

Resources