MongoDB query to get top selling products - javascript

I have a MongoDB collection "orders" with document similar to such:
{
"_id": ObjectId("xxxxx"),
"orderNo": 0,
"products": [
{ "product1": ["_Id": objectId("eeee"),"productName", "quantity", "price"] },
{ "product2": ["_Id": objectId("ffff"),"productName", "quantity", "price"] }
],
"deliveryAddress": "addsfdfghf"
}
{
"_id": ObjectId("yyyyy"),
"orderNo": 1,
"products": [
{ "product1": ["_Id": objectId("dddd"),"productName", "quantity", "price"] }
],
"deliveryAddress": "rwetrutytyi"
}
{
"_id": ObjectId("zzzz"),
"orderNo": 2,
"products": [
{ "product1": ["_Id": objectId("aaaa"), "productName", "quantity", "price"] },
{ "product2": ["_Id": objectId("bbbb"),"productName", "quantity", "price"] },
{ "product3": ["_Id": objectId("cccc"),"productName", "quantity", "price"] }
],
"deliveryAddress": "rwetrutytyi"
}
Now I want to write a query to get top selling products using "quantity" property of every products.
correct values of in above document look like:
{
"products": [{
"_id": {
"$oid": "612baf1b89c77b25c40aa3bc"
},
"productID": "NPZ6I",
"productName": "Cosentys 150ml",
"quantity": "1",
"expireDate": "04/20/2022",
"price": "22200"
}, {
"_id": {
"$oid": "612baf1b89c77b25c40aa3bb"
},
"productID": "NPZ3I",
"productName": "Asunra 400mg",
"quantity": "2",
"expireDate": "09/26/2021",
"price": "13200"
}],
"orderNo": 9,
"deliveryAddress": "Dhaka"
}
I want to return an object look like:
{
"_id": ObjectId("yyyyy"),
"productName": "String",
"productID": "String",
"totalSales" : "Number(sum of each products quantity)",
"totalEarning": "Number(sum of each product's price)"
}

Related

MongoDB : - Merge object with same key into one

I am trying to merge an object inside an array with the same date but with a different key name for the status key.
I have 2 collections users and canteens
The query I am trying to get the result but am not able to figure out how to merge the object with the same Date
OUTPUT
User.aggregate([
{ $sort: { workerId: 1 } },
{
$lookup: {
from: "canteens",
localField: "_id",
foreignField: "employeeId",
pipeline: [
{
$match: {
Date: {
$gte: new Date(fromDate),
$lte: new Date(toDate),
},
},
},
{
$project: {
Date: 1,
status: 1,
},
},
],
as: "canteens",
},
},
{
$project: {
_id: 1,
workerId: 1,
workerFirstName: 1,
workerSurname: 1,
workerDepartment: 1,
workerDesignation: 1,
locationName: 1,
canteenData: "$canteens",
},
},
]);
[
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstaname",
"workerSurname": "lastname",
"workerId": "1",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status": "LUNCH",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b2db8db10c24487201e0a2",
"status": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status": "BREAK FAST",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b3d248c076184fb07ff2c4",
"status": "LUNCH",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b42b8ccb57a4cb7af34015",
"status": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
},
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstaname1",
"workerSurname": "lastname1",
"workerId": "2",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status": "LUNCH",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b2db8db10c24487201e0a2",
"status": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status": "BREAK FAST",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b3d248c076184fb07ff2c4",
"status": "LUNCH",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b42b8ccb57a4cb7af34015",
"status": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
}
]
The output I am trying to get
[
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstanem",
"workerSurname": "lastname",
"workerId": "1",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status1": "LUNCH",
"status2": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status1": "BREAK FAST",
"status2": "LUNCH",
"status3": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status1": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
},
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstanem1",
"workerSurname": "lastname1",
"workerId": "2",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status1": "LUNCH",
"status2": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status1": "BREAK FAST",
"status2": "LUNCH",
"status3": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status1": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
}
]
One option is to add 2 steps into your $lookup pipeline aggregation:
{$group: {
_id: "$Date",
_idVal: {$first: "$_id"},
data: {$addToSet: "$status"}
}},
{$replaceRoot: {
newRoot: {
$mergeObjects: [
{_id: "$_idVal", Date: "$_id"},
{$arrayToObject: {
$reduce: {
input: "$data",
initialValue: [],
in: {$concatArrays: [
"$$value",
[{k: {$concat: [
"status",
{$toString: {$add: [{$size: "$$value"}, 1]}}
]},
v: "$$this"}]
]}
}
}}
]
}
}}
See how it works on the playground example
It's not easy to create status1, status2, ... variables dynamically + how do we know BREAK FAST should be status1 and not status2.
Alternative solution: We $group inside correlated subqueries and push all status values into an array
db.users.aggregate([
{
"$lookup": {
"from": "canteens",
"localField": "_id",
"foreignField": "employeeId",
pipeline: [
{
// Put your custom filters here
$match: {}
},
{
$group: {
_id: "$Date",
//pick "first" canteens _id
id: {
$first: "$_id"
},
status: {
$push: "$status"
}
}
},
{
$project: {
_id: "$id",
Date: "$_id",
status: 1
}
},
],
as: "canteenData",
}
}
])
MongoPlayground

