How to find array greater than document size in MongoDB - node.js

I have schema like below
[
{
id:"111"
tags:[222,333,444,555]
},
{
id: "222"
tags:[312,345,534]
},
{
id:"333"
tags:[111,222,333,444,555]
},
]
I want to find all documents where tags array size is greater than document size returned by $match in aggregation pipeline, so in above Ex. the number of documents are 3 so i want to return all documents having tags array size greater that 3
[
{
id:"111"
tags:[222,333,444,555]
},
{
id:"333"
tags:[111,222,333,444,555]
},
]
I am using aggregation pipeline to process other info, I am stuck at how to have store document size so that i can find all tags greater than document size
below is query which i am using, i want to do it in aggregation and in one call
.aggregate([
{
"$match":{
"ids":{
"$in":[
"111",
"222",
"333"
]
}
}
})]

Facet helps you to solve this problem.
$facet helps to categorize the incoming documents. We use totalDoc for counting the document and allDocuments for getting all the documents
$arrayElemAt helps to get the first object from totalDoc where we already know that only one object should be inside the totalDoc. Because when we group it, we use _id:null
$unwind helps to de-structure the allDocuments array
Here is the code
db.collection.aggregate([
{
$facet: {
totalDoc: [
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
],
allDocuments: [
{
$project: {
tags: 1
}
}
]
}
},
{
$addFields: {
totalDoc: {
"$arrayElemAt": [
"$totalDoc",
0
]
}
}
},
{
$unwind: "$allDocuments"
},
{
$addFields: {
sizeGtDoc: {
$gt: [
{
$size: "$allDocuments.tags"
},
"$totalDoc.count"
]
}
}
},
{
$match: {
sizeGtDoc: true
}
},
{
"$replaceRoot": {
"newRoot": "$allDocuments"
}
}
])
Working Mongo playground

You can try,
$match you condition
$group by null and make root array of documents and get count of root documents in count
$unwind deconstruct root array
$match tags size and count greater than or not using $expr expression match
$replaceRoot to replace root object in root
db.collection.aggregate([
{ $match: { id: { $in: ["111", "222", "333"] } } },
{
$group: {
_id: null,
root: { $push: "$$ROOT" },
count: { $sum: 1 }
}
},
{ $unwind: "$root" },
{ $match: { $expr: { $gt: [{ $size: "$root.tags" }, "$count"] } } },
{ $replaceRoot: { newRoot: "$root" } }
])
Playground
Second option:
first 2 stages $match and $group both are same as like above query,
$project to filter root array match condition if tags size and count greater than or not, this will return filtered root array
$unwind deconstruct root array
$replaceRoot replace root object to root
db.collection.aggregate([
{ $match: { id: { $in: ["111", "222", "333"] } } },
{
$group: {
_id: null,
root: { $push: "$$ROOT" },
count: { $sum: 1 }
}
},
{
$project: {
root: {
$filter: {
input: "$root",
cond: { $gt: [{ $size: "$$this.tags" }, "$count"] }
}
}
}
},
{ $unwind: "$root" },
{ $replaceRoot: { newRoot: "$root" } }
])
Playground
You can skip $unwind and $replaceRoot stages if you want because this query always return one document in root, so you can easily access like this result[0]['root'], you can save 2 stages processing and execution time.

You could use $facet to get two streams i.e. one with the filtered documents and the counts using $count. The resulting streams can then
be aggregated further with a $filter as follows to get the desired result
db.getCollection('collection').aggregate([
{ '$facet': {
'counts': [
{ '$match': { 'id': { '$in': ['111', '222', '333'] } } },
{ '$count': "numberOfMatches" }
],
'docs': [
{ '$match': { 'id': { '$in': ['111', '222', '333'] } } },
]
} },
{ '$project': {
'result': {
'$filter': {
'input': '$docs',
'cond': {
'$gt': [
{ '$size': '$$this.tags' },
{ '$arrayElemAt': ['$counts.numberOfMatches', 0] }
]
}
}
}
} }
])

Related

select single document with minimum value $Min mongodb

