How to aggregate to have the _id as a object property - mongodb

I have the following collection:
{
_id: 1,
type: "Feature",
properties: {
name: "Name 1",
<other properties here>
}
},
{
_id: 2,
type: "Feature",
properties: {
name: "Name 2",
<other properties here>
}
}
How can I write a MongoDB Aggregate query to return the following ?
{
data: {
type: "Feature",
properties: {
_id: 1,
name: "Name 1",
<other properties here>
}
}
},
{
data: {
type: "Feature",
properties: {
_id: 2,
name: "Name 2",
<other properties here>
}
}
}
So, the _id should become an attribute of properties and each document should be returned as an object of data.

Simple structure manipulation will suffice:
db.collection.aggregate([
{
$project: {
data: {
type: "$type",
properties: {
$mergeObjects: [
{_id: "$_id"},
"$properties"
]
}
}
}
}
])

try this
1- If you need only the _id to be inside the properties, and not in data object
db.collection.aggregate([
{
$group: {
_id: "$_id",
type: {
$first: "$type"
},
properties: {
$first: {
_id: "$_id",
name: "$properties.name"
}
}
}
},
{
$project: {
_id: 0,
data: "$$ROOT"
}
},
{
$project: {
"data._id": 0
}
}
])
check this Mongo Playground
2- If it's matter only to get the _id inside the the properties object, and you don't care if it's in data or not
you can use this
db.collection.aggregate([
{
$group: {
_id: "$_id",
type: {
$first: "$type"
},
properties: {
$first: {
_id: "$_id",
name: "$properties.name"
}
}
}
},
{
$project: {
_id: 0,
data: "$$ROOT"
}
}
])
check this Mongo Playground 2

I was unable to utilize your dataset as it was not RFC 8259 formatted.
However assuming your dataset is properly formatted like below. The issued command would be near close provided the fields matched
{
"data":{
"Feature":{
"properties":{
"_id1":[
"object_of_data1",
"object_of_data2",
"object_of_data3"
],
"_id2":[
"object_of_data1",
"object_of_data2",
"object_of_data3"
],
"_id3":[
"object_of_data1",
"object_of_data2",
"object_of_data3"
]
}
}
}
}
I would then use the command:
db.orders.aggregate([
{ $match: { properties: "_id1" } },
{ $group: { _id1: "object_of_data1", "object_of_data2"
"object_of_data3" } } }
])
For further reading please see: Aggregation Commands Comparison

Related

Mongodb- group an array by key

I have an array field (contains objects) in multiple documents, I want to merge the arrays into one array and group the array by object key. I have manage to group the array but I dont know how to group the data. See the code I tried below
const test = await salesModel.aggregate([
{ $unwind: "$items" },
{
$group: {
_id: 0,
data: { $addToSet: '$items' }
},
}
])
Result of the query:
{
_id: 0,
data: [
{
_id: 61435b3c0f773abaf77a367e,
price: 3000,
type: 'service',
sellerId: 61307abca667678553be81cb,
},
{
_id: 613115808330be818abaa613,
price: 788,
type: 'product',
sellerId: 61307abca667678553be81cb,
},
{
_id: 61307c1ea667676078be81cc,
price: 1200,
type: 'product',
sellerId: 61307abca667678553be81cb,
}
]
}
Now I want to group the data array by object key data.sellerId and sum price
Desired Output:
{
data: [
{
sumPrice: 788,
sellerId: 613115808330be818abaa613,
},
{
sumPrice: 1200,
sellerId: 61307abca667678553be81cb,
}
]
}
Extend with the current query and result with:
$unwind: Deconstruct the array field to multiple documents.
$group: Group by data.sellerId to sum ($sum) for data.price.
$group: Group by 0 with $addToSet to combine multiple documents into one document with data.
MongoDB aggregation query
db.collection.aggregate([
{
$unwind: "$data"
},
{
$group: {
_id: {
sellerId: "$data.sellerId"
},
"sumPrice": {
$sum: "$data.price"
}
}
},
{
"$group": {
"_id": 0,
"data": {
$addToSet: {
"sellerId": "$_id.sellerId",
"sumPrice": "$sumPrice"
}
}
}
}
])
Sample Mongo Playground
Output
[
{
"_id": 0,
"data": [
{
"sellerId": ObjectId("61307abca667678553be81cb"),
"sumPrice": 4988
}
]
}
]
If you want to re-write the query, here are the query with sample input.
Input
[
{
items: [
{
_id: ObjectId("61435b3c0f773abaf77a367e"),
price: 3000,
type: "service",
sellerId: ObjectId("61307abca667678553be81cb"),
},
{
_id: ObjectId("613115808330be818abaa613"),
price: 788,
type: "product",
sellerId: ObjectId("61307abca667678553be81cb"),
},
{
_id: ObjectId("61307c1ea667676078be81cc"),
price: 1200,
type: "product",
sellerId: ObjectId("61307abca667678553be81cb"),
}
]
}
]
Mongo aggregation query
db.collection.aggregate([
{
$unwind: "$items"
},
{
$group: {
_id: {
sellerId: "$items.sellerId"
},
"sumPrice": {
$sum: "$items.price"
}
}
},
{
"$group": {
"_id": 0,
"data": {
$addToSet: {
"sellerId": "$_id.sellerId",
"sumPrice": "$sumPrice"
}
}
}
}
])
Sample 2 on Mongo Playground
Output
[
{
"_id": 0,
"data": [
{
"sellerId": ObjectId("61307abca667678553be81cb"),
"sumPrice": 4988
}
]
}
]

clone and rename a field of an array of subdocuments in Mongo