How to update a object value when matched from an array in one mongodb document?

I have created a complex MongoDB document like this :
{
"_id": {
"$oid": "6354129e0f5b15991649fd10"
},
"orderId": "NEK-2209-06215614-79553",
"user": {
"$oid": "634d11565f254092fd666fd1"
},
"shippingAddress": {
"$oid": "6353aaf0fa6a1b0124c22532"
},
"billingAddress": {
"$oid": "6353aaf0fa6a1b0124c22532"
},
"productInfo": [
{
"seller": {
"$oid": "634d784c723ee32fc178aa7a"
},
"products": [
{
"productId": {
"$oid": "6353951e001ff50ea1a92602"
},
"quantity": 2,
"variation": "M",
"tax": 111
}
],
"price": 850,
"status": "Pending"
},
{
"seller": {
"$oid": "6354112f0f5b15991649fcfc"
},
"products": [
{
"productId": {
"$oid": "635411940f5b15991649fd02"
},
"quantity": 2,
"variation": "M",
"tax": 111
}
],
"price": 850,
"status": "Pending"
}
],
"total": 1671,
"shippingFees": 60,
"couponDiscount": 10,
"subtotal": 1721,
"paymentInfo": {
"paymentType": "Cash on Delivery"
},
"paymentMethod": "Home Delivery",
"createdAt": {
"$date": {
"$numberLong": "1666454174641"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1666454174641"
}
},
"__v": 0
}
Here you can see that ProductInfo is an array. My Document structure is
id: "id"
productInfo: [
{seller: "id", ....},
{seller: "id", ....},
]
Now I have two things- id and seller
Actually here I want to do this- first, find by id, then filter productInfo by seller and update status to this particular seller. How can I do that ?
mydocument.findByIdAndUpdate(id,
//Here I have to write an update value to a particular seller from productInfo array.
, {new: true})
Please help me to do this. Can anyone help me?
**Note: Here I want to update only status value from a particular seller when matched find by document id.
You can do it with positional operator - $:
db.collection.update({
_id: ObjectId("6354129e0f5b15991649fd10"),
"productInfo.seller": ObjectId("6354112f0f5b15991649fcfc"),
},
{
"$set": {
"productInfo.$.status": "New status"
}
})
Working example

MongoDB - Structure an array without using key field in Aggregration

