How to use $lookup as INNER JOIN in MongoDB Aggregation? - mongodb

I have used $lookup in my aggregate query.
But as I am seeing it works as LEFT OUTER JOIN.
I want to fetch exact matches document(INNER JOIN) with $lookup.
Is there any way to get it done?
This is my inventory collection:
/* 1 */
{
"_id" : 1,
"sku" : "abc",
"description" : "product 1",
"instock" : 120
}
/* 2 */
{
"_id" : 2,
"sku" : "def",
"description" : "product 2",
"instock" : 80
}
/* 3 */
{
"_id" : 3,
"sku" : "ijk",
"description" : "product 3",
"instock" : 60
}
/* 4 */
{
"_id" : 4,
"sku" : "jkl",
"description" : "product 4",
"instock" : 70
}
/* 5 */
{
"_id" : 5,
"sku" : null,
"description" : "Incomplete"
}
This is my orders collection
/* 1 */
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2
}
/* 2 */
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1
}
/* 3 */
{
"_id" : 10,
"item" : "jklw",
"price" : 20,
"quantity" : 1
}
And this is query
db.getCollection('inventory').aggregate([
{
$lookup:
{
from: "orders",
localField: "sku",
foreignField: "item",
as: "inventory_docs"
}
}
])
In this query I am getting all the inventory's document matches with orders documents
Expected Result
/* 1 */
{
"_id" : 1,
"sku" : "abc",
"description" : "product 1",
"instock" : 120,
"inventory_docs" : [
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2
}
]
}
/* 2 */
{
"_id" : 4,
"sku" : "jkl",
"description" : "product 4",
"instock" : 70,
"inventory_docs" : [
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1
}
]
}

Just add the $match pipeline stage which skips documents with empty inventory_docs field. There no other way to achieve that.
Query:
db.getCollection('inventory').aggregate([
{
$lookup: {
from: "orders",
localField: "sku",
foreignField: "item",
as: "inventory_docs"
}
},
{
$match: {
"inventory_docs": {$ne: []}
}
}
])
Result:
{
"_id" : 1.0,
"sku" : "abc",
"description" : "product 1",
"instock" : 120.0,
"inventory_docs" : [
{
"_id" : 1.0,
"item" : "abc",
"price" : 12.0,
"quantity" : 2.0
}
]
}
{
"_id" : 4.0,
"sku" : "jkl",
"description" : "product 4",
"instock" : 70.0,
"inventory_docs" : [
{
"_id" : 2.0,
"item" : "jkl",
"price" : 20.0,
"quantity" : 1.0
}
]
}

Related

Add unique group ids in group aggregate function of MongoDB query

