Update/Iterate over embedded Array MongoDB - mongodb

I have a document structure like so:
{
data: null,
subArray: [{
key: 'a',
subData: []
}, {
key: 'b',
subData: []
}]
}
I will receive an array containing subArray keys, like: `['a', 'b']
I want to iterate over the subArray to push a value to subData, so that when subArray is ['a','b'], my data will now be like:
{
data: null,
subArray: [{
key: 'a',
subData: ['dataToPush']
}, {
key: 'b',
subData: ['dataToPush']
}]
}
I have this query to do that:
update({_id:"...", "subArray.key":{"$in":['a','b']},
{'$addToSet':{'subArray.$.subData':'...'}}})
Thinking that $in will let me loop through my subArray.
But I realize this does not work because $in gets the elements that match the elements in the array, so I will get my data but not iterate over my subArray.
Is it possible to complete this with a specific selector/modifier combo?

No, not with multiple keys. For one key "a", you could use a query like
db.test.update({ "subArray.key" : "a" }, { "$push" : { "subArray.$.subData" : "dataToPush" } })
but the $ positional operator can only be used for a single match in one update. You could do one update like the above per key, or retrieve the document, modify it based on all of the keys, then save over the original.

Related

Remove entire objects by array of ids

I have an array of objects that contains metadata and looks similar to this.
Data:
metadata:[
{ matchid: '1', region: 'europe' },
{ matchid: '2', region: 'africa' },
{ matchid: '3', region: 'asia' },
]
I have an endpoint setup to receive an array of IDS ['1', '2'] which would the remove all the objects containing these IDS.
This is my current query:
Query to remove objects
xx.findByIdAndUpdate(
id,
$pullAll: {
"metadata.matchid": {
$in: req.body.matches
}
}
)
I am expecting both objects with the ids of 1 and 2 to be removed
Expected Results:
metadata:[
{ matchid: '3', region: 'asia' },
]
I am recieving an error I have never seen before it is an object that says codeName: "BadValue"
As documentation says:
The $pullAll operator removes all instances of the specified values from an existing array. Unlike the $pull operator that removes elements by specifying a query.
$pullAll requires and exact match and $pull is like to use a filter. So you can use $pull in this way.
yourModel.findByIdAndUpdate(
id,
$pull: {
metadata:{
matchid: { $in: req.body.matches}
}
}
)
Example here

MongoDB query for two input arrays at same index?

I have two arrays A and B of length n defined by the input,
fruit_ids = [{id: "id1"}, {id: "id2"}, {id:"id3"}];
fruit_names = [{name: "Orange"},{name: "Kiwi"},{name: "Banana"}]
and MongoDB documents
{ farm_id: "3344", fruits: [{name: "Orange", id:"id1"}, {name: "Kiwi", id:"id67"}]}
Now I want to write a Mongo query such that it pulls items from particular farm_id specified at array fruit_ids and fruit_names but at same index,
for example for the above input, I want for farm_id: 3344 {name: "Orange", id:"id1"} to get deleted.
Can anyone please help me.
You can use $pullAll operator to remove all the matching elements and build your update statement dynamically using below code:
var fruit_ids = [{id: "id1"}, {id: "id2"}, {id:"id3"}];
var fruit_names = [{name: "Orange"},{name: "Apple"},{name: "Banana"}];
var pullAll = {
$pullAll: { fruits: fruit_ids.map((id, index) => Object.assign(fruit_names[index], id)) }
}
db.col.update({ farm_id: 3344 }, pullAll)
This will only try to update the farm_id: 3344.
I was trying $pullAll as suggested by #mickl in his answer, but the thing is I had other fields inside my embedded documents and because $pullAll works only for exact matches, that's why I currently I am using $pull with $or on the array of embedded docs. I found this solution from this answer how-to-force-mongodb-pullall-to-disregard-document-order.
let arr = [{name: "Orange", id:"id1"}, {name: "Kiwi", id:"id67"}];
db.col.update(
{ farm_id: 3344 },
{ "$pull": { "fruits": { "$or": arr } }}
)

MongoDB: Add and remove from array field at the same time

I want to rename tags in our documents' tags array, e.g. change all tags a in the collection to c. The documents look something like this:
[ { _id: …, tags: ['a', 'b', 'c'] },
{ _id: …, tags: ['a', 'b'] },
{ _id: …, tags: ['b', 'c', 'd'] } ]
I need to keep tags unique. This means, an update like this will not work, because the first document will end up containing tag c twice:
db.docs.update(
{ tags: 'a' },
{ $set: { 'tags.$': 'c' } }
)
So, I tried this alternatively:
db.docs.update(
{ tags: 'a' },
{
$pull: { 'a' },
$addToSet: { 'c' }
}
)
But this gives a MongoError: Cannot update 'tags' and 'tags' at the same time.
Any chance of renaming the tags with one single update?
According to official MongoDB documentation, there is no way of expressing "replace" operation on a set of elements. So I guess, there isn't a way to do this in single update.
Update:
After some more investigation, I came across this document. If I understand it correctly, your query should look like this:
db.docs.update({
tags: 'a'
}, {
$set: { 'tags.$': 'c'}
})
Where 'tags.$' represents selector of the first element in "tags" array that matches the query, so it replaces first occurence of 'a' with 'c'. As I understand, your "tags" array does not contain duplicates, so first match will be the only match.

Copy and Filter Array (Extract Array Element) using Aggregation Framework

I'm using MongoDB 3.2
I have a single document with an embedded Array, for which I'd like to extract a single element and keep the rest of the whole document.
My document:
{
_id: 1,
items: [
{
type: "A",
id: 41
},
{
type: "A",
id: 42
},
{
type: "B",
id: 43
},
...
]
}
Now I'd like to have some query that does the following things:
Find document with _id=1
Take the items array, find all elements with type=A AND id=42. Then put those elements within the machtingItems array.
Slice the items array, so it contains max. 2 elements
The result should be this:
{
_id: 1,
items: [
{
type: "A",
id: 41
},
{
type: "A",
id: 42
}
],
matchingItems: [
{
type: "A",
id: 42
}
]
}
Basically there should always be only a single element matching type=A and id=42, but after looking at the Aggregation Framework it might be easier to just use an Array for matchingItems instead of a simple field.
I know how to $match the whole document and how to $slice the array. But I just can't figure out how to extract a single element from the array and place it inside another field. (Step 2)
Thanks in advance!

Creating MongoDB index for all items in array

I have some documents in my mongodb database with the following structure, I want to know how to create an index for document.data.0.data[0-9999].val1?
My problem is where I type [0-9999] I don't know how many elements will be inside this data array, but I want that all the elements inside this array have an index on the val1 key.
Any tips?
{
"_id" : ObjectId("55c455c2fc07853b78c4b6b9"),
"data": {
"0": {
"data": [
{
val1: "abc",
val2: "def"
},
{
val1: "abc",
val2: "def"
}
]
}
}
}
You don't include the array index values in the index, so it would just be:
db.mycoll.ensureIndex({'data.0.data.val1': 1})
This is termed a multikey index in MongoDB.