Get n-th element of an array in MongoDB - mongodb

As part of my document in MongoDB I'm storing an array of objects. How can I query it for only the 4th element of the array for example? So I don't want the get the entire array out, just the 4th element.

Use $slice.
db.foo.find({ bar : "xyz" } , { my_array : { $slice : [n , 1] } } )
will retrieve the nth element of the array "my_array" of all documents in the foo collection where bar = "xyz".
Some other examples from the MongoDB documentation:
db.posts.find({}, {comments:{$slice: 5}}) // first 5 comments
db.posts.find({}, {comments:{$slice: -5}}) // last 5 comments
db.posts.find({}, {comments:{$slice: [20, 10]}}) // skip 20, limit 10
db.posts.find({}, {comments:{$slice: [-20, 10]}}) // 20 from end, limit 10
Which you can read here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields

You can use the $arrayElemAt operator new in MongoDB 3.2 to return the element at the specified array index.
Demo:
A collection named baskets contains documents that look like this:
{
"_id" : ObjectId("578f326f6db61a299a383c5a"),
"fruits" : [
"apple",
"mango",
"banana",
"apricot",
"cherry"
]
}
The following query return the element at index -2 (second element) in the "fruits" array.
db.baskets.aggregate(
[
{ "$project": { "matched": { "$arrayElemAt": [ "$fruits", 1 ] } } }
]
)
which produces
{
"_id" : ObjectId("578f326f6db61a299a383c5a"),
"matched" : "mango"
}
And the following query the element before the last element in the array; thus the element at index -2
db.baskets.aggregate(
[
{ "$project": { "matched": { "$arrayElemAt": [ "$fruits", -2 ] } } }
]
)
which yields:
{
"_id" : ObjectId("578f326f6db61a299a383c5a"),
"matched" : "apricot"
}

Another way to do this is to use the update array syntax. Here, contribs.1 sets the second element in the contribs array to have value ALGOL 58 (Taken from the manual page on update syntax)
db.bios.update(
{ _id: 1 },
{ $set: { 'contribs.1': 'ALGOL 58' } }
)

Related

Mongodb: push element to nested array if the condition is met

I have the following collection:
{
"_id": 11,
"outerArray": [
{ "_id" : 21,
"field": {
"innerArray" : [
1,
2,
3
]
}
},
{ "_id" : 22,
"field": {
"innerArray" : [
2,
3
]
}
},
{ "_id" : 23,
"field": {
"innerArray" : [
2
]
}
}
]
}
I need to go through all documents in collection and push to innerArray new element 4, if innerArray already contains element 1 or element 3
I tried to do it this way, and few others, similar to this one, but it didn't work as expected, it only pushes to innerArray of first element of outerArray
db.collection.updateMany(
{ "outerArray.field.innerArray": { $in: [ 1, 3 ] } },
{ $push: { "outerArray.$.field.innerArray": 4} }
)
How to make it push to all coresponding innerArrays?
Problem here is your missunderstanding a copule things.
When you do "outerArray.field.innerArray": { $in: [ 1, 3 ] } into your query, your are not getting only innerArray where has 1 or 3. You are gettings documents where exists these arrays.
So you are querying the entire document.
You have to use arrayFilter to update values when the filter is match.
So, If I've understood you correctly, the query you want is:
db.collection.update(
{}, //Empty object to find all documents
{
$push: { "outerArray.$[elem].field.innerArray": 4 }
},
{
"arrayFilters": [ { "elem.field.innerArray": { $in: [ 1, 3 ] } } ]
})
Example here
Note how the first object into update is empty. You have to put there the field to match the document (not the array, the document).
If you want to update only one document you have to fill first object (query object) with values you want, for example: {"_id": 11}.

Finding the mongoDB object

