MongoDB: $addToSet duplicate result callback in Meteor - javascript

There are plenty of $addToSet topics out there, but after an hour of searching I still don't know how to evaluate in meteor-serverside-javascript-code if $addToSet added a new element to an array or it was a duplicate match.
The closest one I found was How to check if Mongo's $addToSet was a duplicate or not, but I don't know how to get a db object within meteor.
As written in other posts, the callback-function as a last parameter of the update method always returns 1 and it's always successful, no matter it's a duplicate or distinct element.
If there is no solution currently, I would like to know if there are other ways of checking a nested array (inside one specific collection) for a specific element. A simple true/false result information would be enough.
EDIT:
Ok, I managed to get the Bulk Update working now with the following lines:
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var col = db.collection("posts");
var batch = col.initializeOrderedBulkOp();
batch.find({_id: postid}).upsert().updateOne({"$addToSet": ...});
batch.execute(function(err, result) {
if (err) throw err;
console.log("RESULT: ", JSON.stringify(result));
//db.close();
});</code>
The $addToSet works with this implementation, but the result object returned from the execution, still, is always the same:
RESULT: {
"ok":1,
"writeErrors":[],
"writeConcernErrors":[],
"nInserted":0,
"nUpserted":0,
"nMatched":1,
"nModified":null,
"nRemoved":0,
"upserted":[]
}
The interesting value nModified stays null both for an update and (duplicate found) skip.
Any ideas?

If I re-phrase the question, you're trying to figure if what you're about to insert is already there.
Why not simply query the object for it before attempting to insert?
If you have a set (array) you can simply do
db.collection.find({array_key: object_to_find_in_array})
and see if it returns an object or nothing. Then update as necessary.

Related

Firestore Array of map not updating

So I'm working on a personal project to learn react-native and Firestore.
I have a DB like this:
And I want my code to add a new battery in the array batteries.
The elements in the array are just a map{string, string}
The problem is that when I update the array with a new brand that's work but if I want to update it with the same brand again have,
so having by the end
batteries[0]: {'brand': 'cestmoi'}
batteries[1]: {'brand': 'cestmoi'}
The DB doesn't update, doesn't have any error or so.
I don't understand why and I followed their tutorial. Here is my code:
async function addData(collection, doc, value) {
console.log(`Add data ${value.brand}`)
try {
const result = await firestore()
.collection(collection)
.doc(doc)
.set({
batteries: firestore.FieldValue.arrayUnion(value)
})
console.log(result);
return result;
} catch (error) {
return error;
}
}
I use try-catch by habit but I don't know if the then...catch is better or not.
As already #windowsill mentioned in his answer, there is no way you can add duplicate elements in an array using client-side code. If your application requires that, then you have to read the entire array, add the duplicates and then write the document back to Firestore.
However, if you want to update an existing element in an array of objects (maps) then you have to use arrayUnion with the entire object. If you want to understand the mechanism better, you can read the following article which is called:
How to update an array of objects in Firestore?
arrayUnion says that it "adds elements to an array but only elements not already present". Maybe it does a stringify or something to check equality and therefore doesn't add the new element. I think you'll have to 1. get the current list, 2. add your element, 3. set the batteries field to the updated list.

how to add object to firestore first index of object array

i've been googling around about how to add an object into an array in firestore, and found the arrayUnion() able to add an object into firestore array, but it only add the object into last index of array, but how to add it into first index of array?
//add "greater_virginia" into last index of array
washingtonRef.update({
regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")
});
//how to add "greater_virginia" into first index of array?
its basically same as arrayUnion but instead of add it into last index, i want to add it into first index of array.
If Firestore arrays behave anything like realtime-database arrays, then they don't actually exist. As far as I know, they are store as maps, like:
{
0: "first element",
2: "second and so on",
}
You can probably see how an unshift would be a big transformation. In fact, firestore doesn't let you do this, saying "...in order to avoid some of the issues that can arise in a multi-user environment, you'll be adding them with more of a set-like functionality".
With that in mind, this problem is usually solved at the application level by fetching the array, mutating it as needed, then setting the whole thing.
Bit of further reading https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
PS: be careful with the arrayUnion operator, because it actually performs a add to set
Firestore doesn't offer any way to modify items of array fields by index. arrayUnion will only ever append to the end of the array if the element doesn't already exist.
If you want to modify an array by index, you will have to read the document, modify the array in memory to appear how you want, then write the modified array back to the document.

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
});

mongo cursor findOne

I'm trying to open a stream of data from mongoDB by querying a field with an array of values, and return only one record per index of array.
var cursor = Collection.findOne({'lastName': { $in: [arrayOfLastNames]}}).cursor();
cursor.on('data', function (record) {
//do something with record
})
for whatever reason, it is pulling multiple records per index of arrayOfLastNames. The .findOne() method is behaving as .find() would.
Any suggestions as to why this is happening, or possible alternatives to this implementation would be greatly appreciated.
You're running into issues because you used a cursor. From the mongoDB documentation, "You cannot apply cursor methods to the result of findOne() because a single document is returned."
From your comment, you want to call findOne once for every item in the array since $in with findOne will find the first document according to the "natural order" of the document that matches any item in the array (you might also consider just using a general find honestly, since then your cursor solution will work).

How to increment mongodb fields using variable field name?

So, a little background about what I'm trying to create. I am trying to build a voting app, where a MongoDb database stores the options and the counters for each of those options to check how many times people have voted for each option. The data structure I have implemented looks like below.
_id:"57dfecfa3832360f46e183c2"
options:"iea, aoegboae, aoeugoa, ougr, gege"
options_counter:Object
0:0
1:0
2:0
3:0
4:0
__proto__:Object
title:"hr"
Each of those numbers [0,1,2,3,4] represents an option and the number after the colon represents the counter for each. My function to update the required counter is shown below
var fieldToIncrement = db.collection('polls').options_counter.field;
db.collection('polls').findOneAndUpdate(
{_id: user_id},
{$inc: {fieldToIncrement: 1}},
{upsert:false},
{returnNewDocument: true}
).toArray(function(err, doc){
if(err){
throw new Error(err);
}if(doc){
console.log(doc);
//callback(null, doc);
}
});
However, using the fieldToIncrement variable returns an error saying that cannot find property field of undefined. The variable field contains the number specifying the specific key of the options_counter object that needs to be incremented by 1. I have embedded the options_counter within the original document. My question is how do I set up the syntax so that MongoDb can find the specific key value pair within the options_counter object to update, and increment that counter by 1?
It looks like "options_counter" is a embedded document, for this you var fieldToIncrement has to be something like options_count.0.
I can't see this in your question.
please share your code where you define fieldToIncrement, is now seems undefined as also stated in the error you mentioned: "However, using the fieldToIncrement variable returns an error saying that cannot find property field of undefined."

Categories

Resources