I wanted to add unique group ids for each group created by multiple fields in the Mongo query.
{ "item" : "abc", "price" : 10, "quantity" : 2 },
{ "item" : "jkl", "price" : 20, "quantity" : 1 },
{ "item" : "xyz", "price" : 15, "quantity" : 10 },
{ "item" : "xyz", "price" : 5, "quantity" : 20 },
{ "item" : "abc", "price" : 10, "quantity" : 10 }
I wanted to group by items and price:
{ "_id" : { "item" : "abc", "price" : 10 } },
{ "_id" : { "item" : "jkl", "price" : 20 } },
{ "_id" : { "item" : "xyz", "price" : 10 } },
{ "_id" : { "item" : "xyz", "price" : 15 } }
But along with that I wanted to add group ids for each group and the output I want:
{ "gid" : "1", "_id" : { "item" : "abc", "price" : 10 } },
{ "gid" : "2", "_id" : { "item" : "jkl", "price" : 20 } },
{ "gid" : "3", "_id" : { "item" : "xyz", "price" : 10 } },
{ "gid" : "4", "_id" : { "item" : "xyz", "price" : 15 } }
db.collection('example').aggregate([
{
$group:{
_id:{item:"$item",price:"$price"}
},
{
$addFields: {
gid: {
$function: {
body: function() {
return UUID().toString().split('"')[1];
},
args: [],
lang: "js"
}
}
}
}
])

MongoDB: Remove field in array with $lookup localField

I am beginner with MongoDB. I use $lookup in aggregation and use localField to get reference document.
db.orders.insert([
{ "_id" : 1, "item" : ['almonds','pecans','bread'], "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : ['cashews','catty'], "price" : 20, "quantity" : 1 }
])
I tried to use $lookup and localField in aggregation but I can't find way to remove field _id and description
db.inventory.insert([
{ "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },
{ "_id" : 5, "sku": "catty", description: "Incomplete", "instock" : 100 },
{ "_id" : 6 }
])
Expected results:
[
{
"_id" : 1,
"item" : [
{ "sku" : "almonds", "instock" : 120 },
{ "sku" : "pecans", "instock" : 70 },
{ "sku" : "bread", "instock" : 80 }
],
"price" : 12,
"quantity" : 2
},
{
"_id" : 2,
"item" : [
{ "sku" : "cashews", "instock" : 60 },
{ "sku" : "catty", "instock" : 100 }
],
"price" : 20,
"quantity" : 1
}
]
You can try lookup with aggregation pipeline,
$lookup join with inventory collection
$match to match is inventory sku in item array
$project to display required fields
db.orders.aggregate([
{
$lookup: {
from: "inventory",
as: "item",
let: { i: "$item" },
pipeline: [
{ $match: { $expr: { $in: ["$sku", "$$i"] } } },
{
$project: {
_id: 0,
sku: 1,
instock: 1
}
}
]
}
}
])
Playground

Closing balance in mongo using aggregation

I have a 2 collection
1.transactions:
id:Account1,
type of transaction:[credit or debit],
amount:some currency
date:Date
accounts:
name:Account 1 or so on
openingBalance:some amount
another collection of accounts has opening balance
/* 1 */
{
"_id" : ObjectId("5f214de9d150a21db8095395"),
"name" : "Account 2",
"openingBalance" : 10000.0
}
/* 2 */
{
"_id" : ObjectId("5f214de9d150a21db8095396"),
"name" : "Account 1",
"openingBalance" : 50000.0
}
and another collection has all the transaction by amount and by nature of transaction
/* 1 */
{
"_id" : ObjectId("5f214cf7608157bae3aeb41a"),
"Account" : "Account 1",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-01-04T13:18:43.000Z")
}
/* 2 */
{
"_id" : ObjectId("5f214d01608157bae3aeb41d"),
"Account" : "Account 1",
"NatureOfTranscation" : "debit",
"Amount" : 140,
"Date" : ISODate("2019-12-03T22:30:25.000Z")
}
/* 3 */
{
"_id" : ObjectId("5f214d0b608157bae3aeb422"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-03-24T09:15:12.000Z")
}
/* 4 */
{
"_id" : ObjectId("5f214d14608157bae3aeb425"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-03-13T09:37:13.000Z")
}
/* 5 */
{
"_id" : ObjectId("5f214d1d608157bae3aeb42a"),
"Account" : "Account 1",
"NatureOfTranscation" : "debit",
"Amount" : 140,
"Date" : ISODate("2020-04-26T02:18:39.000Z")
}
/* 6 */
{
"_id" : ObjectId("5f214d28608157bae3aeb431"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-06-26T03:07:19.000Z")
}
/* 7 */
{
"_id" : ObjectId("5f214d33608157bae3aeb436"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-06-22T12:18:13.000Z")
}
Now using aggregation
Sort by Date
Group by Account
if it is credit openingbalance + amount will be closing balance for that document and vice versa
I have tried this
db.getCollection('col').aggregate([
{
$sort: { 'Date': 1}
},
{
$group: {
_id: '$Account',
transactions: {
$push: '$$ROOT'
}
}
},
{
$project: {
id: 1,
transactions:1,
count: 1
}
}
])
So Far i have got this.
/* 1 */
{
"_id" : "Account 1",
"transactions" : [
{
"_id" : ObjectId("5f214d01608157bae3aeb41d"),
"Account" : "Account 1",
"NatureOfTranscation" : "debit",
"Amount" : 140,
"Date" : ISODate("2019-12-03T22:30:25.000Z")
},
{
"_id" : ObjectId("5f214cf7608157bae3aeb41a"),
"Account" : "Account 1",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-01-04T13:18:43.000Z")
},
{
"_id" : ObjectId("5f214d1d608157bae3aeb42a"),
"Account" : "Account 1",
"NatureOfTranscation" : "debit",
"Amount" : 140,
"Date" : ISODate("2020-04-26T02:18:39.000Z")
}
]
}
/* 2 */
{
"_id" : "Account 2",
"transactions" : [
{
"_id" : ObjectId("5f214d14608157bae3aeb425"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-03-13T09:37:13.000Z")
},
{
"_id" : ObjectId("5f214d0b608157bae3aeb422"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-03-24T09:15:12.000Z")
},
{
"_id" : ObjectId("5f214d33608157bae3aeb436"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-06-22T12:18:13.000Z")
},
{
"_id" : ObjectId("5f214d28608157bae3aeb431"),
"Account" : "Account 2",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-06-26T03:07:19.000Z")
}
]
}
Desired result:
{
"_id" : "Account 1",
"transactions" : [
{
"_id" : ObjectId("5f214d01608157bae3aeb41d"),
"Account" : "Account 1",
"NatureOfTranscation" : "debit",
"Amount" : 140,
"Date" : ISODate("2019-12-03T22:30:25.000Z"),
"closingBalance":49860
},
{
"_id" : ObjectId("5f214cf7608157bae3aeb41a"),
"Account" : "Account 1",
"NatureOfTranscation" : "credit",
"Amount" : 140,
"Date" : ISODate("2020-01-04T13:18:43.000Z"),
"closingBalance":50000
},
{
"_id" : ObjectId("5f214d1d608157bae3aeb42a"),
"Account" : "Account 1",
"NatureOfTranscation" : "debit",
"Amount" : 140,
"Date" : ISODate("2020-04-26T02:18:39.000Z"),
"closingBalance":49500
}
]
}
Any suggestion of regarding the database model also will be welcome.
Thanks In advance.

How to return all documents using $group in mongo aggregation

I have a mongodb pipleline that i'm building up. It's working fine, except that I have an issue in my group stage.
'JURISDICTION'=>['$first'=> '$JURISDICTION'],
I'm only getting documents that match the first value, when they're are multiple values. Any ideas?
Since you have used $first, it will only return the first document, You can use $push to get all the records.
An Example:
For the database entry :
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-02-03T09:00:00Z") }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-03T09:05:00Z") }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }
{ "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T09:05:00Z") }
{ "_id" : 6, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-15T12:05:10Z") }
{ "_id" : 7, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T14:12:12Z") }
You can use this aggregation query which uses push
db.sales.aggregate(
[
{
$group:
{
_id: { day: { $dayOfYear: "$date"}, year: { $year: "$date" } },
itemsSold: { $push: { item: "$item", quantity: "$quantity" } }
}
}
]
)
And the result is a list of items in itemSold
{
"_id" : { "day" : 46, "year" : 2014 },
"itemsSold" : [
{ "item" : "abc", "quantity" : 10 },
{ "item" : "xyz", "quantity" : 10 },
{ "item" : "xyz", "quantity" : 5 },
{ "item" : "xyz", "quantity" : 10 }
]
}
{
"_id" : { "day" : 34, "year" : 2014 },
"itemsSold" : [
{ "item" : "jkl", "quantity" : 1 },
{ "item" : "xyz", "quantity" : 5 }
]
}
{
"_id" : { "day" : 1, "year" : 2014 },
"itemsSold" : [ { "item" : "abc", "quantity" : 2 } ]
}
Reference: $push

MongoDB create $lookup on multipe document with LIKE condition

Suppose I have 2 documents and I want to make join.
customers document
/* 1 */
{
"_id" : ObjectId("596d5aacb02f922a92475698"),
"code" : "A001",
"name" : "NameA001",
"age" : 35
}
/* 2 */
{
"_id" : ObjectId("596d5aacb02f922a9247569a"),
"code" : "A002",
"name" : "NameA002",
"age" : 52
}
/* 3 */
{
"_id" : ObjectId("596d5aacb02f922a9247569c"),
"code" : "A003",
"name" : "NameA003",
"age" : 47
}
sale document
/* 1 */
{
"_id" : ObjectId("596d5ad3b02f922a924756c3"),
"ucode" : "SL-A001",
"pname" : "Product 1",
"quantity" : 3
}
/* 2 */
{
"_id" : ObjectId("596d5ad3b02f922a924756c5"),
"ucode" : "SL-A001",
"pname" : "Product 2",
"quantity" : 5
}
/* 3 */
{
"_id" : ObjectId("596d5ad3b02f922a924756cd"),
"ucode" : "SL-A002",
"pname" : "Product 3",
"quantity" : 10
}
/* 4 */
{
"_id" : ObjectId("596d5ad3b02f922a924756d1"),
"ucode" : "SL-A003",
"pname" : "Product 8",
"quantity" : 5
}
I want to make join on customers and sale document to get all customers whose age are less than 50. The result looks like
/* 1 */
{
"_id" : ObjectId("596d5aacb02f922a92475698"),
"code" : "A001",
"name" : "NameA001",
"age" : 35,
"history" : [
{
"_id" : ObjectId("596d5ad3b02f922a924756c3"),
"ucode" : "SL-A001",
"pname" : "Product 1",
"quantity" : 3
},
{
"_id" : ObjectId("596d5ad3b02f922a924756c5"),
"ucode" : "SL-A001",
"pname" : "Product 2",
"quantity" : 5
}
]
}
/* 2 */
{
"_id" : ObjectId("596d5aacb02f922a9247569c"),
"code" : "A003",
"name" : "NameA003",
"age" : 47,
"history" : [
{
"_id" : ObjectId("596d5ad3b02f922a924756d1"),
"ucode" : "SL-A003",
"pname" : "Product 8",
"quantity" : 5
}
]
}
I wrote aggregate statement but I dont know how to make LIKE condition on $lookup.
db.customer.aggregate([
{
$match: { "age": { $lt: 50 } }
},
{
$lookup:
{
from: "sale",
localField: "code",
foreignField: "ucode",
as: "history"
}
}
])
How can I write lookup with Like condition on localField or foreignField? (Or any suggestion to join with like condition)
Thank you in advance.