I am trying to create top 10 product list based on postType = "buy". My logic is a count postType = "buy" and sort the top 10 products from the logs collection. Here are my sample log collections.
[
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "3",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "view",
"product": "4",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "view",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "share",
"product": "3",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "share",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "1",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "1",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "viewvideo",
"product": "1",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "viewvideo",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "viewvideo",
"product": "3",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "4",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "5",
}
]
My expected output is:
[{"product":1, "count":2},{"product":2, "count":3},{"product":3, "count":1},
{"product":4, "count":1},{"product":5, "count":1}
]
I tried the following code, but it is not working.
{
$project: {
_id: 0,
list: { $setEquals: ["$postType", "buy"] }
}
}
I just inserted 4 products but it will be 10 actually.
Would be this one:
db.collection.aggregate([
{ $match: { postType: "buy" } }, // filter on postType = "buy"
{
$group: { // group and count
_id: "$product",
count: { $count: {} }
}
},
{
$project: { // some cosmetic
product: "$_id",
count: 1,
_id: 0
}
}
])
Mongo Playground
Related
I am trying to create top 10 product list based on postType = "buy". My logic is a count postType = "buy" and sort the top 10 products from the logs collection. Here are my sample log collections.
[
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "3",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "view",
"product": "4",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "view",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "share",
"product": "3",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "share",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "1",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "1",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "viewvideo",
"product": "1",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "viewvideo",
"product": "2",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "viewvideo",
"product": "3",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "4",
},
{
"_id": "633dc5b761ff04e7ae8e8c0f",
"postType": "buy",
"product": "5",
}
]
I am trying to get count for totalBuybutton, totalShareButton, totalView if 'buy' keyword matched.
My expected output is:
[
{"product":1, "totalBuycount":2, "shareButtonCount":4, viewCount":4},
{"product":2, "totalBuycount":3, shareButtonCount":4, viewCount":4},
{"product":3, "totalBuycount":1, shareButtonCount":4, viewCount":4},
{"product":4, "totalBuycount":1, shareButtonCount":4, viewCount":4},
{"product":5, "totalBuycount":1, shareButtonCount":2, viewCount":4}
]
My current implementation is as below
aggregate([
{
$match: {
postType: "buybutton"
},
},
{
$group: {
_id: "$product",
count: {
$count: {}
}
}
},
{
$project: {
product: "$_id",
count: 1,
_id: 0,
},
},)
No idea, if this is what you are looking for.
The main part is
{
$group: {
_id: { postType: "$postType", product: "$product" },
count: { $count: {} }
}
}
Do you really need exactly the output as given in the question. It would require quite a lot of cosmetic work
db.collection.aggregate([
{
$group: {
_id: { postType: "$postType", product: "$product" },
count: { $count: {} }
}
},
{
$group: {
_id: "$_id.product",
data: { $push: "$$ROOT" }
}
},
{
$project: {
_id: 0,
product: "$_id",
data: {
$arrayToObject: {
$map: {
input: "$data", in: {
k: "$$this._id.postType", v: "$$this.count",
}
}
}
}
}
},
{ $replaceWith: { $mergeObjects: ["$$ROOT", "$data"] } },
{ $unset: "data" }
])
]
{ product: '5', buy: 1 },
{ product: '3', buy: 1, share: 1, viewvideo: 1 },
{ product: '2', buy: 3, view: 1, viewvideo: 1, share: 1 },
{ product: '1', viewvideo: 1, buy: 2 },
{ product: '4', buy: 1, view: 1 }
]
Mongo Playground
Let's say I have a MongoDB storing transaction prices of a few products like this:
[
{
"_id": 1,
"product": "A",
"price": NumberDecimal("1.00")
},
{
"_id": 2,
"product": "A",
"price": NumberDecimal("20.00")
},
{
"_id": 3,
"product": "A",
"price": NumberDecimal("30.00")
},
{
"_id": 4,
"product": "B",
"price": NumberDecimal("10.00")
},
{
"_id": 5,
"product": "B",
"price": NumberDecimal("200.00")
},
{
"_id": 6,
"product": "B",
"price": NumberDecimal("300.00")
}
]
I want to remove bottom 10% of the extreme transaction prices, I do this:
db.collection.aggregate([
{
$bucketAuto: {
groupBy: "$price",
buckets: 10,
output: {
docs: {
$push: "$$ROOT"
}
}
}
},
{
$skip: 1
},
{
$unwind: "$docs"
},
{
$replaceWith: "$docs"
}
])
The outcome is like this:
[
{
"_id": 4,
"price": NumberDecimal("10.00"),
"product": "B"
},
{
"_id": 2,
"price": NumberDecimal("20.00"),
"product": "A"
},
{
"_id": 3,
"price": NumberDecimal("30.00"),
"product": "A"
},
{
"_id": 5,
"price": NumberDecimal("200.00"),
"product": "B"
},
{
"_id": 6,
"price": NumberDecimal("300.00"),
"product": "B"
}
]
The extreme transaction price 1.00 is removed. But I actually want the extreme 10% prices for each product to be removed, so that price 1.00 of product A is removed, and price 10.00 for product B is also removed. Expected result should be:
[
{
"_id": 2,
"price": NumberDecimal("20.00"),
"product": "A"
},
{
"_id": 3,
"price": NumberDecimal("30.00"),
"product": "A"
},
{
"_id": 5,
"price": NumberDecimal("200.00"),
"product": "B"
},
{
"_id": 6,
"price": NumberDecimal("300.00"),
"product": "B"
}
]
How can I achieve this? I have something very close but it is hard coding the product names in the query, which is very wrong:
https://mongoplayground.net/p/ur3Qmr2VJKb
So I have a parent document with users, as well as an array that has users too. I want to add the DisplayName from the nested users array to the aggregation output. Any ideas?
Output I'm looking to achieve:
[
{
"user": {
"_id": "11",
"Name": "Dave",
"DocID": "1",
"DocDisplyName": "ABC"
},
{
"user": {
"_id": "33",
"Name": "Henry",
"DocID": "1",
"DocDisplyName": "ABC",
"BranchDisplayName:"BranchA"
}
}
]
And so on.. So an array of all users and for users that belong to a branch, add the branch display Name to the output.
// Doc 1
{
"_id": "1",
"DisplayName": "ABC",
"Users": [
{ "_id": "11", "Name": "Dave" },
{ "_id": "22", "Name": "Steve" }
],
"Branches": [
{
"_id": "111",
"DisplayName": "BranchA",
"Users": [
{ "_id": "33", "Name": "Henry" },
{ "_id": "44", "Name": "Josh" },
],
},
{
"_id": "222",
"DisplayName": "BranchB",
"Users": [
{ "_id": "55", "Name": "Mark" },
{ "_id": "66", "Name": "Anton" },
],
}
]
}
``Doc 2
{
"_id": "2",
"DisplayName": "DEF",
"Users": [
{ "_id": "77", "Name": "Josh" },
{ "_id": "88", "Name": "Steve" }
],
"Branches": [
{
"_id": "333",
"DisplayName": "BranchA",
"Users": [
{ "_id": "99", "Name": "Henry" },
{ "_id": "10", "Name": "Josh" },
],
},
{
"_id": "444",
"DisplayName": "BranchB",
"Users": [
{ "_id": "112", "Name": "Susan" },
{ "_id": "112", "Name": "Mary" },
],
}
]
}
Collection.aggregate([
{
$addFields: {
branchUsers: {
$reduce: {
input: "$Branches.Users",
initialValue: [],
in: {
$concatArrays: ["$$this", "$$value"],
},
},
},
},
},
{
$addFields: {
user: {
$concatArrays: ["$branchUsers", "$Users"],
},
},
},
{
$addFields: {
"user.DocID": "$_id","user.DocDisaplyName": "$DisplayName"
},
},
{
$unwind: "$user",
},
{
$project: {
_id: 0,
user: 1,
},
}
])
Thanks in advance!
OK I found a solution.
{
$addFields: {
"branchUsers.BranchDisplayName": {
$let: {
vars: {
first: {
$arrayElemAt: [ "$Branches", 0 ]
}
},
in: "$$first.DisplayName"
}
}
}
},
This creates the field only for the users that belong to the branch
I have this collection:
[
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
}
]
}
]
and i want to find specific document (by _id), then dive into specific chat in this document (by id), than use $lookup for replacing the "senderId" property in each message with a "sender" property that contains the full sender details (as a user), that exist in another collection (users). the result needs to look like this:
[
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"sender": {
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"sender": {
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
"text": "Hello, Im the seller of this product."
}
]
}
]
}
]
You can use this aggregation:
$match to filter only selected document (_id)
$unwind multiple time to transform arrays into objects
$lookup to query external collection (users)
$group in reverse order
I assumed that your collections are more or less like this (next time, post both collections and also an example on a working playground)
db={
"products": [
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308318"
},
"title": "basketball",
"price": 12,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
}
]
},
{
"_id": {
"$oid": "60b22e1dbd46fa18a8308319"
},
"title": "volleyball",
"price": 8,
"category": "Furniture",
"description": "",
"images": [
"http://res.cloudinary.com/hadarush100/image/upload/v1622289949/nfg948x3zro6gbiuknrz.jpg"
],
"categoryId": 1,
"userId": "60ad16493062eb11141d4927",
"createdAt": 1622289948232,
"chats": [
{
"id": 1,
"createdAt": 1622289948232,
"messages": [
{
"id": "1",
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4927",
"text": "Hello, Im the seller of this product."
}
]
},
{
"id": "2",
"createdAt": 1622289948232,
"messages": [
{
"id": 1,
"createdAt": 1622289948232,
"senderId": "60ad16493062eb11141d4928",
"text": "Hello, Im the seller of this product."
}
]
}
]
}
],
"users": [
{
"_id": {
"$oid": "60ad16493062eb11141d4927"
},
"username": "hadar",
"email": "hadarushha#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/79.jpg",
"createdAt": 1621956168518
},
{
"_id": {
"$oid": "60ad16493062eb11141d4928"
},
"username": "test",
"email": "test#gmail.com",
"profileImgUrl": "https://randomuser.me/api/portraits/men/49.jpg",
"createdAt": 1621956168528
},
]
}
And here is the working aggregation:
db.products.aggregate([
{
"$match": {
"_id": {
"$oid": "60b22e1dbd46fa18a8308319"
}
}
},
{
"$unwind": "$chats"
},
{
"$unwind": "$chats.messages"
},
{
"$addFields": {
"chats.messages.senderIdObjId": {
"$convert": {
"input": "$chats.messages.senderId",
"to": "objectId",
}
}
}
},
{
"$lookup": {
"from": "users",
"localField": "chats.messages.senderIdObjId",
"foreignField": "_id",
"as": "chats.messages.sender"
}
},
{
"$unwind": "$chats.messages.sender"
},
{
"$group": {
"_id": "$chats.id",
"messages": {
"$push": "$chats.messages"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$addFields": {
"allFields.chats.messages": "$messages"
}
},
{
"$replaceWith": "$allFields"
},
{
"$group": {
"_id": "$_id",
"chats": {
"$push": "$chats"
},
"allFields": {
"$first": "$$ROOT"
}
}
},
{
"$addFields": {
"allFields.chats": "$chats"
}
},
{
"$replaceWith": "$allFields"
},
])
Working Playground here
I have the collection data from a csv file with header. When i run my query
db.ties.aggregate(
[
{
$group:
{
_id: { "SHOP": "$SHOP" },
isLinkedTo: { $push: { "PERSON": "$PERSON", "CITY": "$CITY", "ROOM": "$ROOM", "STYLE": "$STYLE", "hasDonated": {"DATE": "$DATE", "OBJECT": "$OBJECT", "COST": "$COST", "COLOR": "$COLOR", "PAYMENT": "$PAYMENT"}}}
}
},
{ $out: "ties"}
],
{ allowDiskUse: true }
)
I have like result:
{
"_id": {
"Shop": "FirstShopNameCovered"
},
"isLinkedTo": [{
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitchen",
"STYLEPREFERED": "Modern",
"hasDonated": {
"DATE": "2019-10-11",
"OBJECT": "Set of dishes",
"COST": 72,
"COLOR": "White",
"PAYMENT": "Credit card"
}
}, {
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitcher",
"STYLEPREFERED": "Modern",
"hasDonated": {
"DATE": "2018-10-26",
"OBJECT": "Set of chairs",
"COST": 353,
"COLOR": "Grey",
"PAYMENT": "Coupon"
}
}, {
"PERSON": "Pernick",
"CITY": "Venezia",
"ROOM": "Bathroom",
"STYLE": "Minimalist",
"hasDonated": {
"DATE": "2018-09-18",
"OBJECT": "Mirror",
"COST": 68,
"COLOR": "Brown",
"PAYMENT": "Credit card"
}
}
You can see that there is replicated the Person "PERSON": "Carleen" with all data with 2 different arrays hasDonated.
I wish have something like this result, with person not replicated that contains all hasDonated arrays where he is present:
"_id": {
"Shop": "NameCovered"
},
"isLinkedTo": [{
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitchen",
"STYLE": "RetrĂ²",
"hasDonated": {
"DATE": "2019-10-11",
"OBJECT": "Set of dishes",
"COST": 72,
"COLOR": "White",
"PAYMENT": "Credit card"
},
{
"DATE": "2018-10-26",
"OBJECT": "Chair",
"COST": 53,
"COLOR": "Grey",
"PAYMENT": "Coupon"
}
}, {
"PERSON": "Pernick",
"CITY": "Venezia",
"ROOM": "Bathroom",
"STYLE": "Minimalist",
"hasDonated": {
"DATE": "2018-09-18",
"OBJECT": "Mirror",
"COST": 68,
"COLOR": "Brown",
"PAYMENT": "Credit card"
}
How can I do to have the result like this?
First we need to $unwind to flat the array. Then group the hasDonated using $group where unique is found by combination of "_id" and "PERSON" as you mentioned.
[
{
"$unwind": "$isLinkedTo"
},
{
$group: {
_id: {
_id: "$_id",
per: "$isLinkedTo.PERSON"
},
isLinkedTo: {
$first: {
PERSON: "$isLinkedTo.PERSON",
CITY: "$isLinkedTo.CITY",
ROOM: "$isLinkedTo.ROOM",
STYLEPREFERED: "$isLinkedTo.STYLEPREFERED"
}
},
hasDonated: {
$addToSet: "$isLinkedTo.hasDonated"
}
}
},
{
$addFields: {
_id: "$_id._id",
"isLinkedTo.hasDonated": "$hasDonated"
}
},
{
$project: {
hasDonated: 0
}
},
{
$group: {
_id: "$_id",
isLinkedTo: {
$push: "$isLinkedTo"
}
}
}
]
Working Mongo playground