Query on array on a nested document with pymongodb - python-3.x

I am building a chat program
And in the backend, I have
chatcol.insert({ "chatid": "133235", "messages": [ {"from": "user3", "content": "Hello", "time": "20101213T172215"}, {"from": "user2", "content": "Hi", "time": "20101214T172215"} ] })
chatcol.insert({ "chatid": "134735", "messages": [ {"from": "user2", "content": "Hello", "time": "20101217T172215"}, {"from": "user12", "content": "Hi", "time": "20101213T172215"} ] })
Since there can be a lot of messages, I want the server only give the new message the client didnt see.
The client will give me a lastuptime, the time where they last logon.
I want to find chats with only the new message only.
How do I write such query?

I have considered time as ISODate()
Also, lastuptime given by the client wil be ISODate()
collection:
{
"_id" : ObjectId("5da31a63c35040d7fbf1db3c"),
"chatid" : "133235",
"messages" : [
{
"from" : "user3",
"content" : "Hello",
"time" : ISODate("2016-05-01T00:00:00Z")
},
{
"from" : "user2",
"content" : "Hi",
"time" : ISODate("2018-05-01T00:00:00Z")
}
]
}
{
"_id" : ObjectId("5da31ab2c35040d7fbf1db3d"),
"chatid" : "133235",
"messages" : [
{
"from" : "user2",
"content" : "Hello",
"time" : ISODate("2010-05-01T00:00:00Z")
},
{
"from" : "user12",
"content" : "Hi",
"time" : ISODate("2019-05-01T00:00:00Z")
}
]
}
There are two scenarios. Depending on your use-case, select the appropriate:
1. Fetch chats where any one of the messages has time >= lastuptime
db.chatcol.find({'messages.time': {"$gte": ISODate("2017-10-01T00:00:00.000Z")}})
Output:
{
"_id" : ObjectId("5da31a63c35040d7fbf1db3c"),
"chatid" : "133235",
"messages" : [
{
"from" : "user3",
"content" : "Hello",
"time" : ISODate("2016-05-01T00:00:00Z")
},
{
"from" : "user2",
"content" : "Hi",
"time" : ISODate("2018-05-01T00:00:00Z")
}
]
}
{
"_id" : ObjectId("5da31ab2c35040d7fbf1db3d"),
"chatid" : "133235",
"messages" : [
{
"from" : "user2",
"content" : "Hello",
"time" : ISODate("2010-05-01T00:00:00Z")
},
{
"from" : "user12",
"content" : "Hi",
"time" : ISODate("2019-05-01T00:00:00Z")
}
]
}
2. Fetch messages where time >= lastuptime
This one flattens the message array
db.chatcol.aggregate([
{ $unwind :'$messages'},
{ $match : {"messages.time": { "$gte": ISODate("2017-10-01T00:00:00.000Z") }}}
])
Output:
{
"_id" : ObjectId("5da31a63c35040d7fbf1db3c"),
"chatid" : "133235",
"messages" : {
"from" : "user2",
"content" : "Hi",
"time" : ISODate("2018-05-01T00:00:00Z")
}
}
{
"_id" : ObjectId("5da31ab2c35040d7fbf1db3d"),
"chatid" : "133235",
"messages" : {
"from" : "user12",
"content" : "Hi",
"time" : ISODate("2019-05-01T00:00:00Z")
}
}
Note: Map the $gte query according to your lastuptime

Related

findOne returning full document

