Mongoose push item to array if not exists - node.js

I have a model schema like this:
Collection: {
irrelevant fields,
object: {
more irrelevant fields,
array:
[
{
field1,
field2,
_id: false
}
]
}
}
I want to push to array any object {field1, field2} that does not repeat with field1 already existing in array (in an updateOne query).
field2 is constantly updating itself and i have tried addtoset but it considers field2 so it ends up adding another item i don't want to add.
For example:
array:[
{field1: 1, field2: 6},
{field1: 2, field2: 4},
{field1: 3, field2: 1}
]
if i would want to push {field1: 2, field2: 6} it should not let me push it because the field1:2 already exists in array.
(using addtoset, even it does check that the field1:2 already existis, it ends up adding te object because of field2 being different)
array:[
{field1: 1, field2: 6},
{field1: 2, field2: 4},
{field1: 3, field2: 1}
{field1: 2, field2: 6}
]

You could use the pipeline form of update with the $cond operator. If the field1 value already exists, keep the value of the array the same, if not, append the new value to the end.
Perhaps something similar to:
const newValue = {field1: 2, field2: 6};
db.collection.update({match criteria},
[{$set:{
array:{
$cond:{
if: {$in: [newValue.field1, "$array.field1"]},
then: "$array",
else: {$concatArrays: ["$array", [newValue]]}]}
}
}
}}]
)

Related

Is it possible to delete the firsts elements in an array?

I'm coding an application, and in one of the parts of the code I need to delete a certain amount of elements from an array, but I don't have much experience with MongoDB/Mongoose.
For example, my query needs to delete the first n elements of an array:
let array = ["a", "b", "c", "d", "e"];
if (n < array.length) {
array = array.slice(n); // Delete the first n values
}
I could loop my code, but it will do a lot of queries, deleting one by one, which is bad for performance. My other alternative is to query the array elements, slice in the code, and update the database array with another query. It's also a good alternative, but I prefer to ask here because maybe there is a way to do it with just one query.
I know in MongoDB there is a way to just remove the first/last element, but I haven't found anything about deleting more than one.
Model.updateOne(filter, { $pop: { list: -1 } });
Hey you can check out the $slice operator when you query. That said, it works well, if you're removing multiple elements from the end or multiple elements from the start of the array. But if you're removing from the middle of the array, it may be easier to find the document first, apply a standard .slice() operation in JavaScript, and then save the document with the new array, by calling the .save() method on the document.
As examples for the $slice operator:
const document = { "_id" : 3, "scores" : [ 89, 70, 100, 20 ] }
db.students.update(
{ _id: 3 },
{
$push: {
scores: {
$each: [ ],
$slice: -2
}
}
}
)
/*
Our result is the last two elements, and we removed the first two.
*/
const result = { "_id" : 3, "scores" : [ 100, 20 ] }
Or you can something like the following:
db.students.update({_id: 3}, [
{$set: {scores: {
$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]
}}}
]);
Where P is the index of element you want to stop removing in the array, leaving you with P to the end elements. (meaning all elements prior to P are removed)
Or this:
db.students.update({_id: 3}, [
{$set: {scores: {
$slice: ["$field", P]
}}}
]);
Where P is the index of element you want to stop removing in the array from the end, leaving you with start to P elements. (meaning all elements after P are removed)

How to find a document by its position/index in the array?

I need to retrieve let's say the documents at position 1,5 and 8 in a MongoDB database using Mongoose.
Is it possible at all to get a document by its position in a collection? If so, could you show how to do that?
I need something like this:
var someDocs = MyModel.find({<collectionIndex>: [1, 5, 8]}, function(err, docs) {
//do something with the three documents
})
I tried to do the following to see what indexes are used in collection but I get the 'getIndexes is not a function' error:
var indexes = MyModel.getIndexes();
Appreciate any help.
If by position 5 for example, you mean the literal 5th element in your collection, that isn't the best way to go. A Mongo collection is usually in the order in which you inserted the elements, but it may not always be. See the answer here and check the docs on natural order: https://stackoverflow.com/a/33018164/7531267.
In your case, you might have a unique id on each record that you can query by.
Assuming the [1, 5, 8] you mentioned are the ids, something like this should do it:
var someDocs = MyModel.find({ $or: [{ id: 1 }, { id: 5 }, { id: 8 }]}}, function(err, cb) {
//do something with the three documents
})
You can also read about $in to replace $or and clean up the query a bit.
Assume you have this document in users collections:
{
_id: ObjectId('...'),
name: 'wrq',
friends: ['A', 'B', 'C']
}
Code below to search first and thrid friend of user 'wrq':
db.users.aggregate(
[
{
$match: {
name: 'wrq'
}
},
{
$project:{
friend1: {
$arrayElemAt: ["$friends", 0]
},
friend3: {
$arrayElemAt: ["$friends", 2]
}
}
}
]
)