I'm having an issue with making count for items returned from an array without assuming or using those fields in my aggregration.
Data structure looks like this:
[
{
"_id": "1",
"title": "Vanella Icream",
"contain": "sugar",
"details": [
{
"flavour": "Vanella"
},
{
"weight": "10KG"
},
{
"sugar": "15KG"
}
]
},
{
"_id": "2",
"title": "Pretzels",
"contain": "salt",
"details": [
{
"flavour": "Wheat"
},
{
"weight": "10KG"
},
{
"sugar": "15KG"
}
]
},
{
"_id": "3",
"title": "Rasmalai Icream",
"contain": "sugar",
"details": [
{
"flavour": "Vanella"
},
{
"weight": "15KG"
},
{
"sugar": "12KG"
}
]
},
{
"_id": "4",
"title": "Vanella Icream",
"contain": "sugar",
"details": [
{
"flavour": "Vanella"
},
{
"weight": "15KG"
},
{
"sugar": "12KG"
}
]
}
]
Output I want:
[
{
"details": {
"flavour": {
"Vanella": 3, //Number of times Vanella present in each document.
"Wheat": 1,
},
"weight": {
"10KG": 2,
"15KG": 2
},
"sugar": {
"12KG": 2,
"15KG": 2
}
}
}
]
Query:
db.collection.aggregate([
{
"$unwind": {
"path": "$details"
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$details",
"$$ROOT"
]
}
}
},
{
"$facet": {
"flavour": [
{
"$group": {
"_id": "$flavour",
"sum": {
"$sum": 1
}
}
},
{
"$addFields": {
"flavour": "$_id"
}
},
{
"$project": {
"_id": 0
}
}
],
"weight": [
{
"$group": {
"_id": "$weight",
"sum": {
"$sum": 1
}
}
},
{
"$addFields": {
"weight": "$_id"
}
},
{
"$project": {
"_id": 0
}
}
]
}
},
{
"$addFields": {
"flavour": {
"$reduce": {
"input": {
"$filter": {
"input": {
"$map": {
"input": "$flavour",
"as": "w",
"in": {
"$cond": [
{
"$ne": [
"$$w.flavour",
null
]
},
{
"$let": {
"vars": {
"o": [
[
"$$w.flavour",
"$$w.sum"
]
]
},
"in": {
"$arrayToObject": "$$o"
}
}
},
null
]
}
}
},
"as": "f",
"cond": {
"$ne": [
"$$f",
null
]
}
}
},
"initialValue": {},
"in": {
"$let": {
"vars": {
"d": "$$value",
"p": "$$this"
},
"in": {
"$mergeObjects": [
"$$d",
"$$p"
]
}
}
}
}
},
"weight": {
"$reduce": {
"input": {
"$filter": {
"input": {
"$map": {
"input": "$weight",
"as": "w",
"in": {
"$cond": [
{
"$ne": [
"$$w.weight",
null
]
},
{
"$let": {
"vars": {
"o": [
[
"$$w.weight",
"$$w.sum"
]
]
},
"in": {
"$arrayToObject": "$$o"
}
}
},
null
]
}
}
},
"as": "f",
"cond": {
"$ne": [
"$$f",
null
]
}
}
},
"initialValue": {},
"in": {
"$let": {
"vars": {
"d": "$$value",
"p": "$$this"
},
"in": {
"$mergeObjects": [
"$$d",
"$$p"
]
}
}
}
}
}
}
},
{
"$project": {
"details": "$$ROOT"
}
}
])
Here I'm trying to get the flavour and weight with their count, with manually adding those fields in $filter stage. I want to do it without assuming those keys. So, even if there is 20 items present in array details it will map those items and shows me output with their counts respectively.
I hope you guys understand.
Playground:https://mongoplayground.net/p/j1mzgWvcmvd
You need to change the schema, the thing you want to do is easy, and both those queries are so complicated and slow, even the second that is much smaller has 2 $unwind and 3 $group with 3 $arrayToObject and 8 stages total because of the schema and the schema of the answer.
Don't store data in the keys of the documents, people that are new to MongoDB do those, i was doing it also, but it makes all things harder.(i can't say like never do it but you dont need it here)
Your schema should be something like
{
"_id": "2",
"title": "Pretzels",
"contain": "salt",
"details": [
{
"type" : "flavour",
"value" : "Wheat"
},
{
"type" : "weight",
"value" : "10KG"
},
{
"type" : "sugar",
"value" : "15KG"
}
]
}
See this example
Converts your schema, to the new schema and produce the results you
want but without data in keys (the first part you wouldnt need it you would need only the bellow query if you had that schema from start)
Query with the new Schema (no data in keys)
[{"$unwind": { "path": "$details"}},
{"$replaceRoot": {"newRoot": "$details"}},
{
"$group": {
"_id": {
"type": "$type",
"value": "$value"
},
"sum": {"$sum": 1}
}
},
{
"$replaceRoot": {
"newRoot": {"$mergeObjects": ["$_id","$$ROOT"]}
}
},
{"$project": {"_id": 0}},
{
"$group": {
"_id": "$type",
"values": {
"$push": {
"value": "$value",
"sum": "$sum"
}
}
}
},
{"$addFields": {"type": "$_id"}},
{"$project": {"_id": 0}}
]
MongoDB operators are not made to support for data in keys or dynamic keys(uknown keys) (to do it you do complicated things like the above)
If you want to change your schema, either do it with update in the database,
Or take the documents to the application and do it with javascript, and re-insert.
Even if you solve this question in the next one, you will have again problems.
I'm the guy from Mongodb Forum:
Try this out https://mongoplayground.net/p/tfyfpIkHilQ

