I have a document similar like this in mongodb with collection name: "movie" :-
collection = movie
[{
"_id": {
"$oid": "573a1390f29313caabcd446f"
},
"imdb": {
"rating": 6.6,
"votes": 1375,
"id": 832
},
"review": {
"rating": 6.6,
"votes": 1375,
"id": 832
}
},
{
"_id": {
"$oid": "573a1390f29313caabcd45f7"
},
"imdb": {
"rating": 4.6,
"votes": 1375,
"id": 832
},
"review": {
"rating": 8,
"votes": 432,
"id": 322
}
}]
How can I make a pipeline to group and count the number of based on the
average of rating in review?
You can use $facet and $bucket for that:
db.movie.aggregate( [
{
$facet: {
"review_rating": [
{
$bucket: {
groupBy: "$review.rating",
boundaries: [0, 3, 5, 10], // Boundaries for the buckets
default: "Other", // In case some data do not fall into a bucket
output: { // Output for each bucket
"count": { $sum: 1 }
}
}
}
],
"imbd_rating": [
{
$bucket: {
groupBy: "$imdb.rating",
boundaries: [0, 3, 5, 10], // Boundaries for the buckets
default: "Other", // In case some data do not fall into a bucket
output: { // Output for each bucket
"count": { $sum: 1 }
}
}
}
]
}
}
])
That should count the number of elements of each bucket for both review.rating and imdb.rating.
If you need more information, visit $facet in MongoDB Docs.
Related
I have a collection of this kind of document:
{
"_id": {
"$oid": "6047b855a6ce7d24a4cda693"
},
"address": "Bergamo, Via S. Bernardino, 57",
"products": [{
"productId": "6047b855a6ce7d24a4cda694",
"qty": 7,
"alarmTh": 5
}, {
"productId": "6047b855a6ce7d24a4cda695",
"qty": 5,
"alarmTh": 5
}],
"_class": "warehouseservice.team2.warehouseservice.models.Warehouse"
}
I need to find a product with a specific productId. Let's say that I want the object with Id = 6047b855a6ce7d24a4cda694, the result that I want is the following:
{
"productId": "6047b855a6ce7d24a4cda694",
"qty": 7,
"alarmTh": 5
}
I tried with this:
db.warehouse.find({"products.productId": "6047b855a6ce7d24a4cda694"}, {"products.$": 1, _id: 0})
but this returns:
{ products: [ { productId: '6047b855a6ce7d24a4cda694', qty: 7, alarmTh: 5 } ] }
so an array, but I want only one single object.
Thanks
You can use the aggregation pipeline and $replaceRoot:
db.collection.aggregate([
{
"$match": {
"products.productId": "6047b855a6ce7d24a4cda694"
}
},
{
"$project": {
products: {
"$first": "$products"
}
}
},
{
"$replaceRoot": {
"newRoot": "$products"
}
}
]);
Output:
[
{
"alarmTh": 5,
"productId": "6047b855a6ce7d24a4cda694",
"qty": 7
}
]
I have this document with this structure
{
"itemA": {
"subItemA": {
"20200104": [
{
"item": 1,
"item2": 2,
"item3": 3
},
{
"item": 4,
"item2": 5,
"item3": 6
},
{
"item": 7,
"item2": 8,
"item3": 9
}
]
}
}
}
Let's say I know the key of itemA, subItemA, and 20200104 and i want to retrieve the data so it will look like this:
[
{
"item": 1,
"item2": 2,
"item3": 3
},
{
"item": 4,
"item2": 5,
"item3": 6
},
{
"item": 7,
"item2": 8,
"item3": 9
}
]
This may be possible if I use JavaScript function (like db.data.find().forEach(function () ....) but is it possible to only use the "native query"?
First $unwind the array then use $replaceRoot
db.collection.aggregate([
{
$unwind: "$itemA.subItemA.20200104"
},
{
$replaceRoot: {
newRoot: "$itemA.subItemA.20200104"
}
}
])
I want to find all elements matching product "bat". My structure for database is as follows
[
{
"key": 1,
"productArray" : [
{
"requirementId": 5,
"product": "bat"
},
{
"requirementId": 6,
"product": "Pen"
},
]
},
{
"key": 2
},
{
"key": 3,
"productArray": [
{
"requirementId": 1,
"product": "bat"
},
{
"requirementId": 2,
"product": "Pen"
},
{
"requirementId": 3,
"product": "bat"
},
{
"requirementId": 4,
"product": "bat"
}
]
}
]
I have tried the following query but this query is returning only one matching element.
db.collection.find({"key": 3}, {"productArray": {"$elemMatch": { "product": "bat"}}})
result of above query is as follows
[
{
"_id": ObjectId("5a934e000102030405000002"),
"productArray": [
{
"product": "bat",
"requirementId": 1
}
]
}
]
Can I get expected output for my problem is as follows using mongodb query Or should I use another approach for my case:
My expected output is as follows
[
{
"productArray": [
{
"requirementId": 1,
"product": "bat"
},
{
"requirementId": 3,
"product": "bat"
},
{
"requirementId": 4,
"product": "bat"
}
]
}
]
As you found, $elemMatch but also $ are lazy operators and return the first element that matches.
You could add a pipeline in find (mongoDB 4.4+) but aggregation is better supported:
db.collection.aggregate({
$match: {
key: 3
}
},
{
$project: {
productArray: {
"$filter": {
"input": "$productArray",
"as": "p",
"cond": {
$eq: [
"$$p.product",
"bat"
]
}
}
}
}
})
Live version
{
"userid": 1,
"following": [2, 3],
"salary": 1000,
"age": 19
},
{
"userid": 2,
"following": [],
"salary": 2000,
"age": 25
},
{
"userid": 3,
"following": [2],
"salary": 1500,
"age": 20
},
"following" shows the userid of the people the user follows.
I would like to have an output query showing the userid and the salary of those that he follows only (no need age and the user's salary). I saw that $lookup() could be used but I am unsure of using it.
My desired output is shown below, may I know if it is possible? I am also open to any other outputs. Thank you in advance!
{
"userid": 1,
"following": [{2, 2000}, {3, 1500}]
},
{
"userid": 2,
"following": []
},
{
"userid": 3,
"following": [{2, 2000}]
}
You can use graphLookup to join the collection to itself, then use project stage to exclude unwanted fields like this:
db.collection.aggregate([
{
$graphLookup: {
from: "collection",
startWith: "$following",
connectFromField: "following",
connectToField: "userid",
as: "following",
maxDepth: 0
}
},
{
$project: {
"_id": 0,
"userid": 1,
"following.salary": 1,
"following.userid": 1
}
}
])
The output is not the same as you wanted, but as you said you are open to all suggestions I posted:
[
{
"following": [
{
"salary": 2000,
"userid": 2
},
{
"salary": 1500,
"userid": 3
}
],
"userid": 1
},
{
"following": [],
"userid": 2
},
{
"following": [
{
"salary": 2000,
"userid": 2
}
],
"userid": 3
}
]
Playground:
https://mongoplayground.net/p/WnF1vRY5Avr
I'm currently using aggregation to display registration teams and nets for a stats page. I can do the count on each level, but the calculations for nets is inaccurate. My aggregation is as follows:
module.exports.registrationStats = function(req, res) {
Registration.aggregate([
{
"$group": {
"_id": {
"day": "$day",
"group": "$group",
"division": "$division",
"level": "$level"
},
"count": { "$sum": 1 }
}
},
{
"$group": {
"_id": {
"day": "$_id.day",
"group": "$_id.group",
"division": "$_id.division"
},
"count": { "$sum": "$count" },
"levels": {
"$push": {
"level": "$_id.level",
"teams": "$count",
"nets" : {$ceil : { $divide: [ "$count" , 5 ] } }
}
}
}
},
{
"$group": {
"_id": {
"day": "$_id.day",
"group": "$_id.group"
},
"count": { "$sum": "$count" },
"divisions": {
"$push": {
"division": "$_id.division",
"count": "$count",
"levels": "$levels"
}
}
}
}
]).exec(function(err, regStats){
if(err) {
console.log("Error grouping registrations");
res.status(500).json(err);
} else {
console.log("Found and grouped " + regStats.length + " regStats");
res.json(regStats);
}
});
};
This gives me the following as an output:
[
{
"_id": {
"day": "Saturday",
"group": "nonpro"
},
"count": 144,
"divisions": [
{
"division": "Men's",
"count": 69,
"levels": [
{
"level": "BB",
"teams": 30,
"nets": 6
},
{
"level": "A",
"teams": 8,
"nets": 2
},
{
"level": "B",
"teams": 19,
"nets": 4
},
{
"level": "AA",
"teams": 12,
"nets": 3
}
]
},
{
"division": "Women's",
"count": 75,
"levels": [
{
"level": "AA",
"teams": 9,
"nets": 2
},
{
"level": "BB",
"teams": 16,
"nets": 4
},
{
"level": "B",
"teams": 18,
"nets": 4
},
{
"level": "A",
"teams": 32,
"nets": 7
}
]
}
]
}
]
The problem is that I cannot just run a ceil filter on the Math.ceil(divisions.count/5) to get a value for divisions.nets or Math.ceil(_id.count/5) to get _id.nets because they are wrong in some cases.
I need to be able to total divisions.levels.nets and push that into divisions.nets and add divisions.nets and put that value into _id.nets so the calculations work properly.
Any ideas on how to do this?
Something like this. Add $project stage to calculate the nets for level followed by $sum & $push nets for rest of $groups
Registration.aggregate([
{
"$group": {
"_id": {
"day": "$day",
"group": "$group",
"division": "$division",
"level": "$level"
},
"count": { "$sum": 1 }
}
},
{ $project: { count:1, nets: { $ceil : { $divide: [ "$count" , 5 ] } } } },
{
"$group": {
"_id": {
"day": "$_id.day",
"group": "$_id.group",
"division": "$_id.division"
},
"count": { "$sum": "$count" },
"nets": { "$sum": "$nets" },
"levels": {
"$push": {
"level": "$_id.level",
"teams": "$count",
"nets" : "$nets"
}
}
}
},
{
"$group": {
"_id": {
"day": "$_id.day",
"group": "$_id.group"
},
"count": { "$sum": "$count" },
"nets": { "$sum": "$nets" },
"divisions": {
"$push": {
"division": "$_id.division",
"count": "$count",
"nets:": "$nets",
"levels": "$levels"
}
}
}
}
])