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.
Related
I'd like to increment some field in nested array or if document not exists, or object in array - create them.
Example of existing filled document:
{
_id: "some_id_1",
my_objects: [
{
objectId: "abc",
fieldA: -5,
fieldB: 44
},
{
objectId: "123",
fieldA: -6,
fieldB: 88
}
]
}
To increment fieldA in array's object with id abc I can write query:
colection.updateOne(
{$and:[
{$eq:[_id, "some_id_1"]},
{$eq:[objectId, "abc"]}
]},
{$inc:{"my_objects.$.fieldA":1}}
)
and it works.
The problem is when this collection is empty or my_objects is empty. If document doesn't exist I'd like to create the document with object in array . Or if document exists but array my_objects is empty I'd like to add object with fieldA to array.
///documnet after single update query. Is it possible?
{
_id: "some_id_1",
my_objects: [
{
objectId: "abc",
fieldA: 1,
}
]
}
This question already has an answer here:
Update array with multiple conditions in mongodb
(1 answer)
Closed 4 years ago.
I don't know if it is possible.
I'm trying to do an automatic process to update all elements of a nested array in some documents. The array hasn't a fixed length.
Below is a simplified example of the collection:
{
"_id" : ObjectId("5ba2e413a4dd01725a658c63"),
"MyOwnID" : "123456789",
"MyArray" : [
{
Field1: 'FooName1',
Field2: 'FooSpec1',
FieldToUpdate: '...'
},
{
Field1: 'FooName1',
Field2: 'FooSpec2',
FieldToUpdate: '...'
},
{
... More elements ...
}
]
},
{
"_id" : ObjectId("5ba2e413a4dd01725a658c63"),
"MyOwnID" : "987654321",
"MyArray" : [
{
Field1: 'FooName1',
Field2: 'FooSpec1',
FieldToUpdate: '...'
},
{
Field1: 'FooName2',
Field2: 'FooSpec2',
FieldToUpdate: '...'
},
]
}
I tried and it worked for the first element:
Query for the second element:
db.getCollection('works').findOneAndUpdate(
{ MyOwnID: '123456789', '$and':[ { 'MyArray.Field1': 'FooName1' },{ 'MyArray.Field2': 'FooSpec1' } ] } ,
{ '$set': { 'MyArray.$.FieldToUpdate': 1234} }
)
But when I try to update the second element only the first is updated.
Query for the second element:
db.getCollection('works').findOneAndUpdate(
{ MyOwnID: '123456789', '$and':[ { 'MyArray.Field1': 'FooName1' },{ 'MyArray.Field2': 'FooSpec2' } ] } ,
{ '$set': { 'MyArray.$.FieldToUpdate': 4321} }
)
I tried with arrayFilters option and $elemMatch, both give me an error.
Any options?
You can try below query using $elemMatch
db.getCollection("works").findOneAndUpdate(
{
"MyOwnID": "123456789",
"MyArray": { "$elemMatch": { "Field1": "FooName1", "Field2": "FooSpec2" }}
},
{ "$set": { "MyArray.$.FieldToUpdate": 4321 }}
)
You tried with arrayFilters, but probably in a wrong way, becuse it's working with it. It's not very clear in mongoDB doc, but $[myRef] acts as a placeholder for arrayFilters. Knowing that, you can do this to achieve your goal :
db['01'].findOneAndUpdate(
{MyOwnID: '123456789'},
{$set:{"MyArray.$[object].FieldToUpdate":1234}},
{arrayFilters:[{ $and:[{'object.Field1': 'FooName1' },{ 'object.Field2': 'FooSpec1' }]}]}
)
Note that the unique document in arrayFilters is needed (with $and operator), because both conditions refer to the placeholder. If you put 2 conditions,
({arrayFilters:[{'object.Field1': 'FooName1' },{ 'object.Field2':
'FooSpec1' }]})
MongoDB will complain about two criteria with same base placeholder.
While the answer given by #Anthony Winzlet is right and works perfectly, it will only update the first array element matching conditions defined in $elemMatch, that's why i avoid to use it like that (unless having a unique index on including MyArray.Field1 and MyArray.Field2, you can't be sure that the matching element is unique in your array)
So, I have the following structure in some documents:
{
"Group": [
{
"data": {
"field1": "VALUE1",
"otherfield": "XXXX"
}
},
{
"data": {
"field1": "VALUE2",
"otherfield": "YYYYY"
}
}
]
}
The size of the Group array can be either 0, 1 or 2 in size. What I need to to is match the documents which contains both of VALUE1 and VALUE2 for field1. Couldn't find a suitable answer in here for this specific case.
I tried using $elemMatch but it will not work to bring only documents with both values. That is, it will work like an or not and.
You can use the $all array query operator with dot notation for this:
db.test.find({'Group.data.field1': {$all: ['VALUE1', 'VALUE2']}})
The $all operator selects the documents where the value of a field is an array that contains all the specified elements.
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.
I would like to ranked in descending order a list of documents in array names via their number value.
Here's the structure part of my collection :
_id: ObjectId("W")
var1: "X",
var2: "Y",
var3: "Z",
comments: {
names: [
{
number: 1;
},
{
number: 3;
},
{
number: 2;
}
],
field: Y;
}
but all my request with db.collection.find().sort( { "comments.names.number": -1 } ) doesn't work.
the desired output sort is :
{ "_id" : ObjectId("W"), "var1" : "X", "var3" : "Z", "comments" : { [ { "number" : 3 }, { "number" : 2 },{ "number" : 1 } ], "field": "Y" } }
Can you help me?
You need to aggregate the result, as below:
Unwind the names array.
Sort the records based on comments.names.number in descending
order.
Group the records based on the _id field.
project the required structure.
Code:
db.collection.aggregate([
{$unwind:"$comments.names"},
{$sort:{"comments.names.number":-1}},
{$group:{"_id":"$_id",
"var1":{$first:"$var1"},
"var2":{$first:"$var2"},
"var3":{$first:"$var3"},
"field":{$first:"$comments.field"},
"names":{$push:"$comments.names"}}},
{$project:{"comments":{"names":"$names","field":"$field"},"var1":1,
"var2":1,"var3":1}}
],{"allowDiskUse":true})
If your collection is large, you might want to add a $match criteria in the beginning of the aggregation pipeline to filter records or use (allowDiskUse:true), to facilitate sorting large number of records.
db.collection.aggregate([
{$match:{"_id":someId}},
{$unwind:"$comments.names"},
{$sort:{"comments.names.number":-1}},
{$group:{"_id":"$_id",
"var1":{$first:"$var1"},
"var2":{$first:"$var2"},
"var3":{$first:"$var3"},
"field":{$first:"$comments.field"},
"names":{$push:"$comments.names"}}},
{$project:{"comments":{"names":"$names","field":"$field"},"var1":1,
"var2":1,"var3":1}}
])
What The below query does:
db.collection.find().sort( { "comments.names.number": -1 } )
is to find all the documents, then sort those documents based on the number field in descending order. What this actually does is for each document get the comments.names.number field value which is the largest, for each document. And then sort the parent documents based on this number. It doesn't manipulate the names array inside each parent document.
You need update document for sort an array.
db.collection.update(
{ _id: 1 },
{
$push: {
comments.names: {
$each: [ ],
$sort: { number: -1 }
}
}
}
)
check documentation here:
http://docs.mongodb.org/manual/reference/operator/update/sort/#use-sort-with-other-push-modifiers
MongoDB queries sort the result documents based on the collection of fields specified in the sort. They do not sort arrays within a document. If you want the array sorted, you need to sort it yourself after you retrieve the document, or store the array in sorted order. See this old SO answer from Stennie.