ref.update() removes omitted keys - works just like .set()? - javascript

I am attempting to use Firebase realtime database .update() to update a data node according to documentation. I am using .update() to selectively update only the referenced keys at the location - instead of replacing all the keys at the location.
My problem is that I have omitted key "createdAt" from the update object since I don't wish to update "createdAt" after initial create - but the "createdAt" key gets removed when using .update()! 「(゚ペ)
Database structure
Object sent to update()
{
"data": {
"notifyByEmail": false,
"notifyByPush": false,
"notifyBySms": false
},
"access/members": {
"1234567890": true,
"0987654321": true
}
}
Code
console.log('api', `${myPath}/${id}`, dbUpdateObj)
database.ref(`${myPath}/${id}`).update(dbUpdateObj).then(() => {
resolve({id, reduxStoreObj})
})
Logging
api categories/-LqXvFkeF_QfA5oHquYp [+] Object { data: {…}, "access/members": {…} }
Result
Why is "createdAt" removed? I thought .update() should only update defined keys and not touch omitted keys.
What have I missed?
Kind regards /K

A call to update acts like separate calls to set to each of the properties you pass in. So in your case the data and access/members get completely replaced, but all other properties under database.ref(`${myPath}/${id}`) stay unmodified.
If you want to update deeper level properties, include their entire path in the update map. So to maintain data/createdAt, specify the entire path to the notify... properties that you want to update:
{
"data/notifyByEmail": false,
"data/notifyByPush": false,
"data/notifyBySms": false,
"access/members": {
"1234567890": true,
"0987654321": true
}
}
I find that it's most common to have a flat map of complete paths in such multi-path update statements. So to also maintain the existing member of access/members, the above would become:
{
"data/notifyByEmail": false,
"data/notifyByPush": false,
"data/notifyBySms": false,
"access/members/1234567890": true,
"access/members/0987654321": true
}

You can use .set() with the {merge: true} option to avoid the (not changed) data be replaced by the new data.
See: https://firebase.google.com/docs/firestore/manage-data/add-data

Related

Directly update mapbox source features

I'm drawing orders on the map using layers with symbols, this orders has status. Whenever the status changes I'd like to change de color of the symbol.
My layer has this configuration:
map.addLayer({
id: "orders",
type: "symbol",
source: "order-markers",
layout: {
"icon-image": [
"match",
["get", "orderStatus"],
"UNASSIGNED",
"unassigned-marker",
"ASSIGNED",
"assigned-marker",
"IN_TRANSIT",
"intransit-marker",
"CONCLUDED",
"concluded-marker",
"UNASSIGNED-marker",
],
},
});
});
Is there a recommended way to directly access the source and update the status in the properties object ? Or is it right to always overwrite the whole data object with setData ?
I tried to create a state in React with the features but it seems that the features object dont react to the state changes.
Thanks.
I haven't found a way to update a single property on a single feature. What I've always done is to overwrite the data as you've mentioned (i.e. map.getSource('order-markers').setData(/*...*/)) with the whole data object where a feature's property was changed. This is the method mapbox uses in their example for updating with live data: live-update-feature
As an alternative however, you can update a single feature's state. If that's a possibility you can use map.setFeatureState.
for example:
map.setFeatureState(
{
source: 'order-markers',
id: feature.id,
}, {
status: "IN_TRANSIT"
}
);
You'll then have to update your layer style/expressions to use the state instead of property to properly reflect the data as they are slightly different in how you get the values.

Mongoose create document in wrong key:value order according to schema

So I have a problem with understanding of how Mongo .create and .findAndUpdate operation works. I have mongoose 5.4.2X and a model with schema which has lot of key:value pairs (without any nested objects) in the exact order (in the code below I use 1. 2. 3. etc to show you the right order) like this:
let schema = new mongoose.Schema({
1.code: {
type: String,
required: true
},
2.realm: {
type: String,
required: true,
},
3.type: {
type: String,
required: true,
enum: ['D', 'M'],
},
4.open: Number,
5.open_size: Number,
6.key: typeof value,..
7...another one key: value like previous one,
8.VaR_size: Number,
9.date: {
type: Date,
default: Date.now,
required: true
}
});
and a class object which have absolutely the same properties in the same order like schema above.
When I form data for Mongo via const contract = new Class_name (data) and using console.log(contract) I have a necessary object with properties in the exact right order like:
Contract {1.code: XXX, 2.realm: YYY, 3.type: D, .... 8.VaR_size: 12, 9.date: 327438}
but when I'm trying to create/update document to the DB via findOneAndUpdate or (findByID) it writes in alphabetical order but not the necessary 1->9, for example:
_id:5ca3ed3f4a547d4e88ee55ec
1.code:"RVBD-02.J"
7.VaR:0.9
(not the 1->9)...:...
8.VaR_size:0.22
__v:0
5.avg:169921
The full code snippet for writing is:
let test = await contracts.findOneAndUpdate(
{
code: `${item_ticker}-${moment().subtract(1, 'day').format('DD.MMM')}` //how to find
},
contract, //document for writinng and options below
{
upsert : true,
new: true,
setDefaultsOnInsert: true,
runValidators: true,
lean: true
}
).exec();
Yes, I have read the mongoose docs and don't find any option param for
solving my problem or probably some optional params are matter but
there are no description for this.
It's not my first project, the funny thing is, that when I'm inserting
tons-of-docs via .insertMany docs are inserted according to schema
order (not alphabetical)
My only question is:
How do I fix it? Is there any option param or it's a part of findAnd....
operations? If there is not solution, what should I do if for me it's
necessary the right ordering and check existence of document before
inserting it?
Update #1 after some time I rephrase my search query for google and find a relevant question on SW here: MongoDB field order and document position change after update
Guess I found the right answer according to the link that I post earlier. So yes, it's part of MongoDB:
MongoDB allocates space for a new document based on a certain padding
factor. If your update increases the size of the document beyond the
size originally allocated the document will be moved to the end of the
collection. The same concept applies to fields in a document.
by #Bernie Hackett
But in this useful comment still no solution, right? Wrong. It seems that the only way to evade this situation is using additions optional params during Model.find stage. The same ordering using during project stage via .aggregate and it looks like this:
Model.find({query},{
"field_one":1,
"field_two":1,
.............
"field_last":1
});

Is there a performance difference between adding a .indexOn security rule and not adding one?

{
1:{
status:"inactive"
},
2:{
status:"inactive"
},
3:{
status:"active"
}
}
Here is an example JSON structure. My question is if I do:
return mainDB.ref("products").orderByChild('status').equalTo('inactive').once('value').then(data =>{
console.log(JSON.stringify(data));
})
Is there a performance difference between adding a .indexOn security rule and not adding one, and also if .indexOn was added then where and when does it actually get filtered if not on the client?
Thank you!
If you perform a query on a property for which you've defined no index, Firebase will download all data from the location to the client, and perform the sorting and filtering there.
This means you should always define indexes for properties that you query on, which is why Firebase shows a pretty loud message in your JavaScript console when it finds you querying a property without an index.
Update for question in comments.
To create an index for the property status of each child node under a top-level node called products you'll need:
{
"rules": {
"products": {
".indexOn": "status"
}
}
}

How can I add a special Mongo expression to a JSON object?

So I'm aware how I should add any regular string/object to a JSON object - however, I need to add the following Mongo command to my JSON object:
$set : { "author" : req.body.name }
As you can see, it's not as simple as just doing:
myJsonObject.author = "$set..." etc.
Sorry if this is a dupe question - this isn't the easiest subject to Google without tons of unrelated answers popping up.
The reason I need to know this because I want to be able to build a JSON object based on what the user has changed in the form. So a new problem I have encountered is that even if I can build the JSON object with multiple items to change, MongoDB only updates the last item in the object. My example JSON object is here:
updateItem = {
$set : { "name" : "Squid bonobo" },
$set : { "author" : "Mardy Bum" }
};
and the snippet of code which issues the update command is as follows:
updateData.update(searchItem, updateItem, false, true, function(err, results) {
console.log(results);
db.close();
});
I found this solution at the following git site.
Can anybody help with either of these questions?
Thanks for your help :)
Cameron
You need to form your JSON in the below structure:
updateItem = {
$set : { "name":"Squid bonobo","author":"Mardy Bum"}
}
Javascript Objects can be declared with duplicate keys/properties, but their values get over-written, with the latest bound value for the key, in any order.
Here, $set is a key/property which has been set twice to the same object referenced by the variable updateItem, hence only one value, that is last encountered, will be associated to the key. In this case, the value that takes precedence is:
$set:{ "author":"Mardy Bum" }
and the final query that gets executed becomes,
updateItem = {
$set : { "author" : "Mardy Bum" }
};

Hidden properties to console.log or utils.inspect

I'm working with sails.js waterline orm. Now this is not particularly a sails question, but i have to place some context, so when you create a record you get back an object with the data created. If the record has other records (collections) associated, it has keys related to those in the returned object, but those keys are getters/setters, even if no data is present for those related objects.
I've simplified a few things just to expose the main point.
This is my user model:
var User = {
attributes:
status: {type:'string'}
images: {
collection: 'Image'
}
}
Lets assumme, i performed a create query on a User model, that has a images collection associated. The userRecord is what the query returned.
if i console.log this out it shows the properties related to the model itself
but not the associated records, even though the key is actually there, you can
access it but is not visible to console.log or utils.inspec even when setting show hidden to true.
console.log(userRecord)
This is what gets returned
{ name: 'zaggen'}
This is what should get returned
{ name: 'zaggen',
images: [{
path: 'some-path/img.png'
}]
}
And i can access the hidden property like this:
console.log(userRecord.images[0].path)
// outputs some-path/img.png
How is this even possible?, as far as i know there is no way to hide info to the console.log in node, except maybe when the properties are defined in the __proto__ object, but in this case they are not.
After searching for this i haven't found anything and its pretty weird so i thought it could be a good question for SO. It'll help on my work process if i could console.log this info and get all the data, right now i can use lodash and call clone or defaults and i get the object as it should.
as far as i know there is no way to hide info to the console.log in node, except maybe when the properties are defined in the proto object
That's no longer true in ES5. It was true in ES3.
Notice that even in the original javascript, objects and functions have hidden properties like .__proto__ or .constructor or .prototype? It was like some native javascript objects have these magic features (like how setting innerHTML can call the HTML compiler). ES5 exposes all that magic by way of Object.defineproperty.
The specific feature that hides a property from console.log() is enumerable. Setting it to false on a property makes it hidden from for..in (no need for .hasOwnProperty() anymore):
var foo = {a:1}
Object.defineProperty(foo,'b',{
enumerable: false, // hide it from for..in
value: 2
})
console.log(foo); // prints out {a:1}
console.log(foo.b); // prints out 2
There are other useful features such as getters and setters (allowing you to emulate properties like .innerHTML that calls a function when you write to it) and writable (allowing you to make a property read-only). See the full documentation for details: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Sails uses waterline which is where the model is defined. If you take a look at the source code for it, you see this:
https://github.com/balderdashy/waterline/blob/77fe3a9b9a9b12110a6ba079a84e5cd43a4369db/lib/waterline/model/lib/model.js#L57-L75
/**
* Log output
* #return {String} output when this model is util.inspect()ed
* (usually with console.log())
*/
Object.defineProperty(this, 'inspect', {
enumerable: false,
configurable: false,
writable: false,
value: function() {
var output;
try {
output = self.toObject();
} catch (e) {}
return output ? util.inspect(output) : self;
}
});
So they override the console.log output to self.toObject(). This is one of their internal methods that does all kinds of stuff that could be responsible for the output your seeing. For example:
// Don't run toJSON on records that were not populated
if (!self.proto._properties || !self.proto._properties.joins) return;
Or:
if (!this.proto._properties.showJoins) return;
I noticed in their integration tests, they pass { showJoins: true } as the second argument when creating the model. I couldn't find anything about it in the docs, but maybe you could try that?
https://github.com/balderdashy/waterline/blob/48dc007b69a133169651aeb422fa3a61c3c6802c/test/integration/model/save.js#L150

Categories