Update nested variable field in firestore - node.js

Suppouse a firestore Database filled with this structure, being Events and Users collections:
Firestore
-Events
-1612483200
-peopleSignedDict
-Alex
-state: 1
-Mark:
-state: 0
-1606172400
-peopleSignedDict
-Users
-1zyXSMMlGiaUusUNLQWAxfsI4pM2
-name: Alex
-4zyXSMMlGiaUusUNLQWAxfsI4pM2
-name: Mark
I'm using Node.js and trying to update Events.1612483200.peopleSignedDict.Alex.state to change the state from 1 to 0. To achieve it I'm trying to do:
var randomName = getRandomDBName();
db.collection('Events').doc('1612483200').set({
'peopleSignedDict.'+randomName+'.state': 0
});
But ofc my IDE (PhpStorm) yells at me telling that this syntax is not valid, which should be the correct syntax to approach this?

There's a few way to do this.
My favorite is to first construct the key in a separate variable, and then use that in the collection:
let randomName = getRandomDBName();
let updates = {};
updates['peopleSignedDict.'+randomName+'.state'] = 0;
db.collection('Events').doc('1612483200').update(updates);
But a common (and shorter) alternative is:
let randomName = getRandomDBName();
db.collection('Events').doc('1612483200').update({
[`peopleSignedDict.${randomName}.state`]: 0;
});
I'm actually not sure if the [] are needed in that last snippet, so if you have problems with it, try it without them and let me know.

Related

Is there a way to add an incrementing id in one statement in MongoDB?

So I got a small database, It's not going to grow much more and I'm trying to get one document from the db in an API that I implemented in python so that with a given document Id I retrieve the document in the db. However, I find it a little hard to put the user to write a random number from the db. All I require is a function that modifies each document by setting an id field and to Auto-Increment. As I said, it's not going to grow that much and the performance isn't really an issue here.
So far what I've been able to do is this:
var i = 0
db.MyCollection.update({},
{$set : {"new_field":1}},
{upsert:false,
multi:true}
i ++;),
I achieved to set an id field but it sets the same number to each document (the count of every document) So let's say that if the db has 10 docs, it'll set the Id to 10.
Find-and-modify operation returns the document updated (before or after the update depending on returnDocument setting). You can use this with $inc to implement a counter. Ruby example where c is a collection:
irb(main):005:0> c['foo'].insert_one(counter:true,count:1)
=> #<Mongo::Operation::Insert::Result:0x8040 documents=[{"n"=>1, "opTime"=>{"ts"=>#<BSON::Timestamp:0x00005609f260b7e0 #seconds=1594961771, #increment=2>, "t"=>1}, "electionId"=>BSON::ObjectId('7fffffff0000000000000001'), "ok"=>1.0, "$clusterTime"=>{"clusterTime"=>#<BSON::Timestamp:0x00005609f260b538 #seconds=1594961771, #increment=2>, "signature"=>{"hash"=><BSON::Binary:0x8060 type=generic data=0x0000000000000000...>, "keyId"=>0}}, "operationTime"=>#<BSON::Timestamp:0x00005609f260b290 #seconds=1594961771, #increment=2>}]>
irb(main):011:0> c['foo'].find_one_and_update({counter:true},{'$inc':{count:1}})
=> {"_id"=>BSON::ObjectId('5f112f6b2c97a6281f63f575'), "counter"=>true, "count"=>1}
irb(main):012:0> c['foo'].find_one_and_update({counter:true},{'$inc':{count:1}})
=> {"_id"=>BSON::ObjectId('5f112f6b2c97a6281f63f575'), "counter"=>true, "count"=>2}
irb(main):013:0> c['foo'].find_one_and_update({counter:true},{'$inc':{count:1}})
=> {"_id"=>BSON::ObjectId('5f112f6b2c97a6281f63f575'), "counter"=>true, "count"=>3}
irb(main):014:0> c['foo'].find_one_and_update({counter:true},{'$inc':{count:1}})
=> {"_id"=>BSON::ObjectId('5f112f6b2c97a6281f63f575'), "counter"=>true, "count"=>4}
Why not just use this logic? Instead of updating all via one query, just launch multiple queries one by one? Mongo will do it pretty fast, even if you have >1M docs in database (according to your phrase: I got a small database) because pre-builded index on _id field.
this is a javasript code, but I guess, you'll understand the logic of it
let all_documents = db.MyCollection.find({});
for (let i = 0; i < all_documents.length; i++) {
db.MyCollection.update({_id: all_documents[i]._id }, {$set : {"new_field": i}}, {upsert:false})
}

Creating Node.js enum in code to match list of values in database

I have a list of valid values that I am storing in a data store. This list is about 20 items long now and will likely grow to around 100, maybe more.
I feel there are a variety of reasons it makes sense to store this in a data store rather than just storing in code. I want to be able to maintain the list and its metadata and make it accessible to other services, so it seems like a micro-service data store.
But in code, we want to make sure only values from the list are passed, and they can typically be hardcoded. So we would like to create an enum that can be used in code to ensure that valid values are passed.
I have created a simple node.js that can generate a JS file with the enum right from the data store. This could be regenerated anytime the file changes or maybe on a schedule. But sharing the enum file with any node.js applications that use it would not be trivial.
Has anyone done anything like this? Any reason why this would be a bad approach? Any feedback is welcome.
Piggy-backing off of this answer, which describes a way of creating an "enum" in JavaScript: you can grab the list of constants from your server (via an HTTP call) and then generate the enum in code, without the need for creating and loading a JavaScript source file.
Given that you have loaded your enumConstants from the back-end (here I hard-coded them):
const enumConstants = [
'FIRST',
'SECOND',
'THIRD'
];
const temp = {};
for (const constant of enumConstants) {
temp[constant] = constant;
}
const PlaceEnum = Object.freeze(temp);
console.log(PlaceEnum.FIRST);
// Or, in one line
const PlaceEnum2 = Object.freeze(enumConstants.reduce((o, c) => { o[c] = c; return o; }, {}));
console.log(PlaceEnum2.FIRST);
It is not ideal for code analysis or when using a smart editor, because the object is not explicitly defined and the editor will complain, but it will work.
Another approach is just to use an array and look for its members.
const members = ['first', 'second', 'third'...]
// then test for the members
members.indexOf('first') // 0
members.indexOf('third') // 2
members.indexOf('zero') // -1
members.indexOf('your_variable_to_test') // does it exist in the "enum"?
Any value that is >=0 will be a member of the list. -1 will not be a member. This doesn't "lock" the object like freeze (above) but I find it suffices for most of my similar scenarios.