I have made several efforts to select a single specific document that contains the minimum value from the database.
let Lowestdate = await BTCMongo.aggregate(
[
// { "$sort": { "name": 1,
{
$match : { "createdAt" : { $gte: new Date(last),$lte: new Date(NEW) } } },
{
$group:
{
_id:null,
minFee_doc:{$min: "$$ROOT"},
minFee: { $min:{$toDouble:"$one"}},
firstFee: { $first: "$one" },
lastFee: { $last: "$one" },
maxFee: { $max: {$toDouble:"$one"}},
}
},
]
).then(result => {}):
with minFee_doc:{$min: "$$ROOT"}, I have been trying to return the document containing the minimum $min but it keeps returning document containing $first
How do i select the document with minimum value?
Note : i will like to return the whole document including the "CreatedAt" "UpdatedAt", and _id. of the document containing the minimum value
Expected Result should look like:
{
"minFee_doc": {
"_id": "61e84c9f622642463640e05c",
"createdAt": "2022-01-19T17:38:39.034Z",
"updatedAt": "2022-04-24T14:48:38.100Z",
"__v": 0,
"one": 2
},
"minFee": 2,
"firstFee": 3,
"lastFee": 5,
"maxFee": 6
}
Edit: also to provide a single document not multiple
$push all docs in $group then $set the array with $filter
db.collection.aggregate([
{
$match: {}
},
{
$group: {
_id: null,
minFee_doc: { $push: "$$ROOT" },
minFee: { $min: { $toDouble: "$one" } },
firstFee: { $first: "$one" },
lastFee: { $last: "$one" },
maxFee: { $max: { $toDouble: "$one" } }
}
},
{
$set: {
minFee_doc: {
$filter: {
input: "$minFee_doc",
as: "m",
cond: { "$eq": [ "$$m.one", "$minFee" ] }
}
}
}
}
])
mongoplayground

How to find the latest date in nested array of objects (MongoDB)

I am trying to find the latest "order" in "orders" array in the whole collection (Not only in the one object).
Data:
[
{
_id: 1,
orders: [
{
title: 'Burger',
date: {
$date: '2021-07-18T13:12:08.717Z',
},
},
],
},
{
_id: 2,
orders: [
{
title: 'Salad',
date: {
$date: '2021-07-18T13:35:01.586Z',
},
},
],
},
];
Code:
var restaurant = await Restaurant.findOne({
'orders.date': 1,
});
Rather simple:
db.collection.aggregate([
{ $project: { latest_order: { $max: "$orders.date" } } }
])
If you like to get the full order use this:
db.collection.aggregate([
{
$project: {
latest_order: {
$first: {
$filter: {
input: "$orders",
cond: { $eq: [ "$$this.date", { $max: "$orders.date" } ] }
}
}
}
}
},
{ $sort: { "latest_order.date": 1 } },
{ $limit: 1 }
])
Mongo Playground
You have to use aggregation for that
db.collection.aggregate([
{ $unwind: "$orders" },
{ $sort: { "orders.date": -1 } },
{ $limit: 1 },
{
"$group": {
"_id": "$_id",
"orders": { "$first": "$orders" }
}
}
])
Working Mongo playground

How to count filtered results in $match in mongodb?

I have an aggregation query like this
nSkip=4, count=25, sortOn='first_name' , order=-1, toMatch='biker' // all variables are dynamic
query={status: true, roles: { $regex: toMatchRole, $options: "m" }} // also dynamic
User.aggregate([
{
$match: query
},
// after this I need the total number of documents that matched the criteria,
// before sorting or skipping or limiting in "total_count" variable
{
$sort: {
[sortOn]: order
}
},
{
$skip: nSkip
},
{
$limit: count
},
{
$project: {
last_name: 1,
first_name: 1,
email: 1
}
}
])
User Collection
{
_id:60befdcfa4198332b728f9cd",
status:false,
roles:["biker"],
email:"john#textmercato.com",
last_name:"aggr",
first_name:"john",
}
I am not sure how to achieve this without disturbing the rest of the stages in aggregation. Can someone please help me out.
You can use $group
{
"$group": {
"_id": null,
"data": { "$push": "$$ROOT" },
"total_count": { $sum: 1 }
}
},
{ $unwind: "$data" },
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [ "$$ROOT", "$data" ]
}
}
}
and finally Project the total_count
Working Mongo playground