How to compare two arrays and get matching output?

In my collection I have a category array as below.
I receive another array to my API like below
array = ['Chess','Rugby'];
I want to add a condition to my database query such that catName field from category objects exists in array.
currently I'm using the below code to get the results:
postSchemaModel.aggregate([{
"$geoNear": {
"near": { "type": "Point", "coordinates": [parseFloat(long), parseFloat(lat), ] },
"distanceField": "dist.calculated",
"maxDistance": parseInt(maxDistance),
"includeLocs": "dist.location",
"spherical": true
}
},
{ "$match": { "$or": [{ "typology": "post" }, { "typology": "chat_group" }] } },
{
"$match": {
"createdAt": {
"$gte": '2020-07-15 23:54:38.673665',
"$lt": '2020-06-15 23:54:38.673665'
}
}
},
{ "$limit": limit },
{ "$skip": startIndex },
{ "$sort": { "createdAt": -1 } },
{
"$lookup": {
"from": userSchemaModel.collection.name,
"localField": "user_id",
"foreignField": "_id",
"as": "user_id"
}
},
{
"$project": {
"post_data": 1,
"likes": 1,
"commentsCount": 1,
"post_img": 1,
"isUserLiked": 1,
"usersLiked": 1,
'exp_date': 1,
"has_img": 1,
"user_id": {
"img": "$user_id.img",
"_id": "$user_id._id",
"user_name": "$user_id.user_name",
"bday": "$user_id.bday",
"imagesource": "$user_id.imagesource",
"fb_url": "$user_id.fb_url",
},
"typology": 1,
"geometry": 1,
"category": 1,
"created": 1,
"createdAt": 1,
"updatedAt": 1,
}
},
]).then(async function(posts) {
//some code here
}
});
UPDATE : Sample Output
{
"_id": "5f0bd1b7d6ed4f0017e5177c",
"post_data": "bitch boy sudesh",
"likes": 2,
"commentsCount": 1,
"post_img": null,
"isUserLiked": true,
"usersLiked": [
"5f0bfa296ee76f0017f13787",
"5ef60bba10e9090017e2c935"
],
"exp_date": "2020-07-16T00:00:00.000Z",
"has_img": false,
"user_id": [
{
"img": [
"default-user-profile-image.png"
],
"_id": [
"5ef9a7a2922eba0017ce47e0"
],
"user_name": [
"Sudesh"
],
"bday": [
"1997-05-02T00:00:00.000Z"
],
"imagesource": [
"fb"
],
"fb_url": [
"https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=1846836948784193&width=400&ext=1596011605&hash=AeRsB0QJQH7edpRT"
]
}
],
"typology": "post",
"geometry": {
"pintype": "Point",
"_id": "5f0bd1b7d6ed4f0017e5177d",
"coordinates": [
79.9200017,
6.7088167
]
},
"category": [
{
"_id": "5f0bd1b7d6ed4f0017e5177e",
"catID": "5eef80cc5de48230887f3aa8",
"catName": "Chess"
},
{
"_id": "5f0bd1b7d6ed4f0017e5177e",
"catID": "5eef80cc5de48230887f3aa8",
"catName": "Rugby"
}
],
"created": 1594610103626,
"createdAt": "2020-07-13T03:15:03.629Z",
"updatedAt": "2020-07-18T14:02:35.080Z"
}
You can use some method if you only want to get true/false result:
category.some(element => array.includes(element.catName))
If you want to get an array of all the category objects with cat names that also exist in the array then you can filter method:
category.filter(element => array.includes(element.catName))
If you have an object called array in your code and you want to find at array of categories where cat names are in the array then you can add the condition to your $match stage:
{ "$match": { "$or": [{ "typology": "post" }, { "typology": "chat_group" }] }, "category.catName": { $in: array } }
Using another $match with "$elemMatch" solved the problem
"$match": {
"category": { "$elemMatch": { "catName": "Rugby", "catName": "Carrom" } },
}

