Push and slice multiple times in MongoDB - mongodb

I'm trying to do a push a new value to a capped array:
db.messages.insert({name:"test1"})
db.messages.update({name:"test1"}, {"$push":{"output": {"$each": ["test1"], "$slice": -10}}})
db.messages.update({name:"test1"}, {"$push":{"output": {"$each": ["test2"], "$slice": -10}}})
So, the first time I execute the update, I get what I expect:
{
"_id" : ObjectId("51d482ee7252cb3f7eb81ac1"),
"name" : "test1",
"output" : [
"test1"
]
}
But, after the second update, I get the following:
{
"_id" : ObjectId("51d482ee7252cb3f7eb81ac1"),
"name" : "test1",
"output" : [
"test1",
{
"$each" : [
"test2"
],
"$slice" : -10
}
]
}
When I expected the following:
{
"_id" : ObjectId("51d482ee7252cb3f7eb81ac1"),
"name" : "test1",
"output" : [
"test1",
"test2"
]
}
Probably I'm not understanding how to use $push with $slice, but looking at the documentation I couldn't figure out what I'm doing wrong. How can achive adding a new element to a capped array?

The behavior you observed can be reproduced in MongoDB version 2.2. Your example works as expected in MongoDB version 2.4
> db.things.insert({name:"test1"})
> db.things.update({name:"test1"}, {"$push":{"output": {"$each": ["test1"], "$slice": -10}}})
> db.things.find()
{ "_id" : ObjectId("51d489f730d17c1ffbd6ff9f"), "name" : "test1", "output" : [ "test1" ] }
> db.things.update({name:"test1"}, {"$push":{"output": {"$each": ["test2"], "$slice": -10}}})
> db.things.find()
{ "_id" : ObjectId("51d489f730d17c1ffbd6ff9f"), "name" : "test1", "output" : [ "test1", "test2" ] }
From the documentation, $each support to $push operator was added in version 2.4
http://docs.mongodb.org/manual/reference/operator/each/#op._S_each
Most likely you are using an older version of MongoDB that does not support $each with the $push operator. What version of MongoDB are you running? You can find out the version you are running by using the following command from MongoDB shell.
>db.version()
To get the latest release of MongoDB, please go to the following link.
http://www.mongodb.org/downloads

Related

Scala MongoDB aggregate group and match query

I want a query that will take the latest version out of each document, and check if some given string (applicationId) is in the list allowedApplications.
documents example:
{
"applicationId" : "y...",
"allowedApplications": ["x..."],
"name" : "some-name",
"version" : 3
}
{
"applicationId" : "y...",
"allowedApplications": ["x..."],
"name" : "some-name",
"version" : 2
}
{
"applicationId" : "x...",
"allowedApplications": ["y..."],
"name" : "some-other-name",
"version" : 1
}
So the MongoDB query is:
db.getCollection('..').aggregate(
[
{ "$match": { "allowedApplications": "x..." }},
{"$group": { "_id": "$name", "version": { "$max": "$version" }}}
]
)
And the query will output the name and version (I'll perhaps add the allowedApplications later).
I'm trying now to write this in Scala's mongodb driver.
I tried a bunch of stuff, for example:
collection
.aggregate(List(
`match`(equal("allowedApplications", "x..")),
group("$name", addToSet("version", addToSet("$max", "¢version")))
)
)
But couldn't get it to work.
Using Scala 2.13.1 and mongo-scala-driver 4.1.0.
Any help would be appreciated.
Found the answer:
collection
.aggregate(List(
`match`(equal("allowedApplications", "x...")),
group("$name", max("version", "$version"))
)
The order isn't quite the same, but just use the function in the accumulator field.

MongoDB 4.0 aggregation addFields not saving documents after using toDate

I have the following documents,
{
"_id" : ObjectId("5b85312981c1634f59751604"),
"date" : "0"
},
{
"_id" : ObjectId("5b85312981c1634f59751604"),
"date" : "20180330"
},
{
"_id" : ObjectId("5b85312981c1634f59751604"),
"date" : "20180402"
},
{
"_id" : ObjectId("5b85312981c1634f59751604"),
"date" : "20180323"
},
I tried to convert date to ISODate using $toDate in aggregation,
db.documents.aggregate( [ { "$addFields": { "received_date": { "$cond": [ {"$ne": ["$date", "0"] }, {"$toDate": "$date"}, new Date("1970-01-01") ] } } } ] )
the query executed fine, but when I
db.documents.find({})
to examine all the documents, nothing changed, I am wondering how to fix it. I am using MongoDB 4.0.6 on Linux Mint 19.1 X64.
As they mentioned in the comments, aggregate doesn't update documents in the database directly (just an output of them).
If you'd like to permanently add a new field to documents via aggregation (aka update the documents in the database), use the following .forEach/.updateOne method:
Your example:
db.documents
.aggregate([{"$addFields":{"received_date":{"$cond":[{"$ne":["$date","0"]}, {"$toDate": "$date"}, new Date("1970-01-01")]}}}])
.forEach(function (x){db.documents.updateOne({_id: x._id}, {$set: {"received_date": x.received_date}})})
Since _id's value is an ObjectID(), there may be a slight modification you need to do to {_id:x._id}. If there is, let me know and I'll update it!
Another example:
db.users.find().pretty()
{ "_id" : ObjectId("5acb81b53306361018814849"), "name" : "A", "age" : 1 }
{ "_id" : ObjectId("5acb81b5330636101881484a"), "name" : "B", "age" : 2 }
{ "_id" : ObjectId("5acb81b5330636101881484b"), "name" : "C", "age" : 3 }
db.users
.aggregate([{$addFields:{totalAge:{$sum:"$age"}}}])
.forEach(function (x){db.users.updateOne({name: x.name}, {$set: {totalAge: x.totalAge}})})
Being able to update collections via the aggregation pipeline seems to be quite valuable because of what you have the power to do with aggregation (e.g. what you did in your question, doing calculations based on other fields within the document, etc.). I'm newer to MongoDB so maybe updating collections via aggregation pipeline is "bad practice", but it works and it's been quite valuable for me. I wonder why it isn't more straight-forward to do?
Note: I came up with this method after discovering Nazo's now-deprecated .save() method. Shoutout to Nazo!

How to trim MongoDB aggregate result's one field

This is my data structure store in DB
{
"_id" : ObjectId("58a4e451c164f95c98e96235"),
"_class" : "com.contix.log.parser.log.Log",
"vin" : "6",
"esn" : "c",
"volt" : 11.32,
"internVolt" : 4.14,
"temp" : 39.49,
"timestamp" : NumberLong("1483375743285")
}
What I want to do is get latest 10 unique volt, internVolt, temp based on vin and esn String. Also needs latest timestamp. Then I'm trying to use mongo aggregate way to get right result.
db.log.aggregate({$sort:{timestamp:-1}},{$group:{_id : {esn:"$esn",vin:"$vin"},firstTimestamp:{$first:"$timestamp"},volts:{$addToSet:"$volt"}}},{$limit:5})
But this is my result looks like
{ "_id" : { "esn" : "b", "vin" : "2" }, "firstTimestamp" :
NumberLong("1485852368147"), "volts" : [ 11.95, 10.08, 10.77, 10.47,
11.41, 10.36, 10.96, 10.75, 10.39, 10.53, 10.1, 10.22, 11.16, 10.11, 11.87, 11.33, 11.82, 11.78, 10.25, 11.86, 10.5, 10.41, 11.3, 11.31, 11.97, 10.64, 11.57, 10.93, 10.02, 10.68, 10.9, 11.53, 10.46, 11.42, 11.73, 11.32, 10.19, 10.51, 11.35, 11.28, 10.65, 10.21, 11.18, 10.91, 11.43, 10.52, 11.34, 11.1, 10.99, 10.61, 10.28, 10.97, 10.3, 10.31, 11.81, 11.8, 10.42, 11.51, 10.72, 11.39, 10.69, 11.27, 11.11, 10.15, 10.78, 10.58, 11.49, 10.94, 11.64, 10.32, 11.63, 10.03, 10.81, 11.83, 10.82, 11.84, 10.79, 10.66, 11.21, 10.24, 11.75, 11.2 ] }
And other 4 similar stuff.
I don't know if there is any way that can trim $volts this kind data int group pipeline. The $limit or $skip operation seems use for whole documents.
My dream result should look like below.
{ "_id" : { "esn" : "b", "vin" : "2" }, "firstTimestamp" :
NumberLong("1485852368147"), "volts" : [ 10.81, 11.83, 10.82, 11.84, 10.79, 10.66, 11.21, 10.24, 11.75, 11.2 ], "innerVolts":[...], "temp":[...] }
If you need to trim the results, you can use projection and do something like this:
db.log.aggregate([
{$sort:{timestamp:-1}},
{$group:{
_id : {esn:"$esn",vin:"$vin"},
firstTimestamp:{$first:"$timestamp"},
volts:{$addToSet:"$volt"},
innerVolts:{$addToSet:"$innerVolt"},
temp:{$addToSet:"$temp"}
}},
{ $project: {
_id:1,
firstTimestamp:1,
volts: {$slice : ["$volts",10]},
innerVolts: {$slice : ["$innerVolts",10]},
temp: {$slice:["$temp",10]}
}}])
Hope my answer was helpful.
You can use the $slice modifier to trim the array to the lastest N items.

$each not working in mongodb

I would like to add new data into a child element of a field in mongodb using update query with $push and $each, But it directly insert the whole part of each operator. please help me to fix it. My query given below.
> db.Groups.insert({ "_id" : ObjectId("55b54aa4e2aa83f1f123a1a2"), "_creator" : ObjectId("55b2932cb57f47c0be6f071f"), "_messages" : ["hi"], "_inactive" : [ ], "_active" : [ Obje
> .Groups.update({ "_id" : ObjectId("55b54aa4e2aa83f1f123a1a2")},{$push: {_active : { $each: [ ObjectId("55b2932cb57f47c0be6f072f"), ObjectId("55b2932cb57f47c0be6f073f") ]}}});
Result after running these queries
{ "__v" : 39, "_active" : [ ObjectId("55b2932cb57f47c0be6f071f"), ObjectId("55b28b203a6b52e9b90e3cd4"), { "$each" : [ ObjectId("55b2932cb57f47c0be6f072f"), ObjectId("55b2932cb57f47c0be6f073f") ] } ], "_creator" : ObjectId("55b2932cb57f47c0be6f071f"), "_id" : ObjectId("55b54aa4e2aa83f1f123a1a2"), "_inactive" : [ ], "_messages" : [ "hi" ] }
Expected result
{ "__v" : 39, "_active" : [ ObjectId("55b2932cb57f47c0be6f071f"), ObjectId("55b28b203a6b52e9b90e3cd4"), ObjectId("55b2932cb57f47c0be6f072f"), ObjectId("55b2932cb57f47c0be6f073f") ], "_creator" : ObjectId("55b2932cb57f47c0be6f071f"), "_id" : ObjectId("55b54aa4e2aa83f1f123a1a2"), "_inactive" : [ ], "_messages" : [ "hi" ] }
The version of MongoDB here must be a very old version, and now confirmed as 2.0.4. This should even error in 2.2.x versions due to it interpretting the $each as a field and rejecting it due to the reserved $ in the field name.
Use at least MongoDB 2.4 if you intend to use $each, otherwise there is $pushAll, which is now considered deprecated. The only difference being between 2.4 and 2.6 is that earlier than 2.6 you need to combine this with the $sort modifer as well.

mongodb Cannot apply $pull/$pullAll modifier to non-array, How to remove array element

i met a problem about mongodb.
db.tt.find()
{ "_id" : ObjectId("513c971be4b1f9d71bc8c769"),
"name" : "a",
"comments" : [ { "name" : "2" }, { "name" : "3" } ]
}
above is a test document.
i want to pull comments.name = 2
i do
db.tt.update({'comments.name':'2'},{'$pull':{'comments.$.name':'2'}});
but the console print these message:
Cannot apply $pull/$pullAll modifier to non-array
my mongodb version is 2.0.6
who can help me? Thank you very much
Your $pull syntax is off, it should be:
db.tt.update({'comments.name': '2'}, {$pull: {comments: {name: '2'}}})