I've got a collection like this:
{
name: "A Name",
answers: [
{order: 1},
{order: 2},
{order: 3}
]
}
What I want to do is to add a new filed id to each element of the answers array based on the value of the order property - I want just to clone it, so the output is
{
name: "A Name",
answers: [
{order: 1, id: 1},
{order: 2, id: 2},
{order: 3, id: 3}
]
}
I looked at this post and this one too, but I don't know how to combine them to work properly for subdocuments.
In MongoDB documentation for the aggregate method, I found a simple example of how to update embedded documents here, but I have no idea how to use the order property instead of a fixed term. The following tries seem not to work as I need:
db.collection.aggregate([
{
$addFields: {
"answers.id": "answers.$.order"
}
}
])
db.collection.aggregate([
{
$addFields: {
"answers.id": "$answers.order"
}
}
])
Is it possible to achieve the expected result with the `aggregate method?
Demo - https://mongoplayground.net/p/M79MV6-Zp4C
db.collection.aggregate([
{
$set: {
answers: {
$map: {
input: "$answers",
as: "answer",
in: { $mergeObjects: [ "$$answer", { id: "$$answer.order" } ]
}
}
}
}
}
])
Updated Demo - https://mongoplayground.net/p/iyqIPGQ5-ld
db.collection.aggregate([
{ $unwind: "$answers" },
{
$group: {
_id: "$_id",
answers: { $push: { order: "$answers.order", id: "$answers.order" } },
name: { $first: "$name" } // preserve properties add them to the group pick 1st value
}
}
])
Demo - https://mongoplayground.net/p/Ln5CcmT-Kkm
db.collection.aggregate([
{ $unwind: "$answers" }, // break into individuals documents
{ $addFields: { "answers.id": "$answers.order" } }, // copy order value to id
{ $group: { _id: "$_id", answers: { $push: "$answers" } } } // join and group it back
])
If you want to sort n get id from index
Demo - https://mongoplayground.net/p/6U_sRYWHtDR
db.collection.aggregate([
{ $sort: { "answers.order": 1 } },
{ $unwind: { path: "$answers", includeArrayIndex: "index" } },
{ $group: { _id: "$_id", answers: { "$push": { order: "$answers.order", id: { $add: [ "$index", 1 ] } } } } }
])

push group result into array mongodb

I have this query in mongodb:
db.getCollection('users').aggregate([
{
$group: {
_id: "$type",
// items: { $push: "$_id.type" }
// items: { $addToSet: "$_id.type" }
}
},
{
$project: {
_id: 0,
type: "$_id",
}
}
]).toArray()
This will return a list like this:
[ {type: "AAA"}, {type: "BBB"}, {type: "CCC"} ]
But can I get something like this: ["AAA", "BBB", "CCC"]? Tried with $push and $addToSet but no result. Thank you for your time!
Later edit:
You can find an example here:
https://mongoplayground.net/p/-n0o5i6CnLq
Like this?
{
$group: {
_id: null,
type: {
$push: "$type"
}
}
},
{
$project: {
_id: 0,
type: 1
}
}
Try this one:
db.collection.aggregate([
{
$group: {
_id: null,
type: {
"$addToSet": "$type"
}
}
}
])

Getting the first created document and grouping by field name in MongoDB

I would like to get the very first department created for each company, however, I'm confused with the aggregate query.
Documents:
[
{
_id: "5b7579f2deea1c6e46fd9739",
name: "Sales",
companyId: "123",
},
{
_id: "5c5779f1dffe1c6e45df3973",
name: "Security",
companyId: "123",
},
{
_id: "5d9759f5ceda1c6e64df9772",
name: "Human Resource",
companyId: "789",
},
]
I'm expecting a result like this:
Expected Result:
[
{
_id: "5b7579f2deea1c6e46fd9739",
name: "Sales",
companyId: "123",
},
{
_id: "5d9759f5ceda1c6e64df9772",
name: "Human Resource",
companyId: "789",
},
]
But I'm getting only one result with my query.
Actual Result:
[
{
_id: "5b7579f2deea1c6e46fd9739",
name: "Sales",
companyId: "123",
},
]
Aggregate Query:
db.getCollection('departments').aggregate([
{
$sort:{ item: 1 }
},
{
$group: {
_id:'$item',
companyId: { $first:'$companyId'},
name: { $first:'$name'},
}
}
])
You need to group by companyId field like this:
db.departments.aggregate([
{
$group: {
_id: "$companyId",
doc: {
$first: "$$ROOT"
}
}
},
{
$replaceRoot: {
newRoot: "$doc"
}
}
])
Playground
If you have a natural sort field like a date field, it would be good to apply sort stage on that field before the group stage.

Mongodb Group stage, and query last two document

In mongodb, if we want to take first or last document of the group stage, then the FIRST and LAST operator will helpful. I want to group the collection based on _id:"$department" and also take the LAST document and the LAST-1 document. Is there any way to achieve this.
Assume you have this collection:
[
{ department: "HR", employees: 20 },
{ department: "Finance", employees: 30 },
{ department: "Sales", employees: 5 },
{ department: "IT", employees: 50 }
]
Then you can run this aggregation:
db.collection.aggregate([
{ $sort: { department: 1 } },
{
$group: {
_id: null,
employees: { $sum: "$employees" },
all_departments: { $push: "$department" }
}
},
{
$set: {
last_departments: {
$concatArrays: [
[{ $arrayElemAt: ["$all_departments", -1] }],
[{ $arrayElemAt: ["$all_departments", -2] }]
]
}
}
}
])
Mongo Playground
Update
With $slice it is even shorter:
db.collection.aggregate([
{ $sort: { department: 1 } },
{
$group: {
_id: null,
employees: { $sum: "$employees" },
all_departments: { $push: "$department" }
}
},
{ $set: { last_departments: { $slice: ["$all_departments", -2] } } }
])