I have a mongodb collection that has the following schema
{
_id: ,
data: {
1: {
values: ...
},
3: {
values: ...
}
2: {
values: ...
}
}
}
and I wanted to know if it would be possible to sort the items inside the data object or pick the highest values using core mongodb instead of retrieving them and sorting using javascript.
Please read #NeilLunn's comments to the question first
The "possible" yet "impractical" pipeline could be like following:
db.collection.aggregate([
{$project: {
data: { $objectToArray: "$data" }
}},
{$unwind: "$data"},
{$sort: {_id:1, "data.k":-1}},
{$group: {_id:"$_id", data:{$first:"$data"}}},
{$group: {_id:"$_id", data:{$push:"$data"}}},
{$project: {
_id: 1,
data: { $arrayToObject: "$data" }
}}
])
Related
I'd like to sort a collection, then add a virtual property to the result which is their numerical order in which the results where displayed as.
So for example, we have a collection called calls, and we'd like to ascertain the current call queue priority as a number so it can be synced to our CRM via reverse ETL.
We have to do this inside of the query itself because we don't have an intermediary step where we can introduce any logic to determine this logic.
So my current query is
db.getCollection('callqueues').aggregate([
{
$match: {
'invalidated': false,
'assigned_agent': null
}
},
{ $sort: {
score: -1, _id: -1
} },
{
$addFields: {
order: "<NEW ORDER PROPERTY HERE>",
}
},
])
So I was wondering how would I insert as a virtual property their order, where the first element after the sort should be 1, second 2, etc
One option (since mongoDB version 5.0) is to use $setWindowFields for this:
db.collection.aggregate([
{$match: {invalidated: false, assigned_agent: null}},
{$setWindowFields: {
sortBy: {score: -1, _id: -1},
output: {
order: {
$sum: 1,
window: {documents: ["unbounded", "current"]}
}
}
}}
])
See how it works on the playground example
EDIT: If your mongoDB version is earlier than 5.0, you can use a less efficient query, involving $group and $unwind:
db.collection.aggregate([
{$match: {invalidated: false, assigned_agent: null}},
{$sort: {score: -1, _id: -1}},
{$group: {_id: 0, data: {$push: "$$ROOT"}}},
{$unwind: {path: "$data", includeArrayIndex: "order"}},
{$replaceRoot: {newRoot: {$mergeObjects: ["$data", {order: {$add: ["$order", 1]}}]}}}
])
See how it works on the playground example < 5.0
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'
}}]
I'm trying to get all the documents from a collection that have a variable amount of failed exams.
My collection is the following:
I have to retrieve all student that have for example 3 scores lower than 10.
The query I am currently running is the following:
Student.aggregate([
{
$project: {
_id: 0,
name: 1,
students: {
count: {
$size: {
$filter: {
input: "$results",
as: "result",
cond: {$lt: ["$$result.score", 10]}
}
}
}
}
}
}
])
How would I check if the count is $gte then for example 3?
My current output:
You are almost 100% done already! Just add
{$match: {"students.count": {$gte: 3}}
as a stage in the pipeline after $project.
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
I am having a problem sorting an array in a mongodb aggregation pipeline. I couldn't find an answer in stackoverflow so am posting my problem
I have some data in mongodb which has this structure
{
testOne:{ scores:[1, 5, 8]},
testTwo:{scores:[3, 4, 5]},
testThree:{scores:[9,0,1]},
........
testFifty:{scores:[0,8,1]}
}
Basically a series of many tests and the scores from those tests (this is a simplified example to illustrate the problem).
After a few hours banging my head against a wall I found I could only unwind two arrays, any more and no data was returned.
So I couldn't unwind all my arrays and group to get arrays of each test scores from the db
db.collection.aggregate([
{
{$unwind: '$testOne.scores'},
{$unwind: '$testTwo.scores'},
{$unwind: '$testThree.scores'},
{$group:{
_id: {},
concatTestOne: { $push: "$testOne.scores"},
concatTestTwo: { $push: "$testTwo.scores"},
concatTestThree: { $push: "$testThree.scores"}
}
}
To my frustration , even if I just did one unwind the resulting array did not sort properly see below:
db.collection.aggregate([
{
{$unwind: '$testOne.scores'},
{$group:{
_id: {},
concatTestOne: { $push: "$testOne.scores"}
}
{$sort:{concatTestOne: 1}},
}
The result was [1,5,8,3,4,5,....,9,0,1]. No sorting
So to get all the tests scores I used reduce to flatten the nested arrays resulting from the grouping stage with no 'unwinding' e.g.:
db.collection.aggregate([
{
{$group:{
_id: {},
concatTestOne: { $push: "$testOne.scores"}
},
{$addFields:{
testOneScores: {$reduce: {
input: "$concatTestOne",
initialValue: [ ],
in: { $concatArrays: [ "$$value", "$$this" ] }
}
}
}
Once again the resulting arrays do not sort. Can anyone tell me what I am doing wrong. The arrays are large (length approx 3500) is it just mongoDB aggregation doesn't handle large arrays ?
Many thanks for any comments I have spent a lot of time trying to sort my arrays and noting works so far
In my understanding, this is your needed query, otherwise please update your sample output.
db.col.aggregate([
{$unwind: '$testOne.scores'},
{$unwind: '$testTwo.scores'},
{$unwind: '$testThree.scores'},
{"$sort" : {"testOne.scores" : -1,"testTwo.scores" : -1,"testThree.scores" : -1}},
{$group:{
_id: {},
concatTestOne: { $addToSet: "$testOne.scores"},
concatTestTwo: { $addToSet: "$testTwo.scores"},
concatTestThree: { $addToSet: "$testThree.scores"}
}
}
])