Home > Net >  find data with aggregation in mongodb
find data with aggregation in mongodb

Time:11-26

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 the CUSTOMER.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 in npids.
  • 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]} }}
]);
  • Related