How to query embedded array of objects based on conditions in mongodb - mongodb

I have an array of objects embedded in a document and there are multiple such documents in an collection.
How to do I query those embedded array of objects with below conditions(based on the documents I have below).
First get objects whose "status" is "active"(status will not be in all the objects but only few)
Then get the "parent_user_id" of the above satisfied object and match it with the rest of the objects "parent_user_id" and get those objects
the result of the above conditions have to set instead of the original Array (i.e: "users") of objects in the output instead of all the objects present.
So if you take a look at the result am expecting there are 3 elements missing from the user array because those elements did not satisfy the above conditions.
Document I have in collection(there will be multiple document as such)
{
"_id" : ObjectId("63a8808652f40e1d48a3d1d7"),
"name" : "A",
"description" : null,
"users" : [
{
"id" : "63a8808c52f40e1d48a3d1da",
"owner" : "John Doe",
"purchase_date" : "2022-12-25,
"status" : "active",
"parent_user_id" : "63a8808c52f40e1d48a3d1da",
"recent_items": ["tomato",onion]
},
{
"id" : "63a880a552f40e1d48a3d1dc",
"owner" : "John Doe 1",
"purchase_date" : "2022-12-25,
"parent_user_id" : "63a8808c52f40e1d48a3d1da",
"recent_items": ["onion"]
},
{
"id" : "63a880f752f40e1d48assddd"
"owner" : "John Doe 2",
"purchase_date" : "2022-12-25,
"parent_user_id" : "63a8808c52f40e1d48a3d1da",
},
{
"id" : "63a880f752f40e1d48a3d207"
"owner" : "John Doe 11",
"dt" : "2022-12-25,
"status" : "inactive",
"parent_user_id" : "63a880f752f40e1d48a3d207",
},
{
"id" : "63a880f752f40e1d48agfmmb"
"owner" : "John Doe 112",
"dt" : "2022-12-25,
"status" : "active",
"parent_user_id" : "63a880f752f40e1d48agfmmb",
"recent_items": ["tomato"]
}
{
"id" : "63a880f752f40e1d48agggg"
"owner" : "John SS",
"dt" : "2022-12-25,
"status" : "inactive",
"parent_user_id" : "63a880f752f40e1d48agggg",
}
{
"id" : "63a880f752f40e1d487777"
"owner" : "John SS",
"dt" : "2022-12-25,
"parent_user_id" : "63a880f752f40e1d48agggg",
}
]
}
Result am expecting
{
"_id" : ObjectId("63a8808652f40e1d48a3d1d7"),
"name" : "A",
"description" : null,
"users" : [
{
"id" : "63a8808c52f40e1d48a3d1da",
"owner" : "John Doe",
"purchase_date" : "2022-12-25,
"status" : "active",
"parent_user_id" : "63a8808c52f40e1d48a3d1da",
"recent_items": ["tomato",onion]
},
{
"id" : "63a880a552f40e1d48a3d1dc",
"owner" : "John Doe 1",
"purchase_date" : "2022-12-25,
"parent_user_id" : "63a8808c52f40e1d48a3d1da",
},
{
"id" : "63a880f752f40e1d48assddd"
"owner" : "John Doe 2",
"purchase_date" : "2022-12-25,
"parent_user_id" : "63a8808c52f40e1d48a3d1da",
},
{
"id" : "63a880f752f40e1d48agfmmb"
"owner" : "John Doe 112",
"dt" : "2022-12-25,
"status" : "active",
"parent_user_id" : "63a880f752f40e1d48agfmmb",
"recent_items": ["tomato"]
}
]
}

