Updating in mongodb - mongodb

There is a collection in mongoDB:
info = {
"name":"me",
"note":[
{"date":"Dec.01",
"item":"01",
},
{"date":"Dec.02",
"item":"02",
}
]
}
Using pymongo, how do I push {"date":"Dec.03","item":"03"} to the key "note" directly?

Just perform a regular update operation using pymongo, and use the $push operator provided by MongoDB itself. So something like this:
collection.update({"name":"me"},{$push:{"note":{"date":"Dec.03","item":"03"}}});
Here's some documentation on the $push operator: http://docs.mongodb.org/manual/reference/operator/update/push/
Also, note how the $push operator is actually part of MongoDB itself, so most packages that use Mongo, like pymongo in your case, will support this operator.

You may use the $addToSet operator (see documentation).

Related

MongoDB conditional query on nested document array

Hi I'm trying to write a conditional query on nested document array.
I've read the document for days and couldn't figure out how to make this work.
DB looks like below :
[
{
"id":1,
"team":"team1",
"players":[
{
"name":"Mario",
"substitutes":[
"Luigi",
"Yoshi"
]
},
{
"name":"Wario",
"substitutes":[
]
}
]
},
{
"id":2,
"team":"team2",
"players":[
{
"name":"Bowser",
"substitutes":[
"Toad",
"Mario"
]
},
{
"name":"Wario",
"substitutes":[
]
}
]
}
]
Due to my lack of English, it's hard to put but what I'm trying to do is
to find teams that includes all queried players.
Each object in players array, some have substitutes.
For each objects in players array, if one of the queried players is not the main player("players.name"), then I want it to look for if one of substitutes("players.substitutes") is.
Team.find({players:{$in:[ 'Mario', 'Wario' ]}}) (mongoose query)
this will give me an array with 'team1'.
but what I want to get is both teams because 'Mario' is one of the substitutes for 'Bowser'(team2).
I failed to make a query but what I've been trying is not to use $where since the official MongoDB docs says :
AGGREGATION ALTERNATIVES PREFERRED
Starting in MongoDB 3.6, the $expr operator allows the use of
aggregation expressions within the query language. And, starting in
MongoDB 4.4, the $function and $accumulator allows users to define
custom aggregation expressions in JavaScript if the provided pipeline
operators cannot fulfill your application’s needs.
Given the available aggregation operators:
The use of $expr with aggregation operators that do not use JavaScript
(i.e. non-$function and non-$accumulator operators) is faster than
$where because it does not execute JavaScript and should be preferred
if possible. However, if you must create custom expressions, $function
is preferred over $where.
BUT if it could be easily written with $where operator then it's totally fine.
Any suggestions or ideas that lead to any further would be highly appreciated.
Firstly, your query is incorrect. And it is not very obvious what exactly is your filter criteria. So I am giving two suggestions:
If you want to filter all documents that have name defined in your matching criteria (which returns both documents):
db.Team.find({"players.name":{$in:[ 'Mario', 'Wario' ]}}).pretty()
If you want to filter all documents that have any provided player names in the substitutes array (which returns only one, because team1 doesn't have any substitutes are Mario/Wario)
db.Team.find({"players.substitutes":{$in:[ 'Mario', 'Wario' ]}}).pretty()
The names being looked at could be present in name or substitute
db.Team.find({ $or: [{"players.substitutes":{$in:[ 'Mario', 'Wario' ]}}, {"players.name":{$in:[ 'Mario', 'Wario' ]}}] }).pretty()

Mongo error: "$out stage requires a string argument, but found object (code: 14, codeName: TypeMismatch)"

I'm trying to write a Mongo aggregation using the out operator as described in the docs. This is the aggregation I'm writing:
db.mycollectionname.aggregate([
{ $match: {} },
{ $project: {}},
{ $out: {to: "projets", mode: "insertDocuments"}}
])
When I execute this I get the following error: $out stage requires a string argument, but found object - clear in and of itself but it goes against what the docs say. When I provide a string to the $out stage, I don't get the error but that's not what I want.
Mongo version: 3.6.9
(I have more logic under the $project pipeline stage which I removed for brevity, it doesn't have any impact).
Can someone help me understand why this differs from what the docs say? And how I can provide the arguments I want to pass to the out stage (an object containing "to" and "mode") as a string?
Many thanks,
Chris.
You should look at the version specific documentation:
https://docs.mongodb.com/v3.6/reference/operator/aggregation/out/
$out in MongoDB 3.6 and MongoDB 4.0 only require a single string. In MongoDB 4.2, $out can use a dictionary to set the mode.
I think the problem is with your MongoDb version, you are using 3.6.9 but the document says:
MongoDB 4.2 adds a new syntax structure that implements expanded functionality and flexibility around merging aggregation pipeline results into a target collection, including support for sharded collections and output modes that preserve the existing collection data.
Just update your version and it will work. :)

MongoDB aggregation $lookup to a field that is an indexed array

I am trying a fairly complex aggregate command on two collections involving $lookup pipeline. This normally works just fine on simple aggregation as long as index is set on foreignField.
But my $lookup is more complex as the indexed field is not just a normal Int64 field but actually an array of Int64. When doing a simple find(), it is easy to verify using explain() that the index is being used. But explaining the aggregate pipeline does not explain whether index is being used in the $lookup pipeline. All my timing tests seem to indicate that the index is not being used. MongoDB version is 3.6.2. Db compatibility is set to 3.6.
As I said earlier, I am not using simple foreignField lookup but the 3.6-specific pipeline + $match + $expr...
Could using pipeline be showstopper for the index? Does anyone have any deep experience with the new $lookup pipeline syntax and/or the index on an array field?
Examples
Either of the following works fine and if explained, shows that index on followers is being used.
db.col1.find({followers: {$eq : 823778}})
db.col1.find({followers: {$in : [823778]}})
But the following one does not seem to make use of the index on followers [there are more steps in the pipeline, stripped for readability].
db.col2.aggregate([
{$match:{field: "123"}},
{$lookup:{
from: "col1",
let : {follower : "$follower"},
pipeline: [{
$match: {
$expr: {
$or: [
{ $eq : ["$follower", "$$follower"] },
{ $in : ["$$follower", "$followers"]}
]
}
}
}],
as: "followers_all"
}
}])
This is a missing feature which is going to part of 3.8 version.
Currently eq matches in lookup sub pipeline are optimised to use indexes.
Refer jira fixed in 3.7.1 ( dev version).
Also, this may be relevant as well for non-multi key indexes.

Using maxTimeMS parameter with aggregation queries on Mongo 2.6 and Pymongo 2.7.1

I'm unable to use maxTimeMS parameter with Mongo 2.6 and Pymongo 2.7.1
As per the documentation on this page Official Mongodb Aggregation Page the aggregation method should return a Cursor object. However, when I run the query locally on a mongod instance (2.6+) with pymongo 2.7.1, I get a dict object!
In [14]: obj = coll.aggregate({'$group': {'_id': '$l', 'n': {'$sum':
1}}})
In [15]: type(obj) Out[15]: dict
Can anyone help me understand what is happening here?
Yes, you can use maxTimeMS with pymongo aggregation.
c.foo.bar.aggregate([], maxTimeMS=1000)
{u'ok': 1.0, u'result': []}
If you want a cursor:
for result in c.foo.bar.aggregate([], cursor={}, maxTimeMS=1000):
... print result
The aggregate command didn't support cursors before MongoDB 2.6 so it had to be added as an option to avoid breaking existing applications.
This is covered in the driver documentation where it is described that in order to return a cursor, you need to specify the arguments in addition to the pipeline in your .aggregate() method:
cursor = coll.aggregate([{'$group': { '_id': '$l', 'n': {'$sum': 1} }}],cursor={})
Note that the returned object here is a CommandCursor and not a cursor.
This is because various modifiers such as .limit() and .skip() and other options do not apply in the context of an aggregation result. As such $maxTimeMS is not a valid option for this type of cursor.
In addition, it would not do what you think it will even where valid. The reason being that the "cursor" execution is only counted "after" the "aggregation pipeline" execution is complete, so in this case, just fetching the results.
Look at the .currentOp() and .killOp() implementations for other ways to control long running aggregation tasks.

MongoDB Aggregation Framework: Why do I have to use unwind? Why isn't there a $count?

I'm a bit confused on MongoDB's aggregation framework.
In order to do distinct counts of a specific field, I have to use the $addToSet operator, then $unwind, then $group with a $sum
What I'm not getting is why Mongo doesn't have a $count operator to just count the length of the array (instead of using $unwind and doing all the other steps)
What I'm doing now is just counting the length of the array in my driver, but technically my driver is having to download all the extra data.
Is there any reason why there isn't a $count or a $countUnique operator? Or is there some way to do this already with Mongo that I'm just not aware of?
Upcoming 2.6 version adds a new operator $size which will give you the size of the array and can be used in $project stage after the $group with $addToSet to get the number you want.
References:
http://docs.mongodb.org/master/release-notes/2.6/#aggregation-enhancements
http://docs.mongodb.org/master/reference/operator/aggregation/size/#exp._S_size