My document looks like the following (ignore timepoints for this question):
{
"_id": "xyz-800",
"site": "xyz",
"user": 800,
"timepoints": [
{"timepoint": 0, "a": 1500, "b": 700},
{"timepoint": 2, "a": 1000, "b": 200},
{"timepoint": 4, "a": 3500, "b": 1500}
],
"groupings": [
{"type": "MNO", "group": "<10%", "raw": "1"},
{"type": "IJK", "group": "Moderate", "raw": "23"}
]
}
Can I flatten (maybe not the right term) so the groupings are in a single document. I would like the result to look like:
{
"id": "xyz-800",
"site": "xyz",
"user": 800,
"mnoGroup": "<10%",
"mnoRaw": "1",
"ijkGroup": "Moderate",
"ijkRaw": "23"
}
In reality I would like the mnoGroup and mnoRaw attributes to be created no matter if the attribute groupings.type = "MNO" exists or not. Same with the ijk attributes.
You can use $arrayElemAt to read the groupings array by index in the first project stage and $ifNull to project optional values in the final project stage. Litte verbose, but'll see what I can do.
db.groupmore.aggregate({
"$project": {
_id: 1,
site: 1,
user: 1,
mnoGroup: {
$arrayElemAt: ["$groupings", 0]
},
ijkGroup: {
$arrayElemAt: ["$groupings", -1]
}
}
}, {
"$project": {
_id: 1,
site: 1,
user: 1,
mnoGroup: {
$ifNull: ["$mnoGroup.group", "Unspecified"]
},
mnoRaw: {
$ifNull: ["$mnoGroup.raw", "Unspecified"]
},
ijkGroup: {
$ifNull: ["$ijkGroup.group", "Unspecified"]
},
ijkRaw: {
$ifNull: ["$ijkGroup.raw", "Unspecified"]
}
}
})
Sample Output
{ "_id" : "xyz-800", "site" : "xyz", "user" : 800, "mnoGroup" : "<10%", "mnoRaw" : "1", "ijkGroup" : "Moderate", "ijkRaw" : "23" }
{ "_id" : "ert-600", "site" : "ert", "user" : 8600, "mnoGroup" : "Unspecified", "mnoRaw" : "Unspecified", "ijkGroup" : "Unspecified", "ijkRaw" : "Unspecified" }
Related
I am working on a database of various games and i want to design a query that returns top scorer from each game with specific player details.
The document structure is as follows:
db.gaming_system.insertMany(
[
{
"_id": "01",
"name": "GTA 5",
"high_scores": [
{
"hs_id": 1,
"name": "Harry",
"score": 6969
},
{
"hs_id": 2,
"name": "Simon",
"score": 8574
},
{
"hs_id": 3,
"name": "Ethan",
"score": 4261
}
]
},
{
"_id": "02",
"name": "Among Us",
"high_scores": [
{
"hs_id": 1,
"name": "Harry",
"score": 926
},
{
"hs_id": 2,
"name": "Simon",
"score": 741
},
{
"hs_id": 3,
"name": "Ethan",
"score": 841
}
]
}
]
)
I have created a query using aggregate which returns the name of game and the highest score for that game as follows
db.gaming_system.aggregate(
{ "$project": { "maximumscore": { "$max": "$high_scores.score" }, name:1 } },
{ "$group": { "_id": "$_id", Name: { $first: "$name" }, "Highest_Score": { "$max": "$maximumscore" } } },
{ "$sort" : { "_id":1 } }
)
The output from my query is as follows:
{ "_id" : "01", "Name" : "GTA 5", "Highest_Score" : 8574 }
{ "_id" : "02", "Name" : "Among Us", "Highest_Score" : 926 }
I want to generate output which also provides the name of player and "hs_id" of that player who has the highest score for each game as follows:
{ "_id" : "01", "Name" : "GTA 5", "Top_Scorer" : "Simon", "hs_id": 2, "Highest_Score" : 8574 }
{ "_id" : "02", "Name" : "Among Us", "Top_Scorer" : "Harry", "hs_id": 1, "Highest_Score" : 926 }
What should be added to my query using aggregate pipeline?
[
{
$unwind: "$high_scores" //unwind the high_scores, so you can then sort
},
{
$sort: {
"high_scores.score": -1 //sort the high_scores, irrelevant of game, because we are going to group in next stage
}
},
{
//now group them by _id, take the name and top scorer from $first (which is the first in that group as sorted by score in descending order
$group: {
_id: "$_id",
name: {
$first: "$name"
},
Top_Scorer: {
$first: "$high_scores"
}
}
}
]
I have following Mongo Collection.
[
{
"query": "a",
"page": "p1",
"clicks": 10,
"date": "x"
},
{
"query": "b",
"page": "p1",
"clicks": 5,
"date": "x"
},
{
"query": "a",
"page": "p1",
"clicks": 5,
"date": "y"
},
{
"query": "c",
"page": "p2",
"clicks": 2,
"date": "y"
},
]
Output Should be like this :
[
{
"page" : "p1",
"most_clicks_query" : "a",
"sum_of_clicks_for_query" : 15
},
{
"page" : "p2",
"most_clicks_query" : "c",
"sum_of_clicks_for_query" : 2
},
]
Logic to get this Output :
I need the query name that has most clicks for each page with sum of clicks (for that query)
What I ask :
I am hoping to get this result in one aggregation query.
So I am playing with $$ROOT.
In this path, now I am stuck with grouping the $$ROOT (to get sum of clicks for queries).
Can someone guide me a better path to do this?
Here is the aggregation you're looking for:
db.collection.aggregate([
{
"$group": {
"_id": {
"page": "$page",
"query": "$query"
},
"sum_of_clicks_for_query": {
"$sum": "$clicks"
}
}
},
{
"$project": {
"_id": false,
"page": "$_id.page",
"most_clicks_query": "$_id.query",
"sum_of_clicks_for_query": true
}
},
{
$sort: {
"sum_of_clicks_for_query": -1
}
},
{
$group: {
_id: "$page",
group: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$group"
}
}
])
Playground: https://mongoplayground.net/p/Uzk3CuSwVRM
I'm executing a MongoDB query in ExpressJS via Mongoose.
I have documents that look like the below
{
"name": "first",
"spellings": [
"here",
"is",
"you"
],
"keyStageLevel": 4,
"spellingLevel": 2,
"__v": 0
},
{
"name": "second",
"spellings": [
"her",
"is",
"another"
],
"keyStageLevel": 2,
"spellingLevel": 3,
"__v": 0
},
{
"name": "third",
"spellings": [
"snother",
"list"
],
"keyStageLevel": 2,
"spellingLevel": 4,
"__v": 0
}
I would like to have the result of my query returned so that
1) the keyStageLevel are in order and
2) within each keyStageLevel the spellingLevel are shown in order with the details of the document.
keyStageLevel 2
spellingLevel 3
name: "second",
"spellings": [
"her",
"is",
"another"
]
spellingLevel 4
name: "third",
"spellings": [
"snother",
"list"
]
keyStageLevel 4
spellingLevel 2
etc
My code currently runs
var spellings = await Spelling.aggregate([{"$group" : {_id:{keyStageLevel:"$keyStageLevel",spellingLevel:"$spellingLevel"}}} ]);
which retuns
[
{
"_id": {
"keyStageLevel": 2,
"spellingLevel": 4
}
},
{
"_id": {
"keyStageLevel": 2,
"spellingLevel": 3
}
},
{
"_id": {
"keyStageLevel": 5,
"spellingLevel": 1
}
},
{
"_id": {
"keyStageLevel": 4,
"spellingLevel": 2
}
}
]
Many thanks for any help.
What you are mostly after is using $group to accumulate the remaining document data under each "keyStageLevel" this is done using $push. If you want results in specific order then you always need to $sort, being both before and after feeding to a $group stage:
var spellings = await Spelling.aggregate([
{ "$sort": { "keyStageLevel": 1, "spellingLevel": 1 } },
{ "$group" : {
"_id": { "keyStageLevel": "$keyStageLevel" },
"data": {
"$push": {
"spellingLevel": "$spellingLevel",
"name": "$name",
"spellings": "$spellings"
}
}
}},
{ "$sort": { "_id": 1 } }
])
The first $sort ensures the items added via $push are accumulated in that order, and the final ensures that the "output" is actually sorted in the desired order, as $group will likely not always return the grouped keys in any specific order unless you instruct with such a stage.
This will give you output like:
{
"_id" : {
"keyStageLevel" : 2
},
"data" : [
{
"spellingLevel" : 3,
"name" : "second",
"spellings" : [
"her",
"is",
"another"
]
},
{
"spellingLevel" : 4,
"name" : "third",
"spellings" : [
"snother",
"list"
]
}
]
}
{
"_id" : {
"keyStageLevel" : 4
},
"data" : [
{
"spellingLevel" : 2,
"name" : "first",
"spellings" : [
"here",
"is",
"you"
]
}
]
}
I have a mongo collection:
/* 0 */
{
"_id" : ObjectId("51f1fcc08188d3117c6da351"),
"cust_id" : "abc123",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 25,
"items" : [{
"sku" : "ggg",
"qty" : 7,
"price" : 2.5
}, {
"sku" : "ppp",
"qty" : 5,
"price" : 2.5
}]
}
/* 1 */
{
"_id" : ObjectId("51fa1c318188d305fcbf9f9b"),
"cust_id" : "abc123",
"ord_date" : ISODate("2012-10-03T18:30:00Z"),
"status" : "A",
"price" : 27,
"items" : [{
"sku" : "ggg",
"qty" : 7,
"price" : 2.5
}, {
"sku" : "ppp",
"qty" : 5,
"price" : 2.5
}]
}
When I am giving the aggregate query for sorting in ascending order:
db.orders.aggregate([{
"$unwind": "$items"
}, {
"$sort": {
"price": -1
}
}, {
"$match": {}
}, {
"$group": {
"price": {
"$first": "$price"
},
"items": {
"$push": {
"sku": "$items.sku"
}
},
"_id": "$_id"
}
}, {
"$project": {
"_id": 0,
"price": 1,
"items": 1
}
}])
I get result:
{
"result": [{
"price": 25,
"items": [{
"sku": "ggg"
}, {
"sku": "ppp"
}]
}, {
"price": 27,
"items": [{
"sku": "ggg"
}, {
"sku": "ppp"
}]
}]
}
i.e it is sorting in ascending order and vice versa.
Move the $sort after $group, since the previous sort will be lost after grouping.
db.orders.aggregate([{
"$unwind": "$items"
}, {
"$match": {}
}, {
"$group": {
"price": {
"$first": "$price"
},
"items": {
"$push": {
"sku": "$items.sku"
}
},
"_id": "$_id"
}
}, {
"$sort": {
"price": -1
}
}, {
"$project": {
"_id": 0,
"price": 1,
"items": 1
}
}])
For $natural operator, this is the quoted from the doc.
The $natural operator uses the following syntax to return documents in
the order they exist on disk
Long story short, that means the order you see is not necessarily consistent with the order it store in DB.
I have a collection in MongoDB, which is like following:
{
"_id" : "5327010328645530500",
"members" : [
{
"participationCoeff" : 1,
"tweetID" : "5327010328645530500"
},
{
"participationCoeff" : 1,
"tweetID" : "2820402625046999289"
},
{
"participationCoeff" : 0.6666666666666666,
"tweetID" : "6122060484520699114"
},
{
"participationCoeff" : 1,
"tweetID" : "4656669980325872747"
}
]
}
{
"_id" : "2646953848367646922",
"members" : [
{
"participationCoeff" : 1,
"tweetID" : "2646953848367646922"
},
{
"participationCoeff" : 0.75,
"tweetID" : "7750833069621794130"
},
{
"participationCoeff" : 0.5,
"tweetID" : "6271782334664128453"
}
]
}
Basically, collection have clusters, where a cluster has an _id field and a members field. Members field is an array of documents, having following format.
{
"participationCoeff" : 1,
"tweetID" : "5327010328645530500"
}
Now, from time to time, I have to delete these sub-documents in members attribute of cluster document, by matching tweetID.
However, I'm not able to find a query to achieve this effect. Basically, a tweetID will participate in many clusters, and hence will appear in multiple sub-documents of 'members' attribute of various clusters. I want to supply a bulk $pull operation, where I can remove all the sub-documents in all the clusters (i.e. their members attribute) which match on a particular tweetID.
Some help and intuition will be really helpful.
That's exactly what the $pull operator does, so in the shell you could use an update like:
db.clusters.update({},
{$pull: {members: {tweetID: '5327010328645530500'}}},
{multi: true})
Set the multi option so that every document is updated, not just the first one.
This will delete multiple tweets in a single query just pass an array to $in:-
db.clusters.update({},
{$pull: {members: {$in: [ {tweetID: '5327010328645530500'},{"tweetID" : "2820402625046999289"} ] } } },
{multi: true});
The above method doesnt work in mongoose
so for mongoose do:-
db.clusters.update({},
{$pull: {members:[ {tweetID: '5327010328645530500'},{"tweetID" : "2820402625046999289"} ] } });
In the meantime the update method is deprecated. Therefore one possible up-to-date solution to this would be using the updateMany method instead.
db.clusters.updateMany({},
{$pull: {members: {tweetID: '5327010328645530500'}}})
NOTE: Used in "mongoose": "^5.12.13".
As for today June 22nd, 2021, you can use $in and $pull mongodb operators to remove items from an array of documents :
Parent Document :
{
"name": "June Grocery",
"description": "Remember to pick Ephraim's favorit milk",
"createdDate": "2021-06-09T20:17:29.029Z",
"_id": "60c5f64f0041190ad312b419",
"items": [],
"budget": 1500,
"owner": "60a97ea7c4d629866c1d99d1",
}
Documents in Items array :
{
"category": "Fruits",
"bought": false,
"id": "60ada26be8bdbf195887acc1",
"name": "Kiwi",
"price": 0,
"quantity": 1
},
{
"category": "Toiletry",
"bought": false,
"id": "60b92dd67ae0934c8dfce126",
"name": "Toilet Paper",
"price": 0,
"quantity": 1
},
{
"category": "Toiletry",
"bought": false,
"id": "60b92fe97ae0934c8dfce127",
"name": "Toothpaste",
"price": 0,
"quantity": 1
},
{
"category": "Toiletry",
"bought": false,
"id": "60b92ffb7ae0934c8dfce128",
"name": "Mouthwash",
"price": 0,
"quantity": 1
},
{
"category": "Toiletry",
"bought": false,
"id": "60b931fa7ae0934c8dfce12d",
"name": "Body Soap",
"price": 0,
"quantity": 1
},
{
"category": "Fruit",
"bought": false,
"id": "60b9300c7ae0934c8dfce129",
"name": "Banana",
"price": 0,
"quantity": 1
},
{
"category": "Vegetable",
"bought": false,
"id": "60b930347ae0934c8dfce12a",
"name": "Sombe",
"price": 0,
"quantity": 1
},
Query :
MyModel.updateMany(
{ _id: yourDocumentId },
{ $pull: { items: { id: { $in: itemIds } } } },
{ multi: true }
);
Note: ItemIds is an array of ObjectId.
See below :
[
'60ada26be8bdbf195887acc1',
'60b930347ae0934c8dfce12a',
'60b9300c7ae0934c8dfce129'
]