i would use some $filter stages as follows :
db.collection.aggregate([
{
$addFields: {
users_matched: {
$filter: {
input: "$users",
as: "user",
cond: {
$eq: [
"active",
"$$user.status"
],
},
},
},
},
},
{
$set: {
users: {
$filter: {
input: "$users",
as: "user",
cond: {
$in: [
"$$user.parent_user_id",
"$users_matched.id"
],
},
},
},
},
},
{
$unset: "users_matched"
}
])
You can check for yourself on mongoplayground https://mongoplayground.net/p/SrpsWb4v21x
EDIT TO ANSWER THE SECOND QUESTION:
You could fix your tomato problem as follows :
db.collection.aggregate([
{
$addFields: {
active_users: {
$filter: {
input: "$users",
as: "user",
cond: {
$eq: [
"active",
"$$user.status"
],
},
},
},
tomato_users: {
$filter: {
input: "$users",
as: "user",
cond: {
$in: [
"tomato",
{
"$ifNull": [
"$$user.recent_items",
[]
]
}
],
},
},
}
},
},
{
$set: {
users: {
$filter: {
input: "$users",
as: "user",
cond: {
$and: [
{
$in: [
"$$user.parent_user_id",
"$active_users.id"
],
},
{
$in: [
"$$user.parent_user_id",
"$tomato_users.parent_user_id"
],
}
]
},
},
},
},
},
{
$unset: [
"active_users",
"tomato_users"
]
}
])
See on mongoplayground https://mongoplayground.net/p/mb21UT475yt

Related

How to join two table in mongodb with aggregate and lookup I have as array in second table?

I have two collections:
clients
{
"_id" : ObjectId("60d203145b7b6c19b00ba576"),
"isDeleted" : false,
"createdAt" : ISODate("2021-06-22T15:06:21.564Z"),
"status" : "live",
"customerGUID" : "C8B910A3F74E",
"apps" : [
{
"url" : "https://test.com",
"loginGUID" : "12324654",
"loginAPIAccessKey" : "B072-266C369743CA",
}
],
"avatar" : "",
"description" : "",
"firstName" : "firstname",
"lastName" : "lastname",
"email" : "company#test.com",
"companyName" : "Company name",
}
visitors
{
"_id" : ObjectId("60aed4e0d6e30865f060be3c"),
"lastName" : "CAIN",
"firstName" : "DAVID",
"loginAPIAccessKey" : "B072-266C369743CA",
"email" : "cainins#att.net",
"createdAt" : ISODate("2021-05-26T23:08:16.505Z"),
"activity" : []
}
I want all visitors which have active clients with isDeleted: false status. and the relationship between visitors and clients is visitors.loginAPIAccessKey and clients.apps.loginAPIAccessKey which is in an array of objects.
Can someone help me?
You have to make use of the $lookup stage with the pipeline option.
db.visitors.aggregate([
{
"$lookup": {
"from": "clients",
"let": {
"loginAPIAccessKey": "$loginAPIAccessKey"
},
"pipeline": [
{
"$unwind": "$apps"
},
{
"$match": {
"$expr": {
"$eq": [
"$apps.loginAPIAccessKey",
"$$loginAPIAccessKey"
],
},
"isDeleted": false,
},
},
],
"as": "matchedClients"
}
},
{
"$match": {
"matchedClients": {
"$ne": []
}
},
},
])

How to write following queries in MongoDB using aggregate?

Retrieve films with an actor living in "Spain",
Retrieve films with actor details.
Collections are:
db.actor.insert([{ "actorId" : "5", "firstName" : "Ritik", "lastName" : "Roshan", "address" : { "street" : "GM Road", "city" : "Guwahati", "state" : "Aasam", "country" : "India", "pincode" : "145145" }, "contactDetails" : { "email" : "Ritik.roshan#gmail.com", "phoneno" : "9874584" }, "age" : "52" }])
db.film.insert([{ "filmId" : "10","title" : "Doshti Ka Karishma", "releaseOfYear" : "2001", "category" : ["advanture","Romantic"],
"actor" : [{ "firstName" : "Ritik", "lastName" : "Roshan" },{ "firstName" : "Karishma", "lastName" : "Kapoor" }],
"director" : [{ "firstName" : "Satish", "lastName" : "Ambike" }],
"releaseDetails" : { "place" : "Rajasthan", "date" : ISODate("2001-05-18T15:14:08.023Z"), "rating" : "C"}}])
You can use $lookup to join two collection
db.film.aggregate([
{ $match: { "releaseDetails.place": "Rajasthan" } },
{
"$lookup": {
"from": "actor",
"let": {
actor: "$actor"
},
"pipeline": [
{
$match: {
$expr: {
$and: [
{ $in: [ "$firstName", "$$actor.firstName" ] },
{ $in: [ "$lastName", "$$actor.lastName" ] }
]
}
}
}
],
"as": "joinActors"
}
}
])
Working Mongo playground
Note : You are saving firstname and lastname as reference in film collection. But combination of firstname and lastname are not always unique, Better you save _id of actor into the film collection's actor section.
something like this Save ref id

