How to get Mongo data using aggregate MongoDB version 3.0 - node.js

Suppose we have 10 collections, then we have to find the count on the basis of tag_id. For example, if tag_id contains 0 and 1, then we have to count all the data, as well as counting the data that don't have tag_id, or where tag_id is null. Then if it has unread : false then the output comes, count of all the unread.
Find the counts of tag_id and counts of unread when false.
{
"_id": ObjectId("5912c7240520df77f0c2c18a"),
"email_id": "54",
"unread": "false",
"__v": NumberLong(0),
"tag_id": ["0"
]
}, {
"_id": ObjectId("5912c71e0520df77f0c2c189"),
"email_id": "55",
"unread": "false",
"__v": NumberLong(0),
"tag_id": [
"1"
]
}, {
"_id": ObjectId("5912c71d0520df77f0c2c186"),
"email_id": "51",
"unread": "false",
"__v": NumberLong(0),
"tag_id": [
"2", "1"
]
}
expected result:
{
"data": [{
"tag_id": "1",
"count_email": 1,(count of email on the basis of tag_id)
"unread": 9(count the unread on the basis of output of tag_id)
}, {
"tag_id": "3",
"count_email": 45,
"unread": 3
}, {
"tag_id": "2",
"count_email": 5,
"unread": 4
}, {
"id": null,
"count_email": 52,
"unread": 35
}]
}

Try the following code
Refer - https://docs.mongodb.com/manual/reference/operator/aggregation/eq/
https://docs.mongodb.com/manual/reference/operator/aggregation/cond/
https://docs.mongodb.com/manual/reference/operator/aggregation/group/
DB.aggregate([
{$project:
{
tag_id: '$tag_id',
unreadcount: { $cond: [ { $eq: [ '$unread', 'true' ] }, 1, 0 ] }
}},
{ $group: {
_id: '$tag_id',
unread: { $sum: '$unreadcount'},
}}
], function (err, results) {
console.log(results);
})

Related

mongodb aggregation: get all values for the particular field in list

I have collections like:
[
{
"_id": "1",
"religion": "south",
"tested": true,
"fruit": "orange",
"created_at": 2211123333
},
{
"_id": "2",
"religion": "north",
"tested": false,
"fruit": "apple",
"created_at": 223444433
},
{
"_id": "3",
"religion": "north",
"tested": true,
"fruit": "orange",
"created_at": 234567876
}
]
if religion is south and tested is true then get all the values of fruits in list.
tried:
pipeline = [{"$match": {"$and": [{"religion": "south"}, {"tested": true}]}}, {}{"$project": {"fruit": 1, "_id": 0}}]
db.collection.aggregate(pipeline).to_list(length=None)
getting result as : [{"fruit": "orange"}, {"fruit": "apple"}]
but result should be like: {"fruit" : ["orange", "apple"]}
use $group, $addToSet and using$cond you don't need the $match stage
test it at mongoPlayground
[
{
"$group": {
"_id": null,
"fruit": {
"$addToSet": {
"$cond": [
{
"$and": [
{
"$eq": [
"$religion",
"south"
]
},
{
"$eq": [
"$tested",
true
]
}
]
},
"$fruit",
"$$REMOVE"
]
}
}
}
},
{
"$project": {
fruit: 1,
_id: 0
}
}
]

Aggregate duplicate documents with values in array in Mongo