This is the Data stored in MongoDB database under the collection name of padlos
{
"_id" : ObjectId("60ffb89473dc672be32909c2"),
"courses" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c3"),
"course" : "BCA",
"semesters" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c4"),
"sem" : 1,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c5"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c6"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c7"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909c8"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909c9"),
"subject" : "IC & IT",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ca"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cb"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909cc"),
"topic" : "DataTypes"
}
]
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909cd"),
"sem" : 2,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ce"),
"subject" : "Java",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cf"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d0"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d1"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909d2"),
"subject" : "SQL",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d3"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d4"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d5"),
"topic" : "DataTypes"
}
]
}
]
}
]
}
]
}
],
"__v" : 0
}
This is the code that i am using to query and fetch data
app.get('/',function(req, res)
{
PadhloSchema.findOne(
{"courses.course" : "BCA" , "courses.semesters.sem" : 1, "courses.semesters.subjects.subject" :"C++"},
function(err, courses){
if(!err)
{
res.send(courses);
}
else
{
res.send(err);
}
})
});
I only want to fetch data where the course is BCA ,the semester(sem) is 1 and the subject is C++ and the data in the response must look like this :
{
"_id" : ObjectId("60ff08a977ec48b84ec07b46"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ff08a977ec48b84ec07b47"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ff08a977ec48b84ec07b48"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ff08a977ec48b84ec07b49"),
"topic" : "DataTypes"
}
]
}
]
}
But rather i am all the data back in the response:
{
"_id" : ObjectId("60ffb89473dc672be32909c2"),
"courses" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c3"),
"course" : "BCA",
"semesters" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c4"),
"sem" : 1,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c5"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c6"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c7"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909c8"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909c9"),
"subject" : "IC & IT",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ca"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cb"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909cc"),
"topic" : "DataTypes"
}
]
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909cd"),
"sem" : 2,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ce"),
"subject" : "Java",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cf"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d0"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d1"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909d2"),
"subject" : "SQL",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d3"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d4"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d5"),
"topic" : "DataTypes"
}
]
}
]
}
]
}
]
}
],
"__v" : 0
}
Please help and tell me where i am doing wrong.I am new and noob in mongoDB.
I am using the latest MongoDB version 5.0.1
UPDATED ANSWER after Comment by Rahul Soni
app.get('/',function(req, res)
{
PadhloSchema.find(
{
courses : {course : "BCA",semesters : {sem : 1 ,subjects :{subject : "C++"}}}
},
function(err, courses){
if(!err)
{
res.send(courses);
}
else
{
res.send(err);
}
})
});
But now it is giving me null array
[]
MongoDB returns data at the document level. https://docs.mongodb.com/manual/tutorial/query-array-of-documents/
Basically, it is working as expected. You are searching for the document and you are getting precisely that. If you are looking for a subdocument, you will need to unwind it like so:
db.tmp.aggregate({$unwind:"$courses"},{$unwind:"$courses.semesters"},{$unwind:"$courses.semesters.subjects"},{$match:{"courses.course":"BCA", "courses.semesters.sem": 1, "courses.semesters.subjects.subject" :"C++"}}).pretty()
NB: I have simply added your document to a database and showing it at a the mongo level. Node.js would work similarly.
{
"_id" : ObjectId("60ffd533bccc96b9985944a9"),
"courses" : {
"_id" : ObjectId("60ffb89473dc672be32909c3"),
"course" : "BCA",
"semesters" : {
"_id" : ObjectId("60ffb89473dc672be32909c4"),
"sem" : 1,
"subjects" : {
"_id" : ObjectId("60ffb89473dc672be32909c5"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c6"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c7"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909c8"),
"topic" : "DataTypes"
}
]
}
]
}
}
}
}

Finding an object with multiple items in mongoose

I am having certain issue with querying mongodb using mongoose, but unable to find my required output. Below is the data I am looking for the solution.
I am trying to find with two params here one is location and second is type
{
"_id" : ObjectId("5f04dcf8e123292518a863ae"),
"location" : "Australia",
"name" : "June Bill",
"filters" : [
{
"_id" : ObjectId("5f047efe9fc7ad000f44f990"),
"name" : "Q1",
"type" : "Platinum",
"conditions" : [
{
"And" : [
{
"_id" : "objectid",
"field" : "location",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : "objectid",
"field" : "name",
"value" : "admin",
"operator" : "equal"
}
]
}
]
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6e"),
"name" : "Q2",
"type" : "Gold",
"conditions" : [
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6f"),
"field" : "ocation",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc70"),
"field" : "start_date",
"value" : "2020-06-01T00 3A00",
"operator" : "equal"
},
]
}
],
"__v" : 0
},
{
"_id" : ObjectId("5f04dcf8e123292518a863ae"),
"location" : "Brazil",
"name" : "June Bill",
"filters" : [
{
"_id" : ObjectId("5f047efe9fc7ad000f44f990"),
"name" : "Q1",
"type" : "Silver",
"conditions" : [
{
"And" : [
{
"_id" : "objectid",
"field" : "location",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : "objectid",
"field" : "name",
"value" : "admin",
"operator" : "equal"
}
]
}
]
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6e"),
"name" : "Q2",
"type" : "Gold",
"conditions" : [
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc6f"),
"field" : "location",
"value" : "Australia",
"operator" : "equal"
},
{
"_id" : ObjectId("5f04d51c0ce40120bbb7dc70"),
"field" : "start_date",
"value" : "2020-06-01T00 3A00",
"operator" : "equal"
},
]
}
],
"__v" : 0
}
here I am trying to find with location and in filters with its type
How Can i do this in mongoose?
I tried
Model.find({'location': 'Brazil', 'filters':{ "$in" : ["Silver"]} });
But it didn't work, Can any one help me achieving actual result?
You can use the . to query the embedded filed or if you want to match multiple fields you can use $elemMatch
db.collection.find({
"location": "Brazil",
"filters.type": {
"$in": [
"Silver"
]
}
},
{
"filters.$": 1
})
MongoDB Playground
If you want to filter out the matched result you can use $ operator in projection
Use this way:
db.collection.find({
"location": "Brazil",
"filters.type": "Silver"
})
MongoDb Playground

