Map mongo aggregation result to array - json

I have a mongo query that looks like this:
db.myCollection.aggregate([ {
$group: {
_id: null,
price: {
$sum: "$price"
},
inventory: {
$sum: "$inventory"
}
}
}])
Which returns
{
"_id" : null,
"price" : 26,
"inventory" : 5,
}
I would like a query which would rather return something like this:
[
{
name: "price",
amount: 26
},
{
name: "inventory",
amount: 5
}
]
EDIT:
How would I write this in Java with Spring Data? I can group and sum but don't know how to project it?
Aggregation aggregation = newAggregation(
group("$id")
.sum("price").as("price")
.sum("inventory").as("inventory")
);

You'll need to use $project. It allows us to define which fields will be returned, as well as their format.
db.myCollection.aggregate([{
$group: {
_id: null,
price: {
$sum: "$price"
},
inventory: {
$sum: "$inventory"
}
}
},
{
$project: {
_id: 0 //removes _id from result
something: [
{name: "price", amount: "$price"},
{name: "inventory", amount: "$inventory"}
]
}
}])
That will give you:
{
"something" : [
{
"name" : "price",
"amount" : 26
},
{
"name" : "inventory",
"amount" : 5
}
]
}

You can use below aggregation
db.collection.aggregate([
{ "$project": {
"data": {
"$map": {
"input": { "$objectToArray": "$$ROOT" },
"in": { "name": "$$this.k", "amount": "$$this.v" }
}
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" }},
{ "$match": { "amount": { "$ne": null }}
}
])

Related

why the aggregation query's result is null in mongodb?

why the result is null?
i have this as my collection, the result shoul be contain the product and the average of quantity, match by year.
this is my collection
{
"order":"o1",
"day": ISODate("2020-01-04T00:00:00.000Z"),
"paid":"Y",
"cost": {"price":30, "currency":"USD"},
"items":[{"product":"p1", "colours":["blue","black"], "quantity":15}],
"delivery_days":5
},
{
"order":"o2",
"day": ISODate("2020-01-22T00:00:00.000Z"),
"paid":"Y",
"cost": {"price":13, "currency":"EUR"},
"items":[{"product":"p2","colours":["white"],"quantity":4},
{"product":"p3","colours":["white","black"],"quantity":1}],
"delivery_days":4
},
{
"order":"o3","day": ISODate("2018-10-17T00:00:00.000Z"),
"paid":"N",
"cost":{"price":33,"currency":"EUR"},
"items":[{"product":"p3","colours":["blue","black"],"quantity":4}],
"delivery_days":4
}
this is my code
db.UAS0456.aggregate([
{
$project:
{
order:"$order",
year:{$year:"$day"},
items:"$items",
product:"$item.product",
quantity:"$item.quantity",
}
},
{
$match:
{
"year":{$gte:2018},
"year":{$lte:2020}
}
},
{
$group:
{
_id:"$product",
averageOfQuantity:
{
$avg: "$items.quantity"
}
}
}
]);
and this is the result.
how to get the result? it should be many product.
{ "_id" : null, "averageOfQuantity" : null }
query
db.collection.aggregate([
{
$match: {}
},{
$project: {
year: { $year: '$day' },
items: '$items'
}
}, {
$unwind: {
path: '$items'
}
}, {
$group: {
_id: {
y: '$year',
p: '$items.product'
},
result: {
$avg: '$items.quantity'
}
}
}, {
$project: {
_id: 0,
year: '$_id.y',
product: '$_id.p',
avg: '$result'
}
}
])
result
{
"year" : 2020,"product" : "p1","avg" : Double("15")
},
{
"year" : 2020,"product" : "p2","avg" : Double("4")
},
{
"year" : 2020,"product" : "p3","avg" : Double("1")
},
{
"year" : 2018,"product" : "p3","avg" : Double("4")
}

How to output object of an array in the root of the document in MongoDB using aggregation?

I have this document :
{"_id":"1", "elem":"ok",
"arrayOfObjects":[
{"type":"k","fieldx":"lol"},
{"type":"SndObject","fieldy":"foo"},
{"type":"Object1","fieldx":"bob"}
]
}
what is the aggregation to have this output:
{"_id":"1", "elem":"ok",
"Object1":[
{"type":"Object1","fieldx":"lol"},
{"type":"Object1","fieldx":"bob"}
],
"SndObject":[{"type":"SndObject","fieldy":"foo"}]
}
I found a way out, but it need me to know all the type i have:
{
"$addFields" : {
"Object1" : {
"$filter": {
"input": "$arrayOfObjects",
"as": "types",
"cond": {
"$and": [{ "$eq": [ "$$types.type", "Object1" ] }]
}
}
}
}
}
It would be best if i can loop over my arrayOfObjects and get the same result without pre knowledge of the type.
Might be there would be more easy option than this,
$unwind deconstruct arrayOfObjects array
$group by _id, type and elem, construct array of arrayOfObjects
$arrayToObject convert k and v from array to object
$group by _id and merge objects in root
db.collection.aggregate([
{ $unwind: "$arrayOfObjects" },
{
$group: {
_id: {
type: "$arrayOfObjects.type",
_id: "$_id"
},
elem: { $first: "$elem" },
arrayOfObjects: { $push: "$arrayOfObjects" }
}
},
{
$group: {
_id: "$_id._id",
elem: { $first: "$elem" },
arrayOfObjects: {
$mergeObjects: {
$arrayToObject: [[
{
k: "$_id.type",
v: "$arrayOfObjects"
}
]]
}
}
}
}
])
Playground

Mongoose select an object in Array with specific property

From this 'houses' collection
{
_id: "0",
rooms: [
{
roomName: "living-room"
chairs: "6"
},
{
roomName: "kitchen"
chairs: "0"
}
]
}
I need to find the house with _id = 0, and select only
the 'chairs' from the "living-room" so that the result looks like this:
{
chairs: 6
}
I think of something that looks like this:
House.findOne({_id: '0'}).select('rooms.chairs') // but only from {roomName: "living-room"}
How do I complete the query?
How about this one:
db.houses.aggregate([
{ $match: { _id: "0" } },
{
$project: {
chairs: {
$filter: {
input: "$rooms",
cond: { $eq: ["$$this.roomName", "living-room"] }
}
}
}
},
{
$replaceRoot: {
newRoot: {
chairs: { $arrayElemAt: ["$chairs.chairs", 0] }
}
}
},
])
db.house.findOne({"rooms.chairs":"6"},{"rooms.$":1})

Query to return a field from a nested json using MongoDB

My database has data in the following format :
{ "_id" : ObjectId( "abcd" ),
"coordinate" : [somevalue, somevalue],
"value" : [
{ "time" : 1,
"characteristics" : "pqrs" },
{ "time" : 10,
"characteristics" : "pqrs" } ] }
I want to find the field closest coordinate and a time that is less than or equal to a given value.
Currently I'm using this query :
db.collection.aggregate({
coordinate: {
$geoNear: [latitude, longitude],
$maxDistance: 10
},
"value.time": {
$lte: 5
}
})
This one returned the entire entry, but what I wanted is the field:
{ "time" : 1, "characteristics" : "pqrs" }
Is it even possible to just return this field ? What if there are multiple result and I just want the one that's closest to my value.time input ?
You can perform an aggregation to :
$match items with specified coordinate and value.time
$filter value array to remove everything < 5
$unwind value array
$group by max value
Query is :
db.collection.aggregate({
$match: {
coordinate: {
$geoNear: [0, 0],
$maxDistance: 10
},
"value.time": {
$lte: 5
}
}
}, {
$project: {
value: {
$filter: {
input: "$value",
as: "value",
cond: { $lte: ["$$value.time", 5] }
}
}
}
}, {
$unwind: "$value"
}, {
$group: {
_id: "$_id",
time: { $max: "$value.time" },
characteristics: { $first: "$value.characteristics" }
}
})
Sample output :
{ "_id" : ObjectId("588a8c3080a14de2d654eb7b"), "time" : 4, "characteristics" : "pqrs" }
{ "_id" : ObjectId("588a89327fe89686fd2210b2"), "time" : 1, "characteristics" : "pqrs" }

how to use aggregate for group and count in mongoose?

I have json object in my db like as follows:
{
'name':'test1',
'game':'cric'
},
{
'name':'test1',
'game':'cric'
},
{
'name':'test1',
'game':'football'
},
{
'name':'test2'
'game':'football'
}
I am trying to get output as follows
{
'name':'test1'
'game':[{cric: 2}, {football:1}],
'totalCount': 3
}
I used aggregate query for that.
group {'_id':{name:'$name'}, {game:{$addToSet:$game}}
project {name : $name, game: $game}
I got output as
{name: 'test1', 'game':[cric, football]}
Now i have question that, how can i get count of game. it current example for cricket it is 2 and for football 1 for test1 user
A similar question is answered here.
For your particular case it would be:
db.collection.aggregate([
{
$group: {
_id: { name: "$name", game: "$game" },
games: { "$push": "$game" },
total: { "$sum": 1 }
}
},
{
$group: {
_id: { name: "$_id.name" },
games: { $addToSet: { game: "$_id.game", sum:"$total" } }
}
}
])
And the result should look like:
{
"result" : [
{
"_id" : {
"name" : "test1"
},
"games" : [
{
"game" : "cric",
"sum" : 2
},
{
"game" : "football",
"sum" : 1
}
]
},
{
"_id" : {
"name" : "test2"
},
"games" : [
{
"game" : "football",
"sum" : 1
}
]
}
],
"ok" : 1
}
Here is my solution to test1 name record
db.getCollection('COLLECTION_NAME').aggregate([
{$match : {name : "test1"}},
{
$group: {
_id: "$game" ,
total: { "$sum": 1 },
name : {$first : "$name"}
}
},
{
$group: {
_id : "$name",
games: { $addToSet: { game: "$_id", sum:"$total" } },
totalCount : {$sum : "$total"}
}
}
])