I am using a Mongo aggregated framework, suppose if I am having collection structure like this
{
{
_id: ObjectId(123)
name: john,
sessionDuration: 29
},
{
_id: ObjectId(456)
name: moore,
sessionDuration: 45
},
{
_id: ObjectId(789)
name: cary,
sessionDuration: 25
},
}
I want to query and create a pipeline such that it return something like this:
{
durationsArr: [29, 49, 25, '$sessionDuration_Field_From_Document' ];
}
I am doing this because I want to get average of durations from all the documents, so first adding all of it into an array, then I will add last stage where I do the $avg operation.
Any idea of how can I get the array of sessionDurationField. or do you have any other best approach to calculate the sessionDuration Average from the collection? please thoroughly explain am new to mongo aggregation.
$group - Group all documents.
1.1. $avg - Calculate the average of sessionDuration for all documents.
db.collection.aggregate([
{
$group: {
_id: null,
avgSessionDuration: {
$avg: "$sessionDuration"
}
}
}
])
Demo # Mongo Playground
Related
I am using a Mongo aggregated framework, suppose if I am having collection structure like this
{
{
_id: ObjectId(123)
name: john,
sessionDuration: 29
},
{
_id: ObjectId(456)
name: moore,
sessionDuration: 45
},
{
_id: ObjectId(789)
name: john,
sessionDuration: 25
},
{
_id: ObjectId(910)
name: john,
sessionDuration: 45
},
etc...
}
user with the same name is the one who is using different sessions like in the following example: John is using service from three device with 3 sessions durations are: 2 less than 30 (29,25) and 1 less than 50(45).
I want to do a bucket query for boundaries [0,30,50] but in the range it must only count the user with a unique names, no same name user with less than 30 or 50 duration count more than one, means the result should look like this
{
time: Unique_Name_Users_Only_Lies_In_This_Boundary,
‘30’: 1,
‘50’: 2,
}
so john was having 2 sessions less than 30 duration so we only need 1 from these two.
What I tried:
I group all the docs first with unique name only, then apply bucket. but this approach will also skip the john with 45 sessionDuration.
How can I only get the unique name document count in a particular duration of $bucket boundary?
One option is to use the $bucket with $addToSet and then use $group with $arrayToObject to get your formatting:
db.collection.aggregate([
{$bucket: {
groupBy: "$sessionDuration",
boundaries: [0, 30, 50],
default: "Other",
output: {res: {$addToSet: "$name"}}
}},
{$group: {
_id: 0,
res: {$push: {k: {$toString: "$_id"}, v: {$size: "$res"}}}
}},
{$replaceRoot: {newRoot: {$arrayToObject: "$res"}}}
])
See how it works on the playground example
Notice that the _id of a bucket is its lower boundary. You can manipulate this if you really want, but I don't recommend it
Does mongodb aggragate function support biases or will favor values that are specified, for example i have an array of values for a variable
genre = [science, math, english]
and I want to get (5) random document from the database where the document has a either 1 of the genres in the specified array, I want to get these data from biases since if ever that only 2 documents matched the specified condition, i want the other 3 to be randomized instead, thus completing the 5 random documents that i need.
Heres what i've gotten so far, but it only gets random data without any values
const book = await Book.aggregate([
{
$match: { genre: { $type: "string" } }
},
{
$sample: { size: 6 }
},
{
$set: {
genre: {
$cond: {
if: { $eq: [{ $type: "$genre" }, "string"] },
then: ["$genre"],
else: "$genre"
}
}
}
},
]);
hope you are well. I am trying to make a graph from my collection but I've run into an issue. How can I group the locations and then count the number of locations in the collection? so for the example below I have 5 kids, I want to know all their locations and how many kids share the same locations, (A = 2, B = 2, and C = 1) that way I can plot Location vs the number of kids in that location. So to summarize, what locations are there and how many kids in each location.
"name": "Tom",
"location": "A'
"name": "Sarah",
"location": "B'
"name": "Jane",
"location": "C'
"name": "HIllary",
"location": "A'
"name": "Mat",
"location": "B'
Edit here is my code
router.get('/contact', function (req, res) {
const locations = Kids.aggregate([
{
$group: {
_id: {
continent: "$locations",
},
count: {
$sum: 1,
},
},
}
])
locations.forEach(function(item) {
console.log(`${item._id.province} num of kids is: ${item.count}`);
});
res.render('contact');
});
db.kids.aggregate([ {$group:{_id:"$location" , count:{$sum:1} } } ])
"The logic" (What to do with the values) not related to the steps (Store the values in variables and do "something").
HOW TO (OUTLINE)
Example - Simple countries collection group by continents using compass
(Great to use compass to learn this topic).
Our data (4 countries: Italy, England, Nigeria, Brazil, and their continents).
Step 1/3 - explore your aggregate
Output 3 groups (Europe (count 2), Africa (1), South America (1))
{
_id: {
continent: "$continent"
},
count: {
$sum: 1,
}
}
Step 2/3 - export code
From GUI to code
export
copy paste
Step 3/3 - aggregate() method & cursoer.forEach()
The related code (Assuming you connect a MongoDB instance using node Connection Guide)
const aggregate_cursor = db.collection("test_listing").aggregate([
{
$group: {
_id: {
continent: "$continent",
},
count: {
$sum: 1,
},
},
}
])
And loop throw the cursor:
aggregate_cursor .forEach(function(item) {
console.log(`${item._id.continent} num of countries is: ${item.count}`);
});
Output:
Related docs:
aggregation: https://docs.mongodb.com/manual/reference/operator/aggregation/group/
cursor.forEach: https://docs.mongodb.com/manual/reference/method/cursor.forEach/
Let's say I have three person documents in a MongoDB, inserted in a random order.
{
"firstName": "Hulda",
"lastName": "Lamb",
},
{
"firstName": "Austin",
"lastName": "Todd",
},
{
"firstName": "John",
"lastName": "Doe",
}
My goal is to obtain, let's say, the next person after Austin when the list is in alphabetical order. So I would like to get the person with firstName = Hulda.
We can assume that I know Austin's _id.
My first attempt was to rely on the fact that _id is incremental, but it won't work because the persons can be added in any order in the database. Hulda's _id field has a value less than Austin's. I cannot do something like {_id: {$gt: <Austin's _id here>}};
And I also need to limit the number of returned elements, so N is a dynamic value.
Here is the code I have now, but as I mentioned, the ID trick is not working.
let cursor: any = this.db.collection(collectionName).find({_id: {$gt:
cursor = cursor.sort({firstName: 1});
cursor = cursor.limit(limit);
return cursor.toArray();
Some clarifications:
startId is a valid, existing _id of an object
limit is a variable holding an positive integer value
sorting and limit works as expected, just the selection of the next elements is wrong, so the {_id: {$gt: startId}}; messes up the selection.
Every MongoDB's Aggregation Framework operation's context is restricted to a single document. There's no mechanism like window functions in SQL. Your only way is to use $group to get an array which contains all your documents and then get Austin's index to be able to apply $slice:
db.collection.aggregate([
{
$sort: { firstName: 1 }
},
{
$group: {
_id: null,
docs: { $push: "$$ROOT" }
}
},
{
$project: {
nextNPeople: {
$slice: [ "$docs", { $add: [ { $indexOfArray: [ "$docs.firstName", "Austin" ] }, 1 ] }, 1 ]
}
}
},
{ $unwind: "$nextNPeople" },
{
$replaceRoot: {
newRoot: "$nextNPeople"
}
}
])
Mongo Playground
Depending on your data size / MongoDB performance, above solution may or may not be acceptable - it's up to you to decide if you want to deploy such code on production since $group operation can be pretty heavy.
For example, I have a collection users with the following structure:
{
_id: 1,
name: "John",
from: "Amsterdam"
},
{
_id: 2,
name: "John",
from: "Boston"
},
{
_id: 3,
name: "Mia",
from: "Paris"
},
{
_id: 4,
name: "Kate",
from: "London"
},
{
_id: 5,
name: "Kate",
from: "Moscow"
}
How can I get 3 random documents in which names will not be repeated?
Using the function getFourNumbers(1, 5), I get array with 3 non-repeating numbers and search by _id
var random_nums = getThreeNumbersnumbers(1, 5); // [2,3,1]
users.find({_id: {$in: random_nums}, function (err, data) {...} //[John, Mia, John]
But it can consist two Johns or two Kates, what is unwanted behavior. How can I get three random documents ( [John, Mia, Kate]. Not [John, Kate, Kate] or [John, Mia, John]) with 1 or maximum 2 queries? Kate or John (duplicated names) should be random, but should not be repeated.
There you go - see the comments in the code for further explanation of what the stages do:
users.aggregate(
[
{ // eliminate duplicates based on "name" field and keep track of first document of each group
$group: {
"_id": "$name",
"doc": { $first: "$$ROOT" }
}
},
{
// restore the original document structure
$replaceRoot: {
newRoot: "$doc"
}
},
{
// select 3 random documents from the result
$sample: {
size:3
}
}
])
As always with the aggrgation framework you can run the query with more or less stages added in order to see the transformations step by step.
I think what you are looking for is the $group aggregator, which will give you the distinct value of the collection. It can be used as:
db.users.aggregate( [ { $group : { name : "$name" } } ] );
MongoDB docs: Retrieve Distinct Values