I have a Sequence : [k1,k4,k6,k10]and a MongoDB object as follows:{"_id" : "x" , "k" : [ "k1", "k2 ","k6"]},{"_id" : "y" , "k" : [ "k2", "k4 ","k10","k11"]},{"_id" : "z" , "k" : [ "k4", "k6 ","k10","k12"]}
I have to find the particular object which has a maximum number of matching elements from the array.In this case, it will be "z" as it has three matching elements in the "k" array i,e ["k4","k6","k10"].
So I wanted to know that is there a MongoDB way of doing?
You can use this query :
db.data.aggregate([{
$match: {
k: {
$in: ["k1", "k4", "k6", "k10"]
}
}
}, {
$addFields: {
count: {
$size:{
$setIntersection: [
["k1", "k4", "k6", "k10"], "$k"
]
}
}
}
}, {
$sort: {
count: -1
}
}, {
$limit: 1
}])
This query will :
filter on relevant records (non-necessary but better perfs with it)
Calculate the count of matching elements using the set intersection
sort them by count descending
return the first record
Note: There can be multiple records with maximum matches, the above query gets you only one.

MongoDB project the documents with count greater than 2 [duplicate]

This question already has answers here:
Query for documents where array size is greater than 1
(14 answers)
Closed 6 years ago.
I have a collection like
{
"_id": "201503110040020021",
"Line": "1", // several documents may have this Line value
"LineStart": ISODate("2015-03-11T06:49:35.000Z"),
"SSCEXPEND": [{
"Secuence": 10,
"Title": 1,
},
{
"Secuence": 183,
"Title": 613,
},
...
],
} {
"_id": "201503110040020022",
"Line": "1", // several documents may have this Line value
"LineStart": ISODate("2015-03-11T06:49:35.000Z"),
"SSCEXPEND": [{
"Secuence": 10,
"Title": 1,
},
],
}
SSCEXPEND is an array. I am trying to count the size of SSC array and project if the count is greater than or equal to 2. My query is something like this
db.entity.aggregate(
[
{
$project: {
SSCEXPEND_count: {$size: "$SSCEXPEND"}
}
},
{
$match: {
"SSCEXPEND_count2": {$gte: ["$SSCEXPEND_count",2]}
}
}
]
)
I am expecting the output to be only the the first document whose array size is greater than 2.
Project part is working fine and I am able to get the counts but I need to project only those which has count greater than or equal to two but my match part is not working. Can any one guide me as where am I going wrong?
You need to project the other fields and your $match pipeline will just need to do a query on the newly-created field to filter the documents based on the array size. Something like the following should work:
db.entity.aggregate([
{
"$project": {
"Line": 1,
"LineStart": 1, "SSCEXPEND": 1,
"SSCEXPEND_count": { "$size": "$SSCEXPEND" }
}
},
{
"$match": {
"SSCEXPEND_count": { "$gte": 2 }
}
}
])
Sample Output:
/* 0 */
{
"result" : [
{
"_id" : "201503110040020021",
"Line" : "1",
"LineStart" : ISODate("2015-03-11T06:49:35.000Z"),
"SSCEXPEND" : [
{
"Secuence" : 10,
"Title" : 1
},
{
"Secuence" : 183,
"Title" : 613
}
],
"SSCEXPEND_count" : 2
}
],
"ok" : 1
}
This is actually a very simple query, where the trick is to use a property of "dot notation" in order to test the array. All you really need to ask for is documents where the array index of 2 $exists, which means the array must contain 3 elements or more:
db.entity.find({ "SSCEXPEND.2": { "$exists": true } })
It's the fastest way to do it and can even use indexes. No need for calculations in aggregation operations.

Mongodb: find documents with array field that contains more than one SAME specified value

