Reputation: 3299
I have the following document structure
{
"_id" : 1,
"prices" : {
"100" : [
5.67,
.
.
1.7,
],
"101" : [
4.67,
.
.
1.4,
},
"v" : 2
}
We need to get the last value from each of the fields "100" and "101", e.g 1.7 & 1.4, using mongodb v3.0.2. Currently we are loading the whole document and then getting the required values in the application. It has become quite a bottleneck as each document may be ~1MB, which for 100s of documents is adding up.
We are hoping there is a suitable feature of mongodb that will allow us to just retrieve the data we need from the document, possibly as a separate call for each price field, i.e one call for the price "100" key and second call for the price "101" key.
We have experimented using the $slice operator but this unfortunately cannot be used with exclusion fields, https://jira.mongodb.org/browse/SERVER-3378.
We have also seen the following post: How to combine both $slice and select returned keys operation in function update?
Which almost works, but the return value from the query:
db.account_articles.find({"_id" : ObjectId("1")}, { "prices.100" : { $slice: -1 }, "_id" : 1 })
is:
{
"_id" : 1,
"prices" : {
"100" : [
1.7
],
"101" : [
4.67,
.
.
1.4,
}
}
i.e. it's 90% of the way there, we just need to be able to exclude the "101" field.
Are we missing something or is this not possible in monogodb?
Upvotes: 3
Views: 3392
Reputation: 28939
You can check out this simple and lot easier solution.
Return latest record from subdocument in Mongodb
just get the last index of your array item and you are done.
Upvotes: 1
Reputation: 50436
You need the aggregation framework to do this, I think you really should change the stucture, but working with what you have:
Model.aggregate(
[
{ "$match": { "prices.100": { "$exists": true } }},
{ "$unwind": "$prices.100" },
{ "$group": {
"_id": "$_id",
"price": { "$last": "$prices.100" }
}},
{ "$project": { "_id": 0, "prices.100": "$price" } }
]
)
In future releases you will be able to just use the new $slice
operator:
Model.aggregate(
[
{ "$match": { "prices.100": { "$exists": true } } },
{ "$project": {
"_id": 0,
"prices.100": { "$slice": ["$prices.100",-1] }
}}
]
)
In fact you can do "both" fields at the same time:
Model.aggregate(
[
{ "$match": { "prices.100": { "$exists": true } } },
{ "$project": {
"_id": 0,
"prices.100": { "$slice": ["$prices.100",-1] },
"prices.101": { "$slice": ["$prices.100",-1] }
}}
]
)
And that is a lot better than processing with $unwind
and $last
to get the last element of the array when $group
is applied to get the data back.
It basically has the same performance as a regular query in the newer form. In the present form, it's going to be slower.
Upvotes: 3