How to select collection with id in array embed from other collection mongodb

I have two collection: photos and users. I want select list user with user_id in likes array embed from photos collection.
photos:
{
"_id" : ObjectId(""),
"title": "Title",
"likes" : [
{
"user_id" : ObjectId("")
},
{
"user_id" : ObjectId("")
}
],
}
users:
{
{
"_id" : ObjectId(""),
"name": "Name1",
"avatar": "Path1",
},
{
"_id" : ObjectId(""),
"name": "Name2",
"avatar": "Path2",
},
}
Output with paging:
{
{
"_id" : ObjectId(""),
"name": "Name1",
"avatar": "Path1",
},
{
"_id" : ObjectId(""),
"name": "Name2",
"avatar": "Path2",
},
}
I think this will help you...
db.getCollection('photos').aggregate([{ $lookup:
{
from: "users",
localField: "likes.user_id",
foreignField: "_id",
as: "likes"
}
}
])
The output is:
{
"_id" : ObjectId(""),
"title" : "Title",
"likes" : [
{
"_id" : ObjectId(""),
"name" : "Name1",
"avatar" : "Path1"
},
{
"_id" : ObjectId(""),
"name" : "Name2",
"avatar" : "Path2"
}
]
}

Querying, projecting and sorting multiple fields of documents inside a mongoDB collection

I have a simple mongoDB collection, named usersCollection, whose generale structure is this:
[
{
"username" : "john",
"sex" : "m",
"email" : "john#gmail.com",
"courses" : [
{
"title" : "medicine",
"grade": 25,
},
{
"title" : "art",
"grade": 29,
},
{
"title" : "history",
"grade": 21,
}
]
},
{
"username" : "jane",
"sex" : "f",
"email" : "jane#gmail.com",
"courses" : [
{
"title" : "math",
"grade": 20,
},
{
"title" : "medicine",
"grade": 30,
}
]
},
{
"username" : "sarah",
"sex" : "f",
"email" : "sarah#gmail.com",
"courses" : [ ]
},
{
"username" : "josh",
"sex" : "f",
"email" : "josh#gmail.com",
"courses" : [
{
"title" : "english",
"grade": 28,
}
]
},
{
"username" : "mark",
"sex" : "m",
"email" : "mark#gmail.com",
"courses" : [
{
"title" : "history",
"grade": 30,
},
{
"title" : "medicine",
"grade": 19,
},
{
"title" : "math",
"grade": 22,
}
]
}
]
Every user is a member of usersCollection and has some general informations and a list of courses that he/she has already completed with the relative grades.
Can anyone tell me how I can query usersCollection to get a descending sorted array that contains an object for all users that have already completed a specific course? Every object of this array should contains the "name", the "email" and the "grade" of the relative user.
For example, launching a query on usersCollection around the course with name "medicine", I would obtain this array:
[
{
"username": "jane",
"email": "jane#gmail.com",
"grade": 30
},
{
"username": "john",
"email": "john#gmail.com",
"grade": 25
},
{
"username": "mark",
"email": "mark#gmail.com",
"grade": 19
}
]
You can manage to do it using sort and group pipeline of mongoDB.
if you want to get any specific courses you can apply match pipeline before sort pipeline.
db.users.aggregate([
{
$unwind: "$courses"
},
{
$match: {
'courses.title': 'medicine'
}
},
{
$sort: {
'courses.title': -1,
'courses.grade': -1,
}
},
{
$group: {
_id: "$courses.title",
records: {
$push: {
"_id" : "$_id",
"username" : "$username",
"sex" : "$sex",
"email" : "$email",
"grade":"$courses.grade"
}
}
}
}
])

How to create view to read from two collections in mongoDB?

