MongoDB - $match based on another field value - mongodb

I want to get results like simple inner join from SQL, for get all accounts of the same id_customer.
I almost do this, but I get something like left join, and I don't now how to filer the result of query.
query:
db.customer.aggregate([
{
$match: {
"LOAN.AMOUNT": { $gte: 41000 }
}
},
{
$lookup: {
from: "department",
localField: "ID",
foreignField: "ACCOUNT.ID_CUSTOMER",
as: "customer"
}
},
{ $match: { "myArray": { $ne: [] } } },
{
$unwind: {
path: "$customer",
preserveNullAndEmptyArrays: false
}
}
,
{
$unwind: "$customer.ACCOUNT"
},
{
$match: {
"customer.ACCOUNT.ID_CUSTOMER": "$ID"
}
},
])
actual result:
empty
expected result:
{
"_id" : ObjectId("5dfccc28d29876c2988c7c05"),
"ID" : 4.0,
"SECOND_NAME" : "abc",
"FIRST_NAME" : "abc",
"ID_DISCOUNT" : {
"ID" : 3.0,
"TITLE" : "abc",
"AMOUNT" : 5.0
},
"LOAN" : {
"ID" : 4.0,
"AMOUNT" : 42750.0
},
"customer" : {
"_id" : ObjectId("5dfb7f8f5861584bbaedf718"),
"ID" : 1.0,
"TITLE" : "abc",
"ADDRESS" : "abc",
"CITY" : "abc",
"ACCOUNT" : {
"ID" : 10.0,
"ID_CUSTOMER" : 4.0,
"DATE_OPEN" : "2019-09-24 21:03:44"
}
}
}
{
"_id" : ObjectId("5dfccc28d29876c2988c7c05"),
"ID" : 4.0,
"SECOND_NAME" : "abc",
"FIRST_NAME" : "abc ",
"ID_DISCOUNT" : {
"ID" : 3.0,
"TITLE" : "abc",
"AMOUNT" : 5.0
},
"LOAN" : {
"ID" : 4.0,
"AMOUNT" : 42750.0
},
"customer" : {
"_id" : ObjectId("5dfb7f8f5861584bbaedf718"),
"ID" : 1.0,
"TITLE" : "abc",
"ADDRESS" : "abc",
"CITY" : "abc",
"ACCOUNT" : {
"ID" : 11.0,
"ID_CUSTOMER" : 4.0,
"DATE_OPEN" : "2019-08-23 21:03:44"
}
}
}
List of all account with the same id_customer, but
$match: {
"customer.ACCOUNT.ID_CUSTOMER": "$ID"
}
not doing what I want to do.

I found the solution: use $expr operator
db.customer.aggregate([
{
$match: {
"LOAN.AMOUNT": { $gte: 41000 }
}
},
{
$lookup: {
from: "department",
localField: "ID",
foreignField: "ACCOUNT.ID_CUSTOMER",
as: "customer"
}
},
{ $match: { "myArray": { $ne: [] } } },
{
$unwind: {
path: "$customer",
preserveNullAndEmptyArrays: false
}
}
,
{
$unwind: "$customer.ACCOUNT"
},
{ $match: { $expr: { $eq: ["$customer.ACCOUNT.ID_CUSTOMER", "$ID"] } } },
])

Related

Mongodb Aggregation get Data per user