filter the embedded document by each parent _id?

I have document
{
"_id" : ObjectId("5da832caeb173112348e509b"),
"owner" : {
"image" : "5d999578aeb073247de4bd6e.jpg",
"fullname" : "hem sopheap",
"userID" : "5d999578aeb073247de4bd6e"
},
"project" : {},
"image" : "hem sopheap-1571304138866.png",
"body" : "Lorem Ipsum "),
"comments" : [
{
"user" : "5d999578aeb073247de4bd6e",
"fullname" : "hem sopheap",
"username" : "sopheap",
"comment" : "1000000",
"_id" : ObjectId("5db07900ae100b0c05b1222c"),
"replies" : [],
"date" : ISODate("2019-10-23T15:40:57.535Z"),
"likes" : [
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044"
]
},
{
"user" : "5d999578aeb073247de4bd6e",
"fullname" : "hem sopheap",
"username" : "sopheap",
"comment" : "11111111111",
"_id" : ObjectId("5db0790aae100b0c05b1222d"),
"replies" : [],
"date" : ISODate("2019-10-23T15:40:57.535Z"),
"likes" : []
}
],
"__v" : 33,
"likes" : [
"5d999578aeb073247de4bd6e"
]
}
How can I get likes, filter by post _id and comments _id to get result likes
"likes" : [
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044",
"5da85558886aee13e4e7f044"
]
Let's simplify the input to this. We have two posts, P0 and P2. Each has an array comments. We know that comment._id is at least unique with the post so it is OK to "reuse" them here:
var r =
[
{
"_id" : "P0",
"comments" : [
{ "_id" : "C0", "likes" : [ "AA", "AA" ] }
,{"_id" : "C1", "likes" : [] }
,{"_id" : "C2", "likes" : [ "foo", "bar" ]}
,{"_id" : "C3", "likes" : []
}
]
}
,{
"_id" : "P2",
"comments" : [
{ "_id" : "C0", "likes" : [ "FF", "FF" ] }
,{"_id" : "C1", "likes" : [] }
,{"_id" : "C2", "likes" : [ "foo", "bar" ]}
,{"_id" : "C3", "likes" : []
}
]
}
];
Here is a solution:
db.foo.aggregate([
// First, match on post ID and comments ID. Remember, comments is an
// array so ANY comments entry with key C0 inside the array will match and
// yield the entire array. But this is OK because it very much narrows down
// the info to process:
{$match: {_id: "P2", "comments._id":"C0"}}
,{$unwind: "$comments"} // Unwind the comments:
// And now pick only that comment with ID C0:
,{$match: {"comments._id":"C0"}}
// To complete the request, make "likes" a top level field:
,{$project: {"likes": "$comments.likes"}}
]);
A complete answer to the OP would include setup of the post and comment _id as ObjectId not strings but the query is the same.

How to lookup and filter in array of objects mongo?

