MongoDB - How to select data that has a field equals to the minimum field value - mongodb

I'm new to MongoDB and I want to select all users having the minimum age.
Something like this:
db.users.find({age: {$min: age}})
Seems really basic but I can't find how to do it.

$gorup by age and make array of users
$sort by _id means age in ascending order
$limit 1 document
db.users.aggregate([
{
$group: {
_id: "$age",
users: { $push: "$$ROOT" }
}
},
{ $sort: { _id: 1 } },
{ $limit: 1 }
])
Playground

Related

MongoDB get only the last documents per grouping based on field

I have a collection "TokenBalance" like this holding documents of this structure
{
_id:"SvVV1qdUcxNwSnSgxw6EG125"
balance:Array
address:"0x6262998ced04146fa42253a5c0af90ca02dfd2a3"
timestamp:1648156174658
_created_at:2022-03-24T21:09:34.737+00:00
_updated_at:2022-03-24T21:09:34.737+00:00
}
Each address has multiple documents like of structure above based on timestamps.
So address X can have 1000 objects with different timestamps.
What I want is to only get the last created documents per address but also pass all the document fields into the next stage which is where I am stuck. I don't even know if the way I am grouping is correctly done with the $last operator. I would appreciate some guidance on how to achieve this task.
What I have is this
$group stage (1st stage)
{
_id: '$address',
timestamp: {$last: '$timestamp'}
}
This gives me a result of
_id:"0x6262998ced04146fa42253a5c0af90ca02dfd2a3"
timestamp:1648193827320
But I want the other fields of each document as well so I can further process them.
Questions
1) Is it the correct way to get the last created document per "address" field?
2) How can I get the other fields into the result of that group stage?
Use $denseRank
db.collection.aggregate([
{
$setWindowFields: {
partitionBy: "$address",
sortBy: { timestamp: -1 },
output: { rank: { $denseRank: {} } }
}
},
{
$match: { rank: 1 }
}
])
mongoplayground
I guess you mean this:
{ $group: {
_id: '$address',
timestamp: {$last: '$timestamp'},
data: { $push: "$$ROOT" }
} }
If the latest timestamp is also the last sorted by _id you can use something like this:
[{$group: {
_id: '$_id',
latest: {
$last: '$$ROOT'
}
}}, {$replaceRoot: {
newRoot: '$latest'
}}]

Mongoose remove duplicate social security customer

I have customers who have duplicate SocialSecurity and I want to remove them and keep the newest customer. I am doing this by comparing _id and keeping the one with the largest value. Unfortunately, when I am playing with dumby data, it seems like my code does not always delete the one with the smallest _id. Any idea why? I thought the $sort would work
let hc = db.getSiblingDB('customer');
hc.customers.aggregate([
{
"$group": {
_id: {socialsecurity: "$socialsecurity"},
imeis: { $addToSet: "$_id" },
count: { $sum : 1 }
}
},
{
"$match": {
count: { "$gt": 1 }
}
},
{
$sort: {_id: 1}
}
]).forEach(function(doc) {
doc.socialsecurity.shift();
hc.customers.remove({
_id: {$in: doc.socialsecurity}
});
})
Problem
{ $sort: { _id: 1} } is sorting in ascending order
So smallest will be the 1st _id in the array
doc.socialsecurity.shift(); will remove 1st element from the array that is smallest one.
Solution
{ $sort: { _id: -1 } } sort in descending order
OR
change doc.socialsecurity.shift(); to doc.socialsecurity.pop(); remove the last element from the array.

Count nested wildcard array mongodb query