Report table sample data
{
"_id" : ObjectId("614415f4a6566a001623b622"),
"record" : [
{
"dateTime" : ISODate("2021-09-17T04:13:39.465Z"),
"status" : "time-in",
"month" : 9,
"day" : 17,
"year" : 2021,
"time" : 1631852019465.0,
"date" : ISODate("2021-09-17T00:00:00.000Z"),
},
{
"dateTime" : ISODate("2021-09-17T04:14:01.182Z"),
"status" : "time-out",
"month" : 9,
"day" : 17,
"year" : 2021,
"time" : 1631852041182.0,
"date" : ISODate("2021-09-17T00:00:00.000Z"),
}
],
"uid" : ObjectId("614415b0a6566a001623b80b"),
"date" : ISODate("2021-09-17T00:00:00.000Z"),
"status" : "time-out",
"createdAt" : ISODate("2021-09-17T04:13:40.102Z"),
"updatedAt" : ISODate("2021-09-17T04:14:01.831Z"),
"__v" : 0
}
Users table sample data
{
"_id" : ObjectId("615c0f6db30aff375cd05ac1"),
"displayName" : "test test",
"firstName" : "test",
"lastName" : "test",
"email" : "test#gmail.com",
"brand" : "Jollibee",
"phone" : "+632312312312312",
"role" : 1,
"isVerified" : true,
"isArchived" : false,
"createdAt" : ISODate("2021-10-05T08:40:13.208Z"),
"updatedAt" : ISODate("2021-10-05T08:40:13.208Z"),
"__v" : 0
}
I have a data like this
db.getCollection('users').aggregate([
{
"$match": { brand: "Jollibee" }
},
{
$lookup: {
from: "orders",
let: { id: 'id' },
pipeline: [
{
$match: {
date: { $gte: ISODate("2020-11-01"), $lt: ISODate("2021-11-31") },
}
}
],
as: "orders",
},
},
{
$project: {
"_id": 1,
"name": 1,
"orders": 1
}
}
])
when I'm using this aggregation I'm getting all the data inserted per user.
What I want to happen is that. I will only get the data that belong to the user and not all the data of all users.
Added the sample documents for each collection
You are not comparing the userIds of both collections. You should add that on your $match. Playground
db.users.aggregate([
{
"$match": {
brand: "Jollibee"
}
},
{
$lookup: {
from: "orders",
let: {
id: "$_id"
},
pipeline: [
{
$match: {
date: {
$gte: ISODate("2020-11-01"),
$lt: ISODate("2021-11-30")
},
$expr: {
$eq: [
"$uid",
"$$id"
]
}
}
}
],
as: "orders",
},
},
{
$project: {
"_id": 1,
"name": 1,
"orders": 1
}
}
])

Unable to aggregate two collections using lookup in MongoDB Atlas

I have an orders collection that looks like this:
{
"_id" : "wJNEiSYwBd5ozGtLX",
"orderId" : 52713,
"createdAt" : ISODate("2020-01-31T04:34:13.790Z"),
"status" : "closed",
"orders" : [
{
"_id" : "ziPzwLuZrz9MNkaRT",
"productId" : 10290,
"quantity" : 2
}
]
}
I have an products collection that looks like this
{
"_id" : "238cwwLkZa6gKNN86",
"productId" : 10290,
"title" : "Product Title",
"price" : 9.9
}
I am trying to merge the price information into the orders information.
Something like:
{
"_id" : "wJNEiSYwBd5ozGtLX",
"orderId" : 52713,
"createdAt" : ISODate("2020-01-31T04:34:13.790Z"),
"status" : "closed",
"orders" : [
{
"_id" : "ziPzwLuZrz9MNkaRT",
"productId" : 10290,
"quantity" : 2,
"price": 9.9
}
]
}
If I try a $lookup command on MongoDB Atlas Dashboard like this:
{
from: 'products',
localField: 'orders.productId',
foreignField: 'productId',
as: 'priceInfo'
}
The aggregated output is (not what I wanted):
{
"_id" : "wJNEiSYwBd5ozGtLX",
"orderId" : 52713,
"createdAt" : ISODate("2020-01-31T04:34:13.790Z"),
"status" : "closed",
"orders" : [
{
"_id" : "ziPzwLuZrz9MNkaRT",
"productId" : 10290,
}
],
"priceInfo": [
{
"_id" : "238cwwLkZa6gKNN86",
"productId" : 10290,
"title" : "Product Title",
"price" : 9.9
}
]
}
I do not need a separate priceInfo array. It will be best if I have the product details information merged into the "orders" array. What should be the aggregation lookup syntax to achieve the desired output?
Demo - https://mongoplayground.net/p/bLqcN7tauWU
Read - $lookup $unwind $first $set $push $group
db.orders.aggregate([
{ $unwind: "$orders" }, // break array of orders into individual documents
{
$lookup: { // join
"from": "products",
"localField": "orders.productId",
"foreignField": "productId",
"as": "products"
}
},
{
$set: {
"orders.price": { "$arrayElemAt": [ "$products.price", 0 ] } // set the price
}
},
{
$group: { // group records back
_id: "$_id",
createdAt: { $first: "$createdAt" },
status: { $first: "$status" },
orderId: { $first: "$orderId" },
orders: { $push: "$orders" }
}
}
])

How to $lookup in nested array of objects