I have a question about lookup and filter array of objects in mongodb
I have structure: Person
{
"_id": "5cc3366c22c3767a2b114c6b",
"flags": [
"5cc30210fada5d7820d03aaf",
"5cc2c3924a94a575adbdc56a"
],
"key": "Animal",
"name": "name1",
"description": "description1",
"endpoints": [
{
"isEnabled": true,
"publishUrl": "megaUrl",
"env": "5cc1a8911b19026fd193506b"
},
{
"isEnabled": true,
"publishUrl": "megaUrl",
"env": "5ccaeef3312acb103730d4c5"
}
]
}
envs collection
{
"_id" : "5cc1a8911b19026fd193506b",
"name" : "name2",
"key" : "PROD",
"publishUrl" : "url1",
"__v" : 0
}
{
"_id" : "5ccaeef3312acb103730d4c5",
"name" : "name2",
"key" : "PROD",
"publishUrl" : "url1",
"__v" : 0
}
I should filter Document by endpoints.$.env
so, I have: accessKeys = ["PROD", "UAY"], and i should see result . with endpoints where env.key === "PROD" || env.key === "UAT"
Expected result:
{
"_id": "5cc3366c22c3767a2b114c6b",
"flags": [
"5cc30210fada5d7820d03aaf",
"5cc2c3924a94a575adbdc56a"
],
"key": "Animal",
"name": "name1",
"description": "description1",
"endpoints": [
{
"isEnabled": true,
"publishUrl": "megaUrl",
"env": {
"_id" : "5cc1a8911b19026fd193506b",
"name" : "name2",
"key" : "PROD",
"publishUrl" : "url1",
"__v" : 0
}
},
]
}
Help me pls, how i can do that? I know about aggregate, but cant do it :(
Try this :
db.persons.aggregate([{
$unwind : "$endpoints"
},{
$lookup :{
from : "envs",
localField : "endpoints.env",
foreignField : "_id",
as : "endpoints.env"
}
},{
$unwind : "$endpoints.env"
},{
$match : {
"endpoints.env.key" : {$in : accessKeys}
}
},{
$group : {
_id : "$_id",
flags : {$first : "$flags"},
key : {$first : "$key"},
name : {$first : "$name"},
description : {$first : "$description"},
endpoints : {$push : "$endpoints"},
}
}])

How to use $filter(aggregation) to select some fields of array only if condition true?

Here I'll show you what exactly I want. Suppose I have the below two document for XYZ model.
[
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"email" : "one#one.com",
"mobileNumber" : "910123456989",
"verificationStatus" : true,
"activities" : [
{
"name" : "a1",
"_id" : ObjectId("59ef8786e8c7d60552139bae"),
"type" : 0,
"level" : null,
"verificationStatus" : true
},
{
"name" : "a2",
"_id" : ObjectId("59ef8786e8c7d60552139bad"),
"type" : 0,
"level" : null,
"verificationStatus" : false
}
],
"address" : {
"line1" : "asd",
"line2" : "asd",
"city" : "sd",
"state" : "sd",
"country" : "asd",
"landmark" : "sdsa",
"pincode" : "560090"
},
"__v" : 0
},
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"email" : "one#one.com",
"mobileNumber" : "919876543210",
"verificationStatus" : true,
"activities" : [
{
"name" : "b1",
"_id" : ObjectId("59ef8786e8c7d60552139bae"),
"level" : null,
"type" : 0,
"verificationStatus" : true
},
{
"name" : "b2",
"_id" : ObjectId("59ef8786e8c7d60552139bad"),
"level" : null,
"type" : 0,
"verificationStatus" : false
}
],
"address" : {
"line1" : "asd",
"line2" : "asd",
"city" : "sd",
"state" : "sd",
"country" : "asd",
"landmark" : "sdsa",
"pincode" : "560090"
},
"__v" : 0
}
]
Now I want only the name, mobileNumber and activities.name from the document where verificationStatus is true and I don't want all the activities I want activities.name only if activities.varificationStatus is true.
I can get the list of all document where varificationStatus is true and activities.varificationStatus is true but I'm not able to select only required fields (activities.name) from activities.
My current code is:
XYZ.aggregate(
[
{ $match: { verificationStatus: true } },
{
$project: {
name: 1,
coverImage: 1,
location: 1,
address: 1,
dist: 1,
activities: {
$filter: {
input: "$activities",
as: "activity",
cond: {
$eq: ["$$activity.verificationStatus", true]
}
}
}
}
}], function (err, list) {
if (err) {
reject(err);
}
else {
resolve(list);
}
});
You actually need $map to "alter" the array elements returned, as $filter only "selects" the array elements that "match":
XYZ.aggregate(
[
{ $match: { verificationStatus: true } },
{
$project: {
name: 1,
mobileNumber: 1,
activities: {
$map: {
input: {
$filter: {
input: "$activities",
as: "activity",
cond: "$$activity.verificationStatus"
}
},
"as": "a",
"in": "$$a.name"
}
}
}
}], function (err, list) {
...
Would return:
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "910123456989",
"activities" : ["a1"]
}
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "919876543210",
"activities" : ["b1"]
}
Note also that the "cond" in $filter can be shortened since it's already a boolean value.
If you wanted the "object" with the property of "name" only, then return just that assigned key:
XYZ.aggregate(
[
{ $match: { verificationStatus: true } },
{
$project: {
name: 1,
mobileNumber: 1,
activities: {
$map: {
input: {
$filter: {
input: "$activities",
as: "activity",
cond: "$$activity.verificationStatus"
}
},
"as": "a",
"in": {
"name": "$$a.name"
}
}
}
}
}], function (err, list) {
...
Returns as:
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "910123456989",
"activities" : [{ "name": "a1" }]
}
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "919876543210",
"activities" : [{ "name": "b1" }]
}
If you knew for certain that you were matching "one" element in the array, then $indexOfArray with $arrayElemAt could be used instead if you have MongoDB 3.4
{ "$project": {
"name": 1,
"mobileNumber": 1,
"activities": {
"$arrayElemAt": [
"$activities.name",
{ "$indexOfArray": [ "$activities.verificationStatus", true ] }
]
}
}}
Which would come out a little differently since it's a singular value and not an array:
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "910123456989",
"activities" : "a1"
}
{
"_id" : ObjectId("59ef8786e8c7d60552139ba9"),
"name" : "s1",
"mobileNumber" : "919876543210",
"activities" : "b1"
}

Resources