MongoDB create $lookup on multipe document with LIKE condition - mongodb

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.

Related

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.

Query array of dictionary in mongodb

I have a collection db.orders in mongodb, this is a example:
{
"_id" : ObjectId("5ed269e0f18b5c0d33150ce1"),
"user" : ObjectId("5ed16ace5b16a6100c1ae744"),
"cart" : {
"items" : [
{
"item" : {
"_id" : "5ed16847fb395528f07f2d19",
"tipo" : "anillo",
"imagePath" : "../images/anillos/anillo1.png",
"title" : "Anillo Geo",
"description" : "Anillo en bronce con baño en oro de 24k",
"price" : 25,
"qty" : 1
}
},
{
"item" : {
"_id" : "5ed16847fb395528f07f2d2e",
"tipo" : "collar",
"imagePath" : "../images/collares/collar10.png",
"title" : "Cadenita Pendulo cuarzos naturales",
"description" : "Cadenita en bronce con baño en oro de 18k.",
"price" : 20,
"qty" : 1
}
},
{
"item" : {
"_id" : "5ed16847fb395528f07f2d26",
"tipo" : "anillo",
"imagePath" : "../images/anillos/anillo14.png",
"title" : "Anillo mandala de cristales",
"description" : "Anillo en bronce con baño en oro de 24k, incluye estuche de madera ",
"price" : 20,
"qty" : 1
}
}
],
"totalQty" : 3,
"totalPrice" : 65
},
"address" : "carrera 9/ Madrid",
"city" : "Madrid",
"name" : "Marcela R",
"paymentId" : "ch_1GoIGGByvh8EqLzfNGV02e3e",
"oderdate" : ISODate("2020-05-30T14:12:48.624Z"),
"__v" : 0
}
I need to know how many title: "Anillo geo" and generally how many cart.items.item.title have been sold. I try different methods but I end up with something like this:
{
"_id" : ObjectId("5ed27fee825300b7d182355d"),
"cart" : {
"items" : [
{
"title" : "Anillo Geo",
"price" : 25,
"qty" : 1,
"tipo" : "anillo"
},
{
"title" : "Cadenita Pendulo cuarzos naturales",
"price" : 29,
"qty" : 1,
"tipo" : "collar"
},
{
"title" : "Anillo mandala de cristales",
"price" : 29,
"qty" : 1,
"tipo" : "anillo"
}
]
}
}
But I need a query that give me something like:
{"_id" : Anillo Geo, count: 5}
{"_id" : Cadenita Pendulo cuarzos naturales, count: 8}...
Anyone have any idea of how to do this query?
Kind regards!!
Basically you need to unwind cart.items array & group on _id + title to count no.of titles got repeated for an _id document.
db.collection.aggregate([
{
$unwind: "$cart.items"
},
{
$group: {
_id: { _id: "$_id", title: "$cart.items.item.title" },
count: { $sum: 1 }
}
},
/** transform fields the way you want in output */
{
$project: { _id: "$_id._id", title: "$_id.title", count: 1 }
}
])
Test : mongoplayground
Note :
If possible try to use $match as first stage to do this operation on a single doc i.e; for a single user or at-least lesser docs rather than on an entire collection.

Find MongoDB docs where all sub-docs match criteria

I have some Product documents that each contain a list of ProductVariation sub-documents. I need to find all the Product docs where ALL their child ProductVariation docs have zero quantity.
Schemas look like this:
var Product = new mongoose.Schema({
name: String,
variations: [ProductVariation]
});
var ProductVariation = new mongoose.Schema({
type: String,
quantity: Number,
price: Number
});
I am a little new to mongodb, so even sure where to start here.
Try using $not wrapped around { "$gt" : 0 }:
> db.products.find()
{ "_id" : ObjectId("5b7cae558ff28edda6ba4a67"), "name" : "widget", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 10 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
{ "_id" : ObjectId("5b7cae678ff28edda6ba4a68"), "name" : "foo", "variations" : [ { "type" : "color", "quantity" : 2, "price" : 15 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
{ "_id" : ObjectId("5b7cae7f8ff28edda6ba4a69"), "name" : "bar", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 15 }, { "type" : "size", "quantity" : 1, "price" : 5 } ] }
> db.products.find({"variations.quantity": { "$not" : { "$gt" : 0 } } })
{ "_id" : ObjectId("5b7cae558ff28edda6ba4a67"), "name" : "widget", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 10 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
It can also take advantage of an index on { "variations.quantity" : 1 }.

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

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
}
]
}

mongodb Embedded document search on parent and child field

I have a nested embedded document CompanyProduct below is structure
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"Name" : Company1,
"CompanyDepartment" : [
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca4")
"DepartmentID" : 287,
"DepartmentName" : "Stores",
"DepartmentInventory" : [
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 1,
"ProductName" : "abc",
"Quantity" : 100
},
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
],
}
],
}
There can be N no of companies and each company can have N number of departments and each department can have N number of products.
I want to do a search to find out a particular product quantity under a particular company
I tried below query but it does not work. It returns all the products for the specific company, the less than 20 condition doesn't work.
db.CompanyProduct.find({$and : [{"CompanyDepartment.DepartmentInventory.Quantity":{$lt :20}},{"CompanyID":90449}]})
How should the query be?
You are searching from companyProduct's sub documents. So it will return you companyProduct whole document, it is NoSQL database , some how we do not need to normalize the collection , but your case it has to be normalize , like if you want to EDIT/DELETE any sub document and if there are thousand or millions of sub document then what will you do ... You need to make other collection with the name on CompanyDepartment and companyProduct collection should be
productCompany
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"Name" : Company1,
"CompanyDepartment" : ['53d213c5ddbb1912343a8ca4'],
}
and other collection companyDepartment
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca4")
"DepartmentID" : 287,
"DepartmentName" : "Stores",
"DepartmentInventory" : [
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 1,
"ProductName" : "abc",
"Quantity" : 100
},
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
],
}
after this you got array of companyDeparment' ID and only push and pull query will be used on productCompany
A Solution can be
db.YourCollection.aggregate([
{
$project:{
"CompanyDepartment.DepartmentInventory":1,
"CompanyID" : 1
}
},{
$unwind: "$CompanyDepartment"
},{
$unwind: "$CompanyDepartment.DepartmentInventory"
},{
$match:{$and : [{"CompanyDepartment.DepartmentInventory.Quantity":{$lt :20}},{"CompanyID":90449}]}
}
])
the result is
{
"result" : [
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"CompanyDepartment" : {
"DepartmentInventory" : {
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
}
}
],
"ok" : 1
}