db.customer.insert([
{"_id": "C1",
"CUSTOMER" : { "customerId" : "C1",
"customerName" : "Tony",
"SHOPPINGCART":[{ "cartId" : "cart001",
"purchased": "no",
"containsProdList" : [
{ "prodListId" : 1,
"productId" : "P2",
"quantity" : 2 },
{ "prodListId" : 2,
"productId" : "P1",
"quantity": 1 } ]
},
{ "cartId" : "cart002",
"purchased": "yes",
"containsProdList" : [
{ "prodListId" : 1,
"productId" : "P2",
"quantity": 3 } ]
},
{ "cartId" : "cart006",
"purchased": "yes",
"containsProdList" : [
{ "prodListId" : 1,
"productId" : "P3",
"quantity": 3 } ]
} ]
}
},
{"_id":"C2",
"CUSTOMER" : { "customerId" : "C2",
"customerName" : "James",
"SHOPPINGCART":[
{ "cartId" : "cart003",
"purchased": "yes",
"containsProdList" : [
{ "prodListId" : 1,
"productId" : "P2",
"quantity" : 2 },
{ "prodListId" : 2,
"productId" : "P3",
"quantity": 1 } ]
},
{ "cartId" : "cart004",
"purchased": "no",
"containsProdList" : [
{ "prodListId" : 1,
"productId" : "P1",
"quantity": 3 } ]
},
{ "cartId" : "cart005",
"purchased": "no",
"containsProdList" : [
{ "prodListId" : 1,
"productId" : "P2",
"quantity": 1 } ]
}]
}
}
]);
Above code is my data in JSON form, and I want to extract the customer who purchased both of "P1" and "P2".
db.shoppingCart.aggregate([{
$unwind: {
path: '$CUSTOMER.creates.SHOPPINGCART'
}
}, {
$match: {
'CUSTOMER.creates.SHOPPINGCART.dateClosed': {
$ne: null
},
$and: [
{
'CUSTOMER.creates.SHOPPINGCART.containsProdList.productId': 'P1002'
},
{
'CUSTOMER.creates.SHOPPINGCART.containsProdList.productId': 'P1003'
}
]
}
}])
I wrote the code like this, but this work when P2 and P3 are in the same shopping cart(output is only C2) while I want to find the customer who purchased regardless of date and shopping cart.
Excepted output is both of C1 and C2 since products are in C1's cart002 and cart006, respectively, and their purchased values are set as yes. C2 has both products in the same shopping cart and product list.
I need your help, guys!
CodePudding user response:
If I got you well, you wanna extract all customers that have purchased productId "P1" and "P2"; if this is the case, you can make your query like this:
db.collection.find({
$and: [
{
"CUSTOMER.SHOPPINGCART.containsProdList.productId": "P1"
},
{
"CUSTOMER.SHOPPINGCART.containsProdList.productId": "P2"
}
]
})
You can check it out here
CodePudding user response:
Suppose we want to find customers who purchased P1 and P2 (or any arbitrary number) of products but in the same cart. Here is a solution that features no $unwind
and is thus performant even with large arrays of SHOPPINGCART
.
It is not initially easy to grok what is happening here, but if it
helps if you look at it "depth-up" instead of top-down.
- We want to find carts with both P1 and P2, so we want to filter
on that. To make it a little more generic, we will set up a
target array variable "targs" with ["P1","P2"] - We will use
$map
as a way to take theCUSTOMER.SHOPPINGCART
array
and pass it to$filter
- The array after
$filter
will contain 0 to n items; we get the
length with the$size
operator and capture that innpids
. - We now have an array of
{cartId,npids}
objects... - ...which we use as input to the "outer"
$filter
to only keep those
objects where npids == targs.length which means ALL the products
appeared in that cart. - The whole thing is projected as
XX
. - Many docs will not have any matches and
XX
will be size 0. We
can use$match
to eliminate those.
var targs = ["P1","P2"];
db.shoppingCart.aggregate([
{$project: {
XX: {$filter: {input: // the "outer" filter
{$map: {input: "$CUSTOMER.SHOPPINGCART", as:"z", in: {
cart: "$$z.cartId",
npids: {$size: {$filter: {input: "$$z.containsProdList", // inner filter
as: "z2",
cond: {$in:["$$z2.productId",targs]} // the heart of the matter
}}
}
} }},
as: "z3",
cond: {$eq:["$$z3.npids",targs.length]}
}}
}}
,{$match: {$expr: {$gt:[{$size: "$XX"},0]} }}
]);