I have a large collection of documents that look as follows:
{ "_id": "5a760191813a54000b8475f1", "orders": [{ "row": "3", "seat": "11" }, { "row": "3", "seat": "12" }], "product_id": "5a7628bedbcc42000aa7f614" },
{ "_id": "5a75f6f17abe45000a3ba05e", "orders": [{ "row": "3", "seat": "12" }, { "row": "3", "seat": "13" }], "product_id": "5a7628bedbcc42000aa7f614" },
{ "_id": "5a75ebdf813a54000b8475e7", "orders": [{ "row": "5", "seat": "16" }, { "row": "5", "seat": "15" }], "product_id": "5a75f711dbcc42000c459efc" }
I need to be able to find any documents where the product_id and items in the orders array are duplicates. I can't quite seem to wrap my head around accomplishing this. Any pointers?
I don't know what output you want, but this has the information about the duplicates, maybe you want to add unwind on duplicates also.
Result documents
product_id
order (that found duplicated)
duplicates (the documents that had that order as duplicate)
For your data would print
[{
"duplicates": [
"5a760191813a54000b8475f1",
"5a75f6f17abe45000a3ba05e"
],
"order": {
"row": "3",
"seat": "12"
},
"product_id": "5a7628bedbcc42000aa7f614"
}]
Query
(run it on your driver, MongoPlayground doesn't keep the order of fields and can show wrong results)
aggregate(
[{"$unwind" : {"path" : "$orders"}},
{
"$group" : {
"_id" : {
"orders" : "$orders",
"product_id" : "$product_id"
},
"duplicates" : {
"$push" : "$_id"
}
}
},
{"$match" : {"$expr" : {"$gt" : [ {"$size" : "$duplicates"}, 1 ]}}},
{
"$project" : {
"_id" : 0,
"order" : "$_id.orders",
"product_id" : "$_id.product_id",
"duplicates" : 1
}
}
])
Data (i added some more data)
[
{
"_id": "5a760191813a54000b8475f1",
"orders": [
{
"row": "3",
"seat": "11"
},
{
"row": "3",
"seat": "12"
}
],
"product_id": "5a7628bedbcc42000aa7f614"
},
{
"_id": "5a75f6f17abe45000a3ba05g",
"orders": [
{
"row": "3",
"seat": "12"
},
{
"row": "3",
"seat": "13"
}
],
"product_id": "5a7628bedbcc42000aa7f614"
},
{
"_id": "5a75f6f17abe45000a3ba05e",
"orders": [
{
"row": "3",
"seat": "12"
},
{
"row": "3",
"seat": "13"
}
],
"product_id": "5a7628bedbcc42000aa7f614"
},
{
"_id": "5a75ebdf813a54000b8475e7",
"orders": [
{
"row": "5",
"seat": "16"
},
{
"row": "5",
"seat": "15"
}
],
"product_id": "5a75f711dbcc42000c459efc"
}
]
Results
[{
"duplicates": [
"5a75f6f17abe45000a3ba05g",
"5a75f6f17abe45000a3ba05e"
],
"order": {
"row": "3",
"seat": "13"
},
"product_id": "5a7628bedbcc42000aa7f614"
},
{
"duplicates": [
"5a760191813a54000b8475f1",
"5a75f6f17abe45000a3ba05g",
"5a75f6f17abe45000a3ba05e"
],
"order": {
"row": "3",
"seat": "12"
},
"product_id": "5a7628bedbcc42000aa7f614"
}]
You could use below query. $unwind the orders array, $group by order row and product and collect matching ids and count. Keep the documents where count is greater than 1. $lookup to pull in the matching documents by id and $replaceRoot to flatten the documents.
db.collection.aggregate([
{
"$unwind": "$orders"
},
{
"$group": {
"_id": {
"order": "$orders",
"product_id": "$product_id"
},
"count": {
"$sum": 1
},
"doc_ids": {
"$push": "$_id"
}
}
},
{
"$match": {
"count": {
"$gt": 1
}
}
},
{
"$lookup": {
"from": "collection",
"localField": "doc_ids",
"foreignField": "_id",
"as": "documents"
}
},
{
"$unwind": "$documents"
},
{
"$replaceRoot": {
"newRoot": "$documents"
}
}
])
https://mongoplayground.net/p/YbztEGttUMx
While this can be done purely in Mongo I do not recommend it as it's very very very memory inefficient. you basically have to hold the entire collection in memory the entire time while you do certain manipulations on it.
I will however show the pipeline for this because we will use it with the second more scaleable approach.
We want to $group based on orders and product_id, however there are 2 issues standing in our way.
The orders field might not be sorted the same in all documents, because Mongo does not support "nested" sorting we have to $unwind the array, $sort it and restore the original structure. ( mind you you're sorting the entire collection here in memory ). This step which is one of the pain points of this pipeline can be skipped if you can ensure sort order is maintained in the orders array.
Mongo is inconsistent when $grouping an array of objects. full disclosure I'm not entirely sure what's going on in there but I'm guessing there are some "shortcuts" done for efficiency which affects the stability somehow. So our approach will be to convert these objects into a string (concating the "row" and "seat" together).
db.collection.aggregate([
{
"$unwind": "$orders"
},
{
$sort: {
"orders.row": 1,
"orders.seat": 1
}
},
{
$group: {
_id: "$_id",
tmpOrders: {
$push: {
$concat: [
"$orders.row",
"$orders.seat"
]
}
},
product_id: {
$first: "$product_id"
}
}
},
{
$group: {
_id: {
orders: "$tmpOrders",
product: "$product_id"
},
dupIds: {
$push: "$_id"
}
}
},
{
$match: {
"dupIds.0": {
$exists: true
}
}
},
{
$project: {
_id: 0,
dups: "$dupIds",
}
}
])
Mongo Playground
Now as I said this approach is not scaleable, and on large collections will take a very long time to run. So I recommend utilizing indexes and iterating over product_id's and executing each pipeline separately.
// wraps the native Promise, not required.
import Bluebird = require('bluebird');
// very fast with index.
const productIds = await collection.distinct('product_id')
await Bluebird.map(productIds, async (productId) => {
const dups = await collection.aggregate([
{
$match: {
product_id: productId
}
}
... same pipeline ...
])
if (dups.length) {
// logic required.
}
// can control concurrency based on db workload.
}, { concurrency: 5})
Make sure with this approach you have an index built on product_id so it will work efficiently.

How to filter results from collection the $lookup in mongoose

i want to filter the result as the following in mongodb. I use $lookup to populate the result from another collection. Please check my following code
This code below is what i get
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"_id": "5f3a9bbc325a074240a1a815",
"firstname": "test",
"lastname": "test",
"storename": "test",
"gst": "test",
"phoneNumber": 1,
"email": "1#demo.com",
"address1": "test",
"address2": "test",
"city": "test",
"state": "test",
"country": "test",
"zip": "1",
"password": "1",
"usertype": 3,
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0
}
]
},
How to retrieve only unique_SHOP and zip from source listing.I want result like the one below with one or more fields
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"zip": "1",
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
}
]
},
The query i use
List.aggregate([
{$match:
{ productname: { $regex: req.params.query,$options: "i" }}
},
{ $lookup:{
from: "suppliers",
localField: "creator",
foreignField: "unique_SHOP",
as: "source"
}
},
])
You can try $lookup with pipeline,
$match condition of creator id
$project to display required fields
{
$lookup: {
from: "suppliers",
as: "source",
let: { creator: "$creator" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$creator", "$_id"] }
}
},
{
$project: {
_id: 0,
zip: 1,
unique_SHOP: 1
}
}
]
}
}
Playground