I have to two collections one is tours and other is destinations so in tours have i have an array of locations which has destination object with an id and that id is belongs to another destinations collection but the thing is i am not be able to lookup details of destination in the array of locations. every tried many query search here too. but not getting expected result.
Tours :
{
"_id" : ObjectId("5f3122f4d8d57e3b9650e5b4"),
"title" : "tour 1",
"locations" : [
{
"destination" : {
"id" : "5ec5ae9037ea99f20a79071a"
},
"services" : {
"hotel" : true
}
},
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8c"
},
"services" : {
"hotel" : true
}
}
]
}
{
"_id" : ObjectId("5f2d65e68bc6e9155310d147"),
"title" : "tour 2",
"locations" : [
{
"destination" : {
"id" : "5ecf994435c3a6025d5bf126"
},
"services" : {
"hotel" : true
}
}
]
}
{
"_id" : ObjectId("5f2d66398bc6e9155310d161"),
"title" : "tour 3",
"locations" : [
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8d"
},
"services" : {
"hotel" : true
}
}
]
}
Destinations :
{
"_id" : ObjectId("5ec5ae9037ea99f20a79071a"),
"name" : "dest 1",
"country" : "country name"
}
{
"_id" : ObjectId("5ec5ae8e37ea99f20a78ef8c"),
"name" : "dest 2",
"country" : "country name"
}
{
"_id" : ObjectId("5ec5ae8e37ea99f20a78ef8d"),
"name" : "dest 3",
"country" : "country name"
}
{
"_id" : ObjectId("5ecf994435c3a6025d5bf126"),
"name" : "dest 4",
"country" : "country name"
}
Expected result :
{
"_id" : ObjectId("5f3122f4d8d57e3b9650e5b4"),
"title" : "tour 1",
"locations" : [
{
"destination" : {
"id" : "5ec5ae9037ea99f20a79071a",
"name" : "dest 1",
"country" : "country name"
},
"services" : {
"hotel" : true
}
},
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8c",
"name" : "dest 2",
"country" : "country name"
},
"services" : {
"hotel" : true
}
}
]
},
{
"_id" : ObjectId("5f2d65e68bc6e9155310d147"),
"title" : "tour 2",
"locations" : [
{
"destination" : {
"id" : "5ecf994435c3a6025d5bf126",
"name" : "dest 4",
"country" : "country name"
},
"services" : {
"hotel" : true
}
}
]
},
{
"_id" : ObjectId("5f2d66398bc6e9155310d161"),
"title" : "tour 3",
"locations" : [
{
"destination" : {
"id" : "5ec5ae8e37ea99f20a78ef8d",
"name" : "dest 3",
"country" : "country name"
},
"services" : {
"hotel" : true
}
}
]
}
Tried query :
db.tours.aggregate([
{
"$addFields": {
"locations": {
"$map": {
"input": "$locations",
"in": {
"$mergeObjects": [
"$$this",
{
"dest_oid": {
"$toObjectId": "$$this.destination.id"
}
}
]
}
}
}
}
},
{ "$unwind": "$locations" },
{ "$lookup": {
"from": "destinations",
"localField": "locations.dest_oid",
"foreignField": "_id",
"as": "locations.dest",
}},
{ "$unwind": "$locations.dest" },
{ "$group": {
"_id": "$_id",
"locations": { "$push": "$locations" }
}}
])
even i have tried this
MongoDB $lookup on nested document
Quick fixes,
$unwind locations array put first because need to convert id to object id
db.tours.aggregate([
{ $unwind: "$locations" },
you skip this part if you have already converted string id t object id
$addFields replace locations.destination.id to object id filtered your logic to short, here no need $map and $mergeObjects options
{
$addFields: {
"locations.destination.id": {
$toObjectId: "$locations.destination.id"
}
}
},
$lookup that you have already did, but change as locations.destination
{
$lookup: {
from: "destinations",
as: "locations.destination",
localField: "locations.destination.id",
foreignField: "_id"
}
},
$unwind locations.destination because its array and we need object
{
$unwind: {
path: "$locations.destination"
}
},
$group that you have already did, few changes, push first destination and services in locations and add first title
{
$group: {
_id: "$_id",
locations: { $push: "$locations" },
title: { $first: "$title" }
}
}
])
Playground: https://mongoplayground.net/p/yaTCij7NRUj
If you can convert/update the string destination.id in the Tours collection to ObjectId if they are not already. Following query should work.
Query:
db.Tours.aggregate([
{
$unwind: "$locations",
},
{
$lookup: {
from: "Destinations",
localField: "locations.destination.id",
foreignField: "_id",
as: "destination",
},
},
{
$project: {
title: "$title",
locations: {
destination: {
$arrayElemAt: ["$destination", 0],
},
services: "$locations.services",
},
},
},
{
$group: {
_id: "$_id",
title: {
$first: "$title",
},
locations: {
$push: "$locations",
},
},
},
]);
Playground Link