Return total fields count in mongodb

Is it possible to retrieve the total fields count with respect to fields name in a single query from a collection?
I am expecting a result like this
{
"field_1":101,
"field_2":93,
"field_3":1
}
You can try,
$project convert root object to array using $objectToArray, this will create k(key) and v(value) structure of array
$unwind deconstruct root array
$group by k(key) and get total fields count using $sum
$group by null and construct an array with k(key) and v(value) fields format
$replaceRoot to replace object after converting from array using $arrayToObject
db.collection.aggregate([
{ $project: { root: { $objectToArray: "$$ROOT" } } },
{ $unwind: "$root" },
{
$group: {
_id: "$root.k",
total: { $sum: 1 }
}
},
{
$group: {
_id: null,
root: {
$push: { k: "$_id", v: "$total" }
}
}
},
{
$replaceRoot: {
newRoot: {
$arrayToObject: "$root"
}
}
}
])
Playground
Yes
Playground
db.collection.aggregate({
$group: {
_id: null,
field_2: {
$push: {
$cond: [
"$field_2",
1,
0
]
}
},
field_1: {
$push: {
$cond: [
"$field_1",
1,
0
]
}
}
}
},
{
$project: {
_id:0,
field_1: {
$sum: "$field_1"
},
field_2: {
$sum: "$field_2"
}
}
})
Be aware:
When is true, $exists matches the documents that contain the field, including documents where the field value is null

Retrieving different aggregated fields with mongoose

I am trying to wrap my head around the query which I am trying to make with mongoose on Node JS. Here is my dataset:
{"_id":{"$oid":"5e49c389e3c23a1da881c1c9"},"name":"New York","good_incidents":{"$numberInt":"50"},"salary":{"$numberInt":"50000"},"bad_incidents":"30"}
{"_id":{"$oid":"5e49c3bbe3c23a1da881c1ca"},"name":"Cairo","bad_incidents":{"$numberInt":"59"},"salary":{"$numberInt":"15000"}}
{"_id":{"$oid":"5e49c42de3c23a1da881c1cb"},"name":"Berlin","incidents":{"$numberInt":"30"},"bad_incidents":"15","salary":{"$numberInt":"55000"}}
{"_id":{"$oid":"5e49c58ee3c23a1da881c1cc"},"name":"New York","good_incidents":{"$numberInt":"15"},"salary":{"$numberInt":"56500"}}
What I am trying to do is get these values:
The most repeated city in collection
The average of bad_incidents
The maximum value of good_incidents
Maximum salary where there are no bad_incidents
I am trying to wrap my head around how I can do this in one query, because I only need one value per field. I would be glad if somebody would lead me on the right track. No need for full solution
Regards!
You may perform MongoDB aggregation with $facet operator which allows compute several aggregation at once.
db.collection.aggregate([
{
$facet: {
repeated_city: [
{
$group: {
_id: "$name",
name: {
$first: "$name"
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
$gt: 1
}
}
},
{
$sort: {
count: -1
}
},
{
$limit: 1
}
],
bad_incidents: [
{
$group: {
_id: null,
avg_bad_incidents: {
$avg: {
$toInt: "$bad_incidents"
}
}
}
}
],
good_incidents: [
{
$group: {
_id: null,
max_good_incidents: {
$max: {
$toInt: "$good_incidents"
}
}
}
}
],
max_salary: [
{
$match: {
bad_incidents: {
$exists: false
}
}
},
{
$group: {
_id: null,
max_salary: {
$max: {
$toInt: "$salary"
}
}
}
}
]
}
},
{
$replaceWith: {
$mergeObjects: [
{
$arrayElemAt: [
"$repeated_city",
0
]
},
{
$arrayElemAt: [
"$bad_incidents",
0
]
},
{
$arrayElemAt: [
"$good_incidents",
0
]
},
{
$arrayElemAt: [
"$max_salary",
0
]
}
]
}
}
])
MongoPlayground
[
{
"_id": null,
"avg_bad_incidents": 34.666666666666664,
"count": 2,
"max_good_incidents": 50,
"max_salary": 56500,
"name": "New York"
}
]

Resources