MongoDB Aggregate with sum of array object values

I have a collection with the following data:
{ "id": 1,
"name": "abc",
"age" : "12"
"quizzes": [
{
"id": "1",
"time": "10"
},
{
"id": "2",
"time": "20"
}
]
},
{ "id": 2,
"name": "efg",
"age" : "20"
"quizzes": [
{
"id": "3",
"time": "11"
},
{
"id": "4",
"time": "25"
}
]
},
...
I would like to perform the MongoDB Aggregation for a sum of quizzes for each document.and set it to totalTimes field
And this is the result that I would like to get after the querying:
{ "id": 1,
"name": "abc",
"age" : "12",
"totalTimes": "30"
"quizzes": [
{
"id": "1",
"time": "10"
},
{
"id": "2",
"time": "20"
}
]
},
{ "id": 2,
"name": "efg",
"age" : "20",
"totalTimes": "36"
"quizzes": [
{
"id": "3",
"time": "11"
},
{
"id": "4",
"time": "25"
}
]
},
...
How can I query to get the sum of quizzes time?
Quite simple using $reduce
db.collection.aggregate([
{
$addFields: {
totalTimes: {
$reduce: {
input: "$quizzes",
initialValue: 0,
in: {
$sum: [
{
$toInt: "$$this.time"
},
"$$value"
]
}
}
}
}
}
])
Mongo Playground

Multiple group using Mongoose

I'm wanting to get the sum of all events for each company. Here is my data:
{company: "1" event:"a"}
{company: "1" event:"b"}
{company: "1" event:"c"}
{company: "2" event:"b"}
{company: "2" event:"b"}
{company: "3" event:"c"}
{company: "3" event:"c"}
I currently have this to aggregate the date for events but I'm struggling to further group them by company:
{
"aggregate": [
{
"$group": {
"_id": "$event",
"count": {"$sum": 1}
}
},
{"$sort": {"_id": 1}}
]
}
Which renders these results:
[
{
"_id": "a",
"count": 1
},
{
"_id": "b",
"count": 3
},
{
"_id": "c",
"count": 2
}]
How can I further group the event counts by company to produce something along the lines of:
[{
"_id": {
"company" :"1",
"events": [
{
"event": "a",
"count": 1
},
{
"event": "b",
"count": 1
},
{
"event": "c",
"count": 1
},
]
}
},
{
"_id": {
"company" :"2",
"events": [
{
"event": "b",
"count": 2
}
]
}
},
{
"_id": {
"company" :"3",
"events": [
{
"event": "c",
"count": 2
}
]
}
}
]
I've tried all sorts of further groups in the query but can't produce the data I'm after. There is also one caveat: I'm unable to process anything server side or change the schema, so therefore I am only enable to pass in a query which I hope can be done.
db.collection.aggregate([
{
$group:{
_id:{company:"$company",event:"$event"},
count:{$sum:1}
}
},
{
$group:{
_id:"$_id.company",
events:{$push:{event:"$_id.event",count:"$count"}}
}
}
])
And if u need exact same output you mentioned then add $project stage like
$project:{_id:{company:"$_id",events:"$events"}}

Resources