MongoDB lookup when foreign field is an array of ids

I have two collections, fruit and salesman . I want my query to return all fruit with comma separated salesman.
Salesman document have array of fruit id
fruit document have id, name ,........
salesman table have id,name, fruits[apple_id,mango_id.......],...
db.getCollection('fruit').aggregate([{ "$unwind": "$fruits" }, { "$lookup": {
"from": "salesman",
"localField": "fruits",
"foreignField": "_id",
"as": "fruitObjects"
}},
{ "$unwind": "$fruitObjects" } ])
even this query is not giving result of $fruitObjects..?
Fruit Document
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple"
}
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava"
}
Salesman document
{
"_id" : ObjectId("5b101caddcab7850a4ba3257"),
"name" : "xyz",
"fruits":["5b101caddcab7850a4ba32ec","5b101caddcab7850a4ba32de","5b101caddcab7850a4ba32fe"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3258"),
"name" : "abc",
"fruits":["5b101caddcab7850a4ba32eb","5b101caddcab7850a4ba32de"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3259"),
"name" : "def",
"fruits":["5b101caddcab7850a4ba32ec"]
}
{
"_id" : ObjectId("5b101caddcab7850a4ba3260"),
"name" : "zxc",
"fruits":["5b101caddcab7850a4ba32ec","5b101caddcab7850a4ba32de","5b101caddcab7850a4ba32eb"]
}
``````````````````````````
Yes #barrypicker is correct - types of fields didn't match which is resulting in empty on salesman, Please try to store both of same type. Meanwhile you can actually convert one to other type - while querying each time, Also you need not to do $unwind, Please try below query :
Query 1:
db.fruit.aggregate([
{
$lookup:
{
from: "salesman",
let: { fruitName: { $toString: '$_id' } },
pipeline: [
{
$match:
{
$expr:
{ $in: ["$$fruitName", "$fruits"] }
}
}, { $project: { name: 1, _id: 0 } }
],
as: "salesman"
}
}])
Result for Query 1:
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango",
"salesman" : [
{
"name" : "abc"
},
{
"name" : "zxc"
}
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears",
"salesman" : [
{
"name" : "def"
},
{
"name" : "zxc"
}
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple",
"salesman" : [
{
"name" : "abc"
},
{
"name" : "zxc"
}
]
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava",
"salesman" : []
}
Or if you want those names to be in an array :
Query 2 :
db.fruit.aggregate([
{
$lookup:
{
from: "salesman",
let: { fruitName: { $toString: '$_id' } },
pipeline: [
{
$match:
{
$expr:
{ $in: ["$$fruitName", "$fruits"] }
}
}, { $project: { name: 1, _id: 0 } }
],
as: "salesmanList"
}
}, {
$project: {
name: 1, salesman: {
$map:
{
input: "$salesmanList",
as: "each",
in: '$$each.name'
}
}
}
}])
Result for Query 2:
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango",
"salesman" : [
"abc",
"zxc"
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears",
"salesman" : [
"def",
"zxc"
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple",
"salesman" : [
"abc",
"zxc"
]
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava",
"salesman" : []
}
fruit collection :
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32eb"),
"name" : "Mango"
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32ec"),
"name" : "Pears"
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32de"),
"name" : "apple"
}
/* 4 */
{
"_id" : ObjectId("5b101caddcab7850a4ba32fe"),
"name" : "guava"
}
salesman collection :
/* 1 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3258"),
"name" : "abc",
"fruits" : [
"5b101caddcab7850a4ba32eb",
"5b101caddcab7850a4ba32de"
]
}
/* 2 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3259"),
"name" : "def",
"fruits" : [
"5b101caddcab7850a4ba32ec"
]
}
/* 3 */
{
"_id" : ObjectId("5b101caddcab7850a4ba3260"),
"name" : "zxc",
"fruits" : [
"5b101caddcab7850a4ba32ec",
"5b101caddcab7850a4ba32de",
"5b101caddcab7850a4ba32eb"
]
}
If you want the entire object from salesman then you could remove { $project: { name: 1, _id: 0 } } in $lookup.
Ref : $lookup, $map
Does this help?
db.salesman.aggregate([
{ $unwind : "$fruits" },
{ $addFields : { "fruitObjectId": { $toObjectId: "$fruits" } } },
{ $lookup : {
"from" : "fruit",
"localField" : "fruitObjectId",
"foreignField" : "_id",
"as" : "fruitObjects"
}
}
])
Or, perhaps from the opposite perspective?
db.fruit.aggregate([
{ $project : {
_id : { $toString : "$_id" },
name : 1
}
},
{ $lookup : {
"from" : "salesman",
"localField" : "_id",
"foreignField" : "fruits",
"as" : "fruitObjects"
}
}
])

How to $setDifference in array & Object using Mongo DB

UserDetails
{
"_id" : "5c23536f807caa1bec00e79b",
"UID" : "1",
"name" : "A",
},
{
"_id" : "5c23536f807caa1bec00e78b",
"UID" : "2",
"name" : "B",
},
{
"_id" : "5c23536f807caa1bec00e90",
"UID" : "3",
"name" : "C"
}
UserProducts
{
"_id" : "5c23536f807caa1bec00e79c",
"UPID" : "100",
"UID" : "1",
"status" : "A"
},
{
"_id" : "5c23536f807caa1bec00e79c",
"UPID" : "200",
"UID" : "2",
"status" : "A"
},
{
"_id" : "5c23536f807caa1bec00e52c",
"UPID" : "300",
"UID" : "3",
"status" : "A"
}
Groups
{
"_id" : "5bb20d7556db6915846da55f",
"members" : {
"regularStudent" : [
"200" // UPID
],
}
},
{
"_id" : "5bb20d7556db69158468878",
"members" : {
"regularStudent" : {
"0" : "100" // UPID
}
}
}
Step 1
I have to take UID from UserDetails check with UserProducts then take UPID from UserProducts
Step 2
we have to check this UPID mapped to Groups collection or not ?.
members.regularStudent we are mapped UPID
Step 3
Suppose UPID not mapped means i want to print the UPID from from UserProducts
I have tried but couldn't complete this, kindly help me out on this.
Expected Output:
["300"]
Note: Expected Output is ["300"] , because UserProducts having UPID 100 & 200 but Groups collection mapped only 100& 200.
My Code
var queryResult = db.UserDetails.aggregate(
{
$lookup: {
from: "UserProducts",
localField: "UID",
foreignField: "UID",
as: "userProduct"
}
},
{ $unwind: "$userProduct" },
{ "$match": { "userProduct.status": "A" } },
{
"$project": { "_id" : 0, "userProduct.UPID" : 1 }
},
{
$group: {
_id: null,
userProductUPIDs: { $addToSet: "$userProduct.UPID" }
}
});
let userProductUPIDs = queryResult.toArray()[0].userProductUPIDs;
db.Groups.aggregate([
{
$unwind: "$members.regularStudent"
},
{
$group: {
_id: null,
UPIDs: { $addToSet: "$members.regularStudent" }
}
},
{
$project: {
members: {
$setDifference: [ userProductUPIDs , "$UPIDs" ]
},
_id : 0
}
}
])
My Output
{
"members" : [
"300",
"100"
]
}
You need to fix that second aggregation and get all UPIDs as an array. To achieve that you can use $cond and based on $type either return an array or use $objectToArray to run the conversion, try:
db.Groups.aggregate([
{
$project: {
students: {
$cond: [
{ $eq: [ { $type: "$members.regularStudent" }, "array" ] },
"$members.regularStudent",
{ $map: { input: { "$objectToArray": "$members.regularStudent" }, as: "x", in: "$$x.v" } }
]
}
}
},
{
$unwind: "$students"
},
{
$group: {
_id: null,
UPIDs: { $addToSet: "$students" }
}
},
{
$project: {
members: {
$setDifference: [ userProductUPIDs , "$UPIDs" ]
},
_id : 0
}
}
])