There is three documents in collection test:
// document 1
{
"id": 1,
"score": [3,2,5,4,5]
}
// document 2
{
"id": 2,
"score": [5,5]
}
// document 3
{
"id": 3,
"score": [5,3,3]
}
I want to fetch documents that score field contains [5,5].
query:
db.test.find( {"score": {"$all": [5,5]}} )
will return document 1, 2 and 3, but I only want to fetch document 1 and 2.
How can I do this?
After reading your problem I personally think mongodb not supported yet this kind of query. If any one knows about how to find this using mongo query they defiantly post answers here.
But I think this will possible using mongo forEach method, so below code will match your criteria
db.collectionName.find().forEach(function(myDoc) {
var scoreCounts = {};
var arr = myDoc.score;
for (var i = 0; i < arr.length; i++) {
var num = arr[i];
scoreCounts[num] = scoreCounts[num] ? scoreCounts[num] + 1 : 1;
}
if (scoreCounts[5] >= 2) { //scoreCounts[5] this find occurrence of 5
printjsononeline(myDoc);
}
});
Changed in version 2.6.
The $all is equivalent to an $and operation of the specified values; i.e. the following statement:
{ tags: { $all: [ "ssl" , "security" ] } }
is equivalent to:
{ $and: [ { tags: "ssl" }, { tags: "security" } ] }
I think you need to pass in a nested array -
So try
db.test.find( {"score": {"$all": [[5,5]]}} )
Source
Changed in version 2.6.
When passed an array of a nested array (e.g. [ [ "A" ] ] ), $all can now match documents where the field contains the nested array as an element (e.g. field: [ [ "A" ], ... ]), or the field equals the nested array (e.g. field: [ "A" ]).
http://docs.mongodb.org/manual/reference/operator/query/all/
You can do it with an aggregation. The first step can use an index on { "score" : 1 } but the rest is hard work.
db.test.aggregate([
{ "$match" : { "score" : 5 } },
{ "$unwind" : "$score" },
{ "$match" : { "score" : 5 } },
{ "$group" : { "_id" : "$_id", "sz" : { "$sum" : 1 } } }, // use $first here to include other fields in the results
{ "$match" : { "sz" : { "$gte" : 2 } } }
])

how to match the last value of array in mongo db? [duplicate]

I have a sample document like shown below
{
"_id" : "docID",
"ARRAY" : [
{
"k" : "value",
"T" : "20:15:35",
"I" : "Hai"
},
{
"K" : "some value",
"T" : "20:16:35",
"I" : "Hello"
},
{
"K" : "some other value",
"T" : "20:15:35",
"I" : "Update"
}
]
}
I am trying to update the last element in the "ARRAY" based on field "ARRAY.T"(which is only field i know at the point of update), but what my problem is first element in the array matches the query and its ARRAY.I field is updated.
Query used to update:
db.collection.update( { _id: "docID","ARRAY.T" : "20:15:35"},
{ $set: { "ARRAY.$.I": "Updated value" }
})
Actually i don't know index of the array where to update so i have to use ARRAY.I in the query, is there any way to to tell Mongodb to update the first element matched the query from last of the array.
I understand what you are saying in that you want to match the last element in this case or in fact process the match in reverse order. There is no way to modify this and the index stored in the positional $ operator will always be the "first" match.
But you can change your approach to this, as the default behavior of $push is to "append" to the end of the array. But MongoDB 2.6 introduced a $position modifier so you can in fact always "pre-pend" to the array meaning your "oldest" item is at the end.
Take this for example:
db.artest.update(
{ "array": { "$in": [5] } },
{ "$push": { "array": { "$each": [5], "$position": 0 } }},
{ "upsert": true }
)
db.artest.update(
{ "array": { "$in": [5] } },
{ "$push": { "array": { "$each": [6], "$position": 0 } }},
{ "upsert": true }
)
This results in a document that is the "reverse" of the normal $push behavior:
{ "_id" : ObjectId("53eaf4517d0dc314962c93f4"), "array" : [ 6, 5 ] }
Alternately you could apply the $sort modifier when updating your documents in order to "order" the elements so they were reversed. But that may not be the best option if duplicate values are stored.
So look into storing your arrays in "reverse" if you intend to match the "newest" items "first". Currently that is your only way of getting your "match from last" behavior.