Elasticsearch v6.0.1 Nodejs boost match with one of arrays elements

In my project I have user objects like this.
{
"_id": "1",
"username": "RAggro",
"name": "Vardan Tadevosyan"
},
{
"_id": "2",
"username": "XACHIK",
"name": "XACHIK"
},
{
"_id": "3",
"username": "vardar",
"name": "Vardan Gukoyan"
},
{
"_id": "4",
"username": "Gordey",
"name": "Gordey Gordeev"
},
{
"_id": "5",
"username": "id220107973",
"name": "Vardan Ayvazyan"
},
{
"_id": "6",
"username": "vvardanyan4",
"name": "Vardan Vardanyan"
},
{
"_id": "7",
"username": "svardan",
"name": "Vardan Sargsyan"
}
And I have list of _id-s, like [51,3,9,11,6, 2].
I whant to query users by 'name' and 'username', orderid like first comes users that contains in ids array then others
query: {
multi_match: {
query: "vardan",
fields: ["name", "username"],
operator: "or"
},
boosting: {
positive: {
term: {
_id: [51,3,9,11,6, 2]
}
},
positive_boost: 2.0
}
}
So the expected result is:
{
"_id": "3",
"username": "vardar",
"name": "Vardan Gukoyan"
},
{
"_id": "6",
"username": "vvardanyan4",
"name": "Vardan Vardanyan"
},
{
"_id": "1",
"username": "RAggro",
"name": "Vardan Tadevosyan"
},
{
"_id": "5",
"username": "id220107973",
"name": "Vardan Ayvazyan"
},
{
"_id": "7",
"username": "svardan",
"name": "Vardan Sargsyan"
}
But I'm fetching empty array,
Please, help how can I modify my query to reach expected ordered result.
You can do it easily using a bool/should clause that will boost the documents whose IDs are within the specified group:
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "vardan",
"fields": [
"name",
"username"
],
"operator": "or"
}
}
],
"should": [
{
"ids": {
"values": ["51","3","9","11","6","2"]
}
}
]
}
}
}
It works using this query:
{
query:{
"bool": {
"must": [{
"multi_match": {
"query": "vardan",
"fields": [
"name",
"username"
],
"operator": "or"
}
}],
"should": [{
"terms": {
"_id": ["51","3","9","11","6","2"],
"boost": 100
}
}]
}
}
}

Categories

Resources