I have the following data of users and model cars:
[
{
"user_id":"ebebc012-082c-4e7f-889c-755d2679bdab",
"car_1a58db0b-5449-4d2b-a773-ee055a1ab24d":1,
"car_37c04124-cb12-436c-902b-6120f4c51782":0,
"car_b78ddcd0-1136-4f45-8599-3ce8d937911f":1
},
{
"user_id":"f3eb2a61-5416-46ba-bab4-459fbdcc7e29",
"car_1a58db0b-5449-4d2b-a773-ee055a1ab24d":1,
"car_0d15eae9-9585-4f49-a416-46ff56cd3685":1
}
]
I want to see how many users have a car_ with the value 1 using mongodb, something like:
{"car_1a58db0b-5449-4d2b-a773-ee055a1ab24d": 2}
For this example.
The issue is that I will never know how are the fields car_ are going to be, they will have a random structure (wildcard).
Notes:
car_id and user_id are at the same level.
The car_id is not given, I simply want to know for the entire database which are the most commmon cars_ with value 1.
$group by _id and convert root object to array using $objectToArray,
$unwind deconstruct root array
$match filter root.v is 1
$group by root.k and get total count
db.collection.aggregate([
{
$group: {
_id: "$_id",
root: { $first: { $objectToArray: "$$ROOT" } }
}
},
{ $unwind: "$root" },
{ $match: { "root.v": 1 } },
{
$group: {
_id: "$root.k",
count: { $sum: 1 }
}
}
])
Playground

How to perform case-insensitive aggregation grouping in MongoDb?

Let's say that I want to aggregate and group by documents in MongoDb by the Description field.
Running the following (case-sensitive by default):
db['Products'].aggregate(
{ $group: {
_id: { 'Description': "$Description" },
count: { $sum: 1 },
docs: { $push: "$_id" }
}},
{ $match: {
count: { $gt : 1 }
}}
);
on my sample data gives me 1000 results, which is fine.
But now I expect that running a case-insensitive query (using $toLower) should give me less than or equal to 1000 results:
db['Products'].aggregate(
{ $group: {
_id: { 'Description': {$toLower: "$Description"} },
count: { $sum: 1 },
docs: { $push: "$_id" }
}},
{ $match: {
count: { $gt : 1 }
}}
);
But instead I get more than 1000 results. That can't be right, can it? More common entries should get grouped together to yield less number of total groupings ... I think.
So then probably my aggregation query is wrong! Which brings me to my question:
How should case-insensitive aggregation grouping in MongoDb be performed?
You approach to case-insensitive grouping is correct so perhaps your observation is not? ;)
Try this example:
// insert two documents
db.getCollection('test').insertOne({"name" : "Test"}) // uppercase 'T'
db.getCollection('test').insertOne({"name" : "test"}) // lowercase 't'
// perform the grouping
db.getCollection('test').aggregate({ $group: { "_id": { $toLower: "$name" }, "count": { $sum: 1 } } }) // case insensitive
db.getCollection('test').aggregate({ $group: { "_id": "$name", "count": { $sum: 1 } } }) // case sensitive
You may have a typo somewhere?
The documentation also states that
$toLower only has a well-defined behavior for strings of ASCII characters.
Perhaps that's what's biting you here?

MongoDB Group by field, count it and sort it desc

I have the following document structure:
{
..
"mainsubject" : {
"code": 2768,
"name": "Abc"
}
}
Now I need a list of all mainsubject.code's and how often they are used.
In SQL i would do something like this:
SELECT mainsubject_code, COUNT(*) AS 'count'
FROM products
GROUP BY mainsubject_code
ORDER BY count
I already was able to group it and count it:
db.products.aggregate([
{"$group" : {_id:"$mainsubject.code", count:{$sum:1}}}
]);
But how to sort it?
db.coll.aggregate([
{
$group: {
_id: "$mainsubject.code",
countA: { $sum: 1}
}
},
{
$sort:{$mainsubject.code:1}
}
])
did not work?
On looking at your sql query, it looks like you want to sort by count. So in mongo query also you should mention countA as the sort field.
db.coll.aggregate([
{
$group: {
_id: "$mainsubject.code",
countA: { $sum: 1}
}
},
{
$sort:{'countA':1}
}
])
You have to sort by _id field that is the name of the field resulting from the $group stage of your aggregation pipeline. So, modify your query in this way:
db.coll.aggregate([
{
$group: {
_id: "$mainsubject.code",
countA: { $sum: 1}
}
},
{
$sort:{_id:1}
}
])
In this way you're sorting by _id ascending. Your SQL equivalent query is actually sorting by count and to achieve this you can change the $sort stage to:
$sort:{"countA":1}
Use Sort By Count ($sortByCount)
Groups incoming documents based on the value of a specified expression, then computes the count of documents in each distinct group.
db.coll.aggregate([ { $sortByCount: "$mainsubject.code" } ]