Started with mongoDB syntax and use in project.
I am looking for a solution where I can combine more than two collections with couple of condition to create a view.
Here is my collection Range
/* 1 */
{
"_id" : ObjectId("1"),
"range" : {
"start" : "00"
},
"products" : [
{
"id" : "01",
"name" : "FirstProduct",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
},
{
"id" : "03",
"name" : "Third Product",
"type" : "Third Type"
},
]
}
/* 2 */
{
"_id" : ObjectId("2"),
"range" : {
"start" : "100",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
}
]
}
/* 3 */
{
"_id" : ObjectId("3"),
"range" : {
"start" : "500",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
}
]
}
Second Collection. Stock
/* 1 */
{
"_id" : ObjectId("1"),
"range" : {
"start" : "00"
},
"products" : [
{
"id" : "01",
"expired" : false,
"returned" : false
},
{
"id" : "02",
"expired" : false,
"returned" : false
}
]
}
/* 2 */
{
"_id" : ObjectId("02"),
"range" : {
"start" : "100"
},
"products" : [
{
"id" : "01",
"expired" : true,
"returned" : true
},
{
"id" : "02",
"expired" : true,
"returned" : true
}
{
"id" : "03",
"expired" : true,
"returned" : true
}
]
}
Now want to have a view with combine result from above two collection above.
For each range document in Range collections
if Range.range.start = Stock.range.start
if Range.products.id = Stock.products.id
copy "expired" and "returned" field from Stock for that product and
add to Range.product
end if
end if
Return Range
So final result will something like below.
/* 1 */
{
"_id" : ObjectId("1"),
"range" : {
"start" : "00"
},
"products" : [
{
"id" : "01",
"name" : "FirstProduct",
"type" : "First Type"
"expired" : false,
"returned" : false
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
"expired" : false,
"returned" : false
}
]
}
/* 2 */
{
"_id" : ObjectId("2"),
"range" : {
"start" : "100",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type",
"expired" : true,
"returned" : true
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type",
"expired" : true,
"returned" : true
}
]
}
/* 3 */
{
"_id" : ObjectId("3"),
"range" : {
"start" : "500",
},
"products" : [
{
"id" : "01",
"name" : "First Product",
"type" : "First Type"
},
{
"id" : "02",
"name" : "Second Product",
"type" : "Second Type"
}
]
}
I started with aggregate pipeline stages with fail to get right queries.
if anyone can help with right syntax and proper aggregate function.
Thanks in advance.
You need $lookup to merge the data from both collections but then you have to use $unwind to be able to match corresponding documents by product.id. In the last step you can use $group to get back an array:
db.Range.aggregate([
{
$lookup: {
from: "Stock",
localField: "range.start",
foreignField: "range.start",
as: "stock"
}
},
{
$unwind: "$stock"
},
{
$unwind: "$products"
},
{
$unwind: "$stock.products"
},
{
$match: { $expr: { $eq: [ "$products.id", "$stock.products.id" ] } }
},
{
$group: {
_id: "$_id",
"range": { $first: "$range" },
products: {
$push: {
id: "$products.id",
name: "$products.name",
type: "$products.type",
expired: "$stock.products.expired",
returned: "$stock.products.returned"
}
}
}
}
])
EDIT: Alternative solution which operates directly on arrays using $map and $filter below. The drawback is that the code is less readable but the good part is that it should return documents when there's no match and you should get better performance using this approach
db.Range.aggregate([
{
$lookup: {
from: "Stock",
localField: "range.start",
foreignField: "range.start",
as: "stock"
}
},
{
$unwind: "$stock"
},
{
$addFields: {
products: {
$map: {
input: "$products",
as: "p",
in: {
$let: {
vars: {
stockItem: {
$arrayElemAt: [
{ $filter: { input: "$stock.products", cond: { $eq: [ "$$p.id", "$$this.id" ] } } }, 0
]
}
},
in: {
$cond: [
{ $eq: [ "$$stockItem", undefined ] },
"$$p",
{
id: "$$p.id",
name: "$$p.name",
type: "$$p.type",
expired: "$$stockItem.expired",
returned: "$$stockItem.returned",
}
]
}
}
}
}
}
}
},
{
$project: {
stock: 0
}
}
])