Insert an array item after a specific value in mongodb

I have db like;
{"_id" : 1 , children: ["2", "123", "5"]}
I want to insert an element into children array. It is easy when you know the index. $position can be used for this purpose. Like;
db.module_menu.update({"_id" : "1"} ,
{$push : {"children" : {
$each : ["12"] , $position : 1}}});
But lets say I don't know the position index, I know the value of element that I want to insert after.
I have value: 12 and value that I want to insert after. insertAfter: 2
As a result it should be;
{"_id" : 1, text: "" , children: ["2","12", "123", "5"]}
How can I do it?
Go through each element in the children array find the index of value and by using $position, insert the value. Is it the only way to do it?
I'm afraid it's not possible. There's an ancient issue to use $function in update but you're only way is to read, modify and persist the array.
On the other hand if you an array of embedded documents then you can use the $ positional operator.
The $ can be used to $set a value in you simple array but to $push.
> db.test.find()
{ "_id" : ObjectId("56d9570d09e01f20e254d0f3"), "a" : [ 1, 2, 3 ] }
> db.test.update({"_id": ObjectId("56d9570d09e01f20e254d0f3"), "a": 2},
{$push: {a: {$each: [2], $position: "$"}}})
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 2,
"errmsg" : "The value for $position must be a positive numeric value not a String"
}
})
There is no function to get position and update at one go,
so in you problem you need to get position before update and pass it to query - so that means we are operating on 1:1 bassis.
Other approach is to retrieve data - perform in-place update and push full array set back to mongo

node.js, mongoose how to find in array of objects by multiple IDs

I am using Node.js with Mongoose module and ran in to a problem.
I have a Schema mockup which looks like this:
_id: '',
foo: '',
bar: '',
fb: [{
id: ''
}]
How do I find all objects in collection for matching fb[0].id plus passing in an array of IDs?
I have tried this (find() method with following query):
{'fb[0].id': {$in: friendsIDList}}
And this:
{'fb': {$in: [{id: friendsIDList}]} }
And even this
{'fb': {$in: {$in: friendsIDList}}}
To be more clear, I have an users object which contains their FB data in fb param, but it is an array containing just one object with data.
Now I receive a friend IDs list and want to query all of the user friends.
MongoDB has something called dot notation so you can query with 'foo.0.id': 'id' .
Tested as follows:
> db.users.insert({foo: 'foo', bar: 'bar', fb: [ { id: '456' } ]})
> db.users.find({'fb.0.id': '456'}).pretty()
{
"_id" : ObjectId("52fb695e403fb143985459dc"),
"foo" : "foo",
"bar" : "bar",
"fb" : [
{
"id" : "456"
}
]
}

Nested arrays in Mongoose

In the collection I'm working on, a document looks like this:
{
name: 'Myname',
other: 'other',
stuff: [
['something', 12, 4, 'somethingelse'],
['morestuff', 2, 4, 8],
['finally', 12, 'again', 58],
]
}
I wrote this Mongoose schema to access it:
var MyDocSchema = new Schema({
name: String,
other: String,
stuff: [],
});
When I query a doc, everything works well, the output shown in the console is right. But when, I try to do console.log(myDoc.stuff), I got the following:
['something', 12, 4, 'somethingelse', 'morestuff', 2, 4, 8, 'finally', 12, 'again', 58]
instead of
[
['something', 12, 4, 'somethingelse'],
['morestuff', 2, 4, 8],
['finally', 12, 'again', 58],
]
What am I doing wrong? Thank you for your help!!
Disclaimer: This response is pretty dated, 2012! It might not be the most accurate.
From the Mongoose documentation.
http://mongoosejs.com/docs/schematypes.html: Scroll down to the Array section:
Note: specifying an empty array is equivalent to [Mixed]. The
following all create arrays of Mixed.
Details on what that means is in the Mixed section right above the Array section.
Here's what you need to do.
Define a schema for the embedded documents:
var Stuff = new Schema({
name: String,
value1: Number,
...
});
Use that instead of an empty array []:
var MyDocSchema = new Schema({
name: String,
other: String,
stuff: [Stuff],
});

Resources