NotesException: Unknown or unsupported object type in Vector

I'm trying to add new names to the address book programmatically but I'm getting the following error:
[TypeError] Exception occurred calling method NotesDocument.replaceItemValue(string, Array)
Unknown or unsupported object type in Vector
Code snippet below:
var addressBook = session.getDatabase("","names.nsf");
var gView:NotesView = addressBook.getView("($VIMGroups)");
var gDoc:NotesDocument = gView.getDocumentByKey("groupName", true);
var newg:java.util.Vector = [];
var mems:java.util.Vector = new Array(gDoc.getItemValue('Members'));
newg.push(mems);
var newNames:java.util.Vector = new Array(getComponent("NewMems").getValue());
newg.push(newNames);
gDoc.replaceItemValue("Members", newg);
gDoc.save();
Adding a single user works fine, but then it does not save users in the required canonical format below:
CN=John Doe/O=Org
Instead it is saved in the original format below:
John Doe/Org
I look forward to your suggestions. Thanks.
You can't store an Array in a field. Make newg a java.util.Vector instead and integrate with that.
For OpenNTF Domino API the team wrote a lot of code to auto-convert to Vectors, which may cover Arrays.
Don't use an Array (which is a JS thing). Initialize it as a Vector.
var newg:java.util.Vector = new java.util.Vectory();
Then look up the Vector methods to see how to add to that vector. Not sure if you will have to convert the names using the Name method but I would store them as "CN=Joe Smith/O=Test Org" to be sure you got the right format.
I was able to solve the issue using a forloop to loop through the list and push it into a newly created array. Using the forloop seems to make the difference.
var newg = [];
var group = new Array(getComponent("NewMems").getValue()), lenGA = group.length;
for(i = 0; i < lenGA; i++){
newg.push(group[i]);
}
gDoc.replaceItemValue("Members", newg);
gDoc.save();
An explanation about this behaviour will be appreciated.

CouchDB "[Circular]" when writing to an array

In CouchDB, I am writing to an array and keep getting the message "[Circular]". I am using Node.js to create the data to be written like this.
Say I have an two email objects in the same document in CouchDB:
unverifiedEmail = [{"address":"john#example.com","dateAdded":"1389215329484"}]
verifiedEmail = []
Now in Node.js I do this before writing.
var oldData = readFromCouchDb();
var newData = oldData;
newData.verifiedEmail.unshift(newData.unverifiedEmail[0]);
writeToCouchDb(newData);
Then when I view the document in Futon I see this:
unverifiedEmail = [{"address":"john#example.com","dateAdded":"1389215329484"}]
verifiedEmail = "[Circular]"
What's going on here?
I found out the issue has to do with the depth of the way to set Javascript objects equal to each other.
To solve this, I used the following code in place of the unshift above:
newData.verifiedEmail.unshift(JSON.parse(JSON.stringify(newData.unverifiedEmail[0])));

What is wrong in this LINQ Query, getting compile error

I have a list AllIDs:
List<IAddress> AllIDs = new List<IAddress>();
I want to do substring operation on a member field AddressId based on a character "_".
I am using below LINQ query but getting compilation error:
AllIDs= AllIDs.Where(s => s.AddressId.Length >= s.AddressId.IndexOf("_"))
.Select(s => s.AddressId.Substring(s.AddressId.IndexOf("_")))
.ToList();
Error:
Cannot implicitly convert type 'System.Collections.Generic.List<string>' to 'System.Collections.Generic.List<MyCompany.Common.Users.IAddress>'
AllIDs is a list of IAddress but you are selecting a string. The compiler is complaining it cannot convert a List<string> to a List<IAddress>. Did you mean the following instead?
var substrings = AllIDs.Where(...).Select(...).ToList();
If you want to put them back into Address objects (assuming you have an Address class in addition to your IAddress interface), you can do something like this (assuming the constructor for Address is in place):
AllIDs = AllIDs.Where(...).Select(new Address(s.AddressID.Substring(s.AddressID.IndexOf("_")))).ToList();
You should also look at using query syntax for LINQ instead of method syntax, it can clean up and improve the readability of a lot of queries like this. Your original (unmodified) query is roughly equivalent to this:
var substrings = from a in AllIDs
let id = a.AddressId
let idx = id.IndexOf("_")
where id.Length >= idx
select id.Substring(idx);
Though this is really just a style thing, and this compiles to the same thing as the original. One slight difference is that you only have to call String.IndexOf() one per entry, instead of twice per entry. let is your friend.
Maybe this?
var boundable =
from s id in AllIDs
where s.AddressId.Length >= s.AddressId.IndexOf("_")
select new { AddressId = s.AddressId.Substring(s.AddressId.IndexOf("_")) };
boundable = boundable.ToList();

Resources