MGO Query nested array of objects - mongodb

I
am having difficulty converting a MongoDB query to mgo bson. The Mongo record schema is as shown below. I want to find records that have topics with label "Education" and "Students".
db.questions.insert
(
{
"_id" : ObjectId("5cb4048478163fa3c9726fdf"),
"questionText" : "why?",
"createdOn" : new Date(),
"createdBy": user1,
"topics" : [
{
"label": "Education",
},
{
"label": "Life and Living",
},
{
"label": "Students"
}
]
}
)
Using Robo 3T, the query looks like this:
db.questions.find({$and : [
{"topics": {"label": "Students"}},
{"topics": {"label": "Education"}}
]})
I am having trouble modeling this with MGO. Currently, have tried this:
map[$and:[
map[topics:map[label:students]]
map[topics:map[label:life and living]]
]]
and this
map[topics:map[$and:[
map[label:students]
map[label:life and living]
]]]

If you want to find some value from nested array then you use $elemMatch method.
db.questions.find(
{$and:
[
{topics: {$elemMatch: {label: 'Students'}}},
{topics: {$elemMatch: {label: 'Education'}}}
]
}
)

The bson model for the above answer is as follows:
query = getAndFilters(
bson.M{"topics": bson.M{"$elemMatch": bson.M{"label": "Students"}}},
bson.M{"topics": bson.M{"$elemMatch": bson.M{"label": "Education"}}})

Related

MongoDB Project - return data only if $elemMatch Exist

Hello Good Developers,
I am facing a situation in MongoDB where I've JSON Data like this
[{
"id": "GLOBAL_EDUCATION",
"general_name": "GLOBAL_EDUCATION",
"display_name": "GLOBAL_EDUCATION",
"profile_section_id": 0,
"translated": [
{
"con_lang": "US-EN",
"country_code": "US",
"language_code": "EN",
"text": "What is the highest level of education you have completed?",
"hint": null
},
{
"con_lang": "US-ES",
"country_code": "US",
"language_code": "ES",
"text": "\u00bfCu\u00e1l es su nivel de educaci\u00f3n?",
"hint": null
}...
{
....
}
]
I am projecting result using the following query :
db.collection.find({
},
{
_id: 0,
id: 1,
general_name: 1,
translated: {
$elemMatch: {
con_lang: "US-EN"
}
}
})
here's a fiddle for the same: https://mongoplayground.net/p/I99ZXBfXIut
I want those records who don't match $elemMatch don't get returned at all.
In the fiddle output, you can see that the second item doesn't have translated attribute, In this case, I don't want the second Item at all to be returned.
I am using Laravel as Backend Tech, I can filter out those records using PHP, but there are lots of records returned, and I think filtering using PHP is not the best option.
You need to use $elemMatch in the first parameter
db.collection.find({
translated: {
$elemMatch: {
con_lang: "IT-EN"
}
}
})
MongoPlayground

PyMongo(3.6.1) - db.collection.aggregate(pipeline) NOT WORKING

I'm having trouble with PyMongo, I have been googling for hours, but found no solution.
I'm working with some Python Scripts just to practice with MongoDb, which is running on my local machine. I have populated my mongoDb instance with one database "moviesDB", which contains 3 different collections:
1.Movies collection, here is an example of a document from this coll:
{'_id': 1,
'title': 'Toy Story (1995)',
'genres': ['Adventure', 'Animation', 'Children', 'Comedy', 'Fantasy'],
'averageRating': 3.87,
'numberOfRatings': 247,
'tags': [
{'_id': ObjectId('5b04970b87977f1fec0fb6e9'),
'userId': 501,
'tag': 'Pixar',
'timestamp': 1292956344}
]
}
2.Ratings collection, which looks like this:
{ '_id':ObjectId('5b04970c87977f1fec0fb923'),
'userId': 1,
'movieId': 31,
'rating': 2.5,
'timestamp': 1260759144}
3.Tags collection, that I won't use here, so it's not important.
Now, what I'm trying to do is: given a user (in this example, user 1), find all the genres of movies that he rated and per each genre list all the movieIds regarding that genre.
Here's my code:
"""
This query basically retrieves movieIds,
so from the result list of several documents like this:
{
ObjectId('5b04970c87977f1fec0fb923'),
'userId': 1,
'movieId': 31,
'rating': 2.5,
'timestamp': 1260759144},
retrieves only an array of integers, where each number represent a movie
that the user 1 rated."""
movies_rated_by_user = list(db.ratings.distinct(movieId, {userId: 1}))
pipeline = [
{"$match": {"_id ": {"$in": movies_rated_by_user}}},
{"$unwind": "$genres"},
{"$group": {"_id": "$genres", "movies": {"$addToSet": "$_id"}}}]
try:
"""HERE IS THE PROBLEM, SINCE db.movies.aggregate() RETURNS NOTHING!
so the cursor is empty."""
cursor = db.movies.aggregate(pipeline, cursor={})
except OperationFailure:
print("Something went Wrong", file=open("operations_log.txt", "a"))
print(OperationFailure.details, file=open("operations_log.txt", "a"))
sys.exit(1)
aggregate_genre = []
for c in cursor:
aggregate_genre.append(c)
print(aggregate_genre)
The point is that the aggregate function on the movies collection retrieves NOTHING, whereas it really should, since I tried this query on the MongoShell and it worked just fine. Here's how the mongoDB shell-query looks like:
db.movies.aggregate(
[
{$match:{_id : {$in: ids}}},
{$unwind : "$genres"},
{$group :
{
_id : "$genres",
movies: { $addToSet : "$_id" }}}
]
);
Where the 'ids' variables is defined like this, just like the movies_rated_by_user variable in the code:
ids= db.ratings.distinct("movieId", {userId : 1});
The result from the aggregate method looks like this (this is what the aggregate_genre variable in the code, should contain):
{ "_id" : "Western", "movies" : [ 3671 ] }
{ "_id" : "Crime", "movies" : [ 1953, 1405 ] }
{ "_id" : "Fantasy", "movies" : [ 2968, 2294, 2193, 1339 ] }
{ "_id" : "Comedy", "movies" : [ 3671, 2294, 2968, 2150, 1405 ] }
{ "_id" : "Sci-Fi", "movies" : [ 2455, 2968, 1129, 1371, 2105 ] }
{ "_id" : "Adventure", "movies" : [ 2193, 2150, 1405, 1287, 2105, 2294,
2968, 1371, 1129 ] }
Now the problem is the aggregate method, is there any error with the pipeline string??
PLEASE HELP!!
Thank you
I think you need to cast the cursor to a list before iterating through it. Hope this helps.
cursor = list(db.movies.aggregate(pipeline, cursor={}))

field within the object of array in Mongodb

The sample structure is as follows:
{
"_id": 1,
"College_name" : "abcdef"
"Students": [
{
"name": "a",
"Grade": "First"
},
{
"name": "b",
"Grade": "Second"
},
{
"name": "c",
"Grade": "First"
},
{
"name": "d",
"Grade": "First"
}
]
}
What I am not getting is - I want to get all ("Grade" : "First") objects in the array and insert only ("Grade" : "First") objects to other collection where ("College_name" : "abcdef"). What is the optimal solution for this?
Is this possible through aggregation??
Please help me out to solve this.
You can fetch the data with {"Grade": "First"} either using aggregation or mongo query -
Aggregation query -
db.coll.aggregate([{'$match':{"Students.Grade":"First"}}, {$project: {"_id":1, "College_name":1}}])
Mongo Query -
db.coll.find({"Students.Grade":"First"}, {"College_name":1})
After that you can insert the data to other collection.
Answer to this question is in the link:
query to retrieve multiple objects in an array in mongodb
i.e.,
Aggregation query will have 4 pipeline operators:
[match, unwind, match, group]
and then can be inserted to the other collection.

Mongodb: Trying to find all documents with specific subdocument field, why is my query not working?

Here is an example of a document from the collection I am querying
meteor:PRIMARY> db.research.findOne({_id: 'Z2zzA7dx6unkzKiSn'})
{
"_id" : "Z2zzA7dx6unkzKiSn",
"_userId" : "NtE3ANq2b2PbWSEqu",
"collaborators" : [
{
"userId" : "aTPzFad8DdFXxRrX4"
}
],
"name" : "new one",
"pending" : {
"collaborators" : [ ]
}
}
I want to find all documents within this collection with either _userId: 'aTPzFad8DdFXxRrX4' or from the collaborators array, userId: 'aTPzFad8DdFXxRrX4'
So I want to look though the collection and check if the _userId field is 'aTPzFad8DdFXxRrX4'. If not then check the collaborators array on the document and check if there is an object with userId: 'aTPzFad8DdFXxRrX4'.
Here is the query I am trying to use:
db.research.find({$or: [{_userId: 'aTPzFad8DdFXxRrX4'}, {collaborators: {$in: [{userId: 'aTPzFad8DdFXxRrX4'}]}}] })
It does not find the document and gives me a syntax error. What is my issue here? Thanks
The $in operator is basically a simplified version of $or but you really only have one argument here so you should not even need it. Use dot notation instead:
db.research.find({
'$or': [
{ '_userId': 'aTPzFad8DdFXxRrX4'},
{ 'collaborators.userId': 'aTPzFad8DdFXxRrX4'}
]
})
If you need more than one value then use $in:
db.research.find({
'$or': [
{ '_userId': 'aTPzFad8DdFXxRrX4'},
{ 'collaborators.userId': {
'$in': ['aTPzFad8DdFXxRrX4','aTPzFad8DdFXxRrX5']
}}
]
})

Updating embedded document property in Mongodb

I have a document that looks like this:
{
"_id": 3,
"Slug": "slug",
"Title": "title",
"Authors": [
{
"Slug": "slug",
"Name": "name"
}
]
}
I want to update all Authors.Name based on Authors.Slug.
I tried this but it didn't work:
.update({"Authors.Slug":"slug"}, {$set: {"Authors.Name":"zzz"}});
What am I doing wrong here?
.update(Authors:{$elemMatch:{Slug:"slug"}}, {$set: {'Authors.$.Name':"zzz"}});
You can use update with array filters:
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/#positional-update-arrayfilters
Probably something like this:
yourcollection.update(
{},
{
"$set": {
"Authors.$[element].Name": "zzz"
}
},
{
"multi": true,
"arrayFilters": [
{ "element.Slug": "slug" }
]
}
)
Ps.: it will not work in Robo3T as explained here: Mongodb 3.6.0-rc3 array filters not working?
However, you can try on a mongo shell with version >= 3.6.
yes, Rock's solution is working, P.S Notes is really helpful when trying Robo31..
If we want to update all
db.collection_name.update({}, {$set: {"Authors.$[].Name": "zzz"}})
If we want to update with matching object in an array
db.collection_name.update({}, {$set: {"Authors.$[i].Name": "zzz"}}, {arrayFilters: [{"i.Slug": "slug"}]})
Ref: https://jira.mongodb.org/browse/SERVER-1243