How to use the key of mongoDB collection as a key with some value in my query result? - mongodb

Suppose this is my collection :
{
{
"productType":"bike",
"productID":1,
"lat":22.7,
"long":77.4
},
{
"productType":"car",
"productID":5,
"lat":25.7,
"long":75.4
},
{
"productType":"bike",
"productID":2,
"lat":26.7,
"long":76.4
}
}
I want to use the product type as key to my mongo query and grouping to get result like this :
{
"bike":{
"1":{
"latitude":"22.7",
"longitude":"77.4"
},
"2":{
"latitude":"26.7",
"longitude":"76.4"
}
},
"car":{
"5":{
"latitude":"25.7",
"longitude":"75.4"
}
}
}
I tried to use $replaceRoot but could not get the desired result.

Try this query:
db.products.aggregate([
{
$group: {
_id: "$productType",
products: {
$push: {
"k": { $toString: "$productID" },
"v": {
latitude: "$lat",
longitude: "$long"
}
}
}
}
},
{
$replaceWith: {
array: [
{
k: "$_id",
v: { $arrayToObject: "$products" }
}
]
}
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$array" }
}
}
]);
Output:
/* 1 */
{
"bike" : {
"1" : {
"latitude" : 22.7,
"longitude" : 77.4
},
"2" : {
"latitude" : 26.7,
"longitude" : 76.4
}
}
},
/* 2 */
{
"car" : {
"5" : {
"latitude" : 25.7,
"longitude" : 75.4
}
}
}

Related

Out new collection with sequence numeric id after stages in mongodb aggregate

I'm trying to out a collection, after some stages in the MongoDB aggregation pipeline.
the out collection needs to contain an id with sequence numeric id (1,2,3,4 etc...).
db.getCollection('MY_COLLECTION').aggregate([{
$addFields: {
"dataEntries.targetCol1": "$dataEntries.col1",
"dataEntries.targetCol2": "$dataEntries.col2"
}
},
{
$project: {
"_id": 0,
"dataEntries.col1": 0,
"dataEntries.col2": 0,
"dataEntries.col3": 0,
"dataEntries.col4": 0
}
},
{
$unionWith: {
coll: "MY_COLLECTION",
pipeline: [{
$addFields: {
"dataEntries.targetCol1": "$dataEntries.col3",
"dataEntries.targetCol2": "$dataEntries.col4"
}
},
{
$project: {
"_id": 0,
"dataEntries.col1": 0,
"dataEntries.col2": 0,
"dataEntries.col3": 0,
"dataEntries.col4": 0
}
}
]
}
},
{
$out: "test_out"
}
])
The result collection contains an objectId as the id.
/* 1 */
{
"_id" : ObjectId("6239cb749482cf6b13248a80"),
"dataEntries" : {
"targetCol1" : "101-A",
"targetCol2" : "101"
}
}
/* 2 */
{
"_id" : ObjectId("6239cb749482cf6b13248a81"),
"dataEntries" : {
"targetCol1" : "101-B",
"targetCol2" : "101"
}
}
There is a way to set the id to sequence numeric counter for every document?
The expected results:
/* 1 */
{
"_id" : 1,
"dataEntries" : {
"targetCol1" : "101-A",
"targetCol2" : "101"
}
}
/* 2 */
{
"_id" : 2,
"dataEntries" : {
"targetCol1" : "101-B",
"targetCol2" : "101"
}
}
In MongoDB 5.0 it is very simple:
db.collection.aggregate([
{
$setWindowFields: {
sortBy: { _id: 1, },
output: { _id: { $documentNumber: {} } }
}
}
])
In earlier versions, it is a little more to do:
db.collection.aggregate([
{
$group: {
_id: null, data: { $push: "$$ROOT" }
}
},
{
$set: {
data: {
$map: {
input: "$data",
in: {
$mergeObjects: ["$$this",
{ _id: {$add: [{ $indexOfArray: ["$data._id", "$$this._id"] }, 1]} }
]
}
}
}
}
},
{ $unwind: "$data" },
{ $replaceWith: "$data" }
])

How to combine results in a Mongo aggregation query

I'm new to aggregation queries in Mongo and been really struggling trying to produce the output I want. I have the following aggregation query:
db.events.aggregate([
{ $match: { requestState: "APPROVED" } },
{ $unwind: { path: "$payload.description" } },
{ $group: { _id: { instr: "$payload.description", bu: "$createdByUser", count: { $sum: 1 } } } }
]);
that returns the following results:
{ "_id" : { "instr" : "ABC-123", "bu" : "BU2", "count" : 1 } }
{ "_id" : { "instr" : "ABC-123", "bu" : "BU1", "count" : 1 } }
{ "_id" : { "instr" : "DEF-456", "bu" : "BU1", "count" : 1 } }
How can I amend the aggregation query so that there are only 2 documents returned instead of 3? With the two "ABC-123" results combined into a single result with a new array of counts with the "bu" and "count" fields i.e.
{ "_id" : { "instr" : "ABC-123", "counts": [ { "bu" : "BU1", "count" : 1 }, { "bu" : "BU2", "count" : 1 } ] } }
Many thanks
You can add another stage to only $group by _id.instr and another stage to $project to your desired output shape
db.events.aggregate([
{
$match: { requestState: "APPROVED" }
},
{
$unwind: { path: "$payload.description" }
},
{
$group: {
_id: { instr: "$payload.description", bu: "$createdByUser", count: { $sum: 1 } }
}
},
{
$group: {
_id: { instr: "$_id.instr" },
counts: { $push: { bu: "$_id.bu", count: "$_id.count" } }
}
},
{
$project: {
_id: { instr: "$_id.instr", counts: "$counts" }
}
}
]);

How to access the second level array in mongodb (with group)

here is sample collection:
{
"_id":ObjectId("5cc7d8e88c33e065c56b0883"),
"age":70,
"child":[
{
"id":"son1",
"age":40,
"grandSon":[
{
"id":"grand1",
"age":10,
"like":[
{
"id":"like1",
"info":"apple"
},
{
"id":"like2",
"info":"banana"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":10
},
{
"id":"grandGrand2",
"age":13
}
]
},
{
"id":"grand2",
"age":13,
"like":[
{
"id":"like1",
"info":"apple"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":12
},
{
"id":"grandGrand2",
"age":14
}
]
}
]
},
{
"id":"son2",
"age":40,
"grandSon":[
{
"id":"grand1",
"age":10,
"like":[
{
"id":"like1",
"info":"apple"
},
{
"id":"like2",
"info":"banana"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":15
},
{
"id":"grandGrand2",
"age":16
}
]
},
{
"id":"grand2",
"age":14,
"like":[
{
"id":"like1",
"info":"apple"
},
{
"id":"like2",
"info":"banana"
}
],
"grandGrandSon" :[
{
"id":"grandGrand1",
"age":12
},
{
"id":"grandGrand2",
"age":13
}
]
}
}
]
}
]
I want to result like this
parent age and child'size and like grandSons Count, grandGrandSons count and child's like count
{
"_id": ObjectId("5cc7d8e88c33e065c56b0883"),
"age": 70,
"childCount": 2,
"grandSonsCount": 4
"likeCount": 7
"grandGrandSonsCount": 8,
"grandSonsAgeCount": 105
}
here is my code
{
$lookup:
{
from: "..."
localField: "...",
foreignField: "..",
as: "child"
},
},
{ $unwind: "$child" }
{ $group : {
_id : "$_id",
age: {$first:"$age"},
childCount: {$sum: 1},
grandSonsCount : {$sum : {$size : "$child.grandSon"}},
likeCount: {$sum : {$size : "$child.like"}},
grandGrandSonsCount :
{$sum : {$sum : {$size : "$child.grandSon.grandGrandSon"}}},
//it is return 4(grandSonsCount)
}},
I used lookup, making for above collection(child)
it return grandSon count, but I want to get grandGrandSon Count
how can I get nested array in nested array of size?
how can I have to do??
Add $reduce to the group stage:
{
"$group" : {
....
....
"grandGrandSonsCount" : {
"$sum" : {
"$reduce" : {
"input" : "$child.grandSon",
"initialValue" : 0,
"in" : {
"$sum" : [
"$$value",
{
"$size" : "$$this.grandGrandSon"
}
]
}
}
}
}
}
}

Query datevalue of a inner Array element

Need help with some MongoDB query:
The document I have is below and I am trying to search based on 2 conditions
The meta.tags.code = "ABC"
Its LastSyncDateTime should
meta.extension.value == "" (OR)
the meta.extension.value is less than meta.lastUpdated
Data :
{
"meta" : {
"extension" : [
{
"url" : "LastSyncDateTime",
"value" : "20190206-00:49:25.694"
},
{
"url" : "RetryCount",
"value" : "0"
}
],
"lastUpdate" : "20190207-01:21:41.095",
"tags" : [
{
"code" : "ABC",
"system" : "type"
},
{
"code" : "XYZ",
"system" : "SourceSystem"
}
]
}
}
Query:
db.proc_patients_service.find({
"meta.tags.code": "ABC",
$or: [{
"meta.extension.value": ""
}, {
$expr: { "$lt": [{ "mgfunc": "ISODate", "params": [{ "$arrayElemAt": ["$meta.extension.value", 0] }] }, { "mgfunc": "ISODate", "params": ["$meta.lastUpdate"] }] }
}]
})
But it is only fetching ABC Patients whose LastSyncDateTime is empty and ignores the other condition.
Using MongoDB Aggregation, I have converted your string to date with operator $dateFromString and then compare the value as per your criteria.
db.proc_patients_service.aggregate([
{ $match: { "meta.tags.code": "ABC", } },
{ $unwind: "$meta.extension" },
{
$project: {
'meta.tags': '$meta.tags',
'meta.lastUpdate': { '$dateFromString': { 'dateString': '$meta.lastUpdate', format: "%Y%m%d-%H:%M:%S.%L" } },
'meta.extension.url': '$meta.extension.url',
'meta.extension.value': {
$cond: {
if: { $ne: ["$meta.extension.value", "0"] }, then: { '$dateFromString': { 'dateString': '$meta.extension.value', format: "%Y%m%d-%H:%M:%S.%L" } }, else: 0
}
}
}
},
{
$match: {
$or: [
{ "meta.extension.value": 0 },
{ $expr: { $lt: ["$meta.extension.value", "$meta.lastUpdate"] } }
]
}
},
{
$group: { _id: '_id', 'extension': { $push: '$meta.extension' }, "lastUpdate": { $first: '$meta.lastUpdate' }, 'tags': { $first: '$meta.tags' } }
},
{
$project: { meta: { 'extension': '$extension', lastUpdate: '$lastUpdate', 'tags': '$tags' } }
}
])

MongoDB nested object aggregation sum and sort

I have highly nested mongodb set of objects and i want to sort subofdocuments according to the result of sum their votes for example :
{
"_id":17846384es,
"company_name":"company1",
"products":[
{
"product_id":"123785",
"product_name":"product1",
"user_votes":[
{
"user_id":1,
"vote":1
},
{
"user_id":2,
"vote":2
}
]
},
{
"product_id":"98765",
"product_name":"product2",
"user_votes":[
{
"user_id":5,
"vote":3
},
{
"user_id":3,
"vote":3
}
]
}
]
}
i want to sort as descending products according to the result of sum their votes
the expected output is
{
"_id":17846384es,
"company_name":"company1",
"products":[
{
"product_id":"98765",
"product_name":"product2",
"user_votes":[
{
"user_id":5,
"vote":3
},
{
"user_id":3,
"vote":3
}
]
"votes":8
},
{
"product_id":"123785",
"product_name":"product1",
"user_votes":[
{
"user_id":1,
"vote":1
},
{
"user_id":2,
"vote":2
}
],
"votes":3
}
]
}
Any Idea ?
The following pipeline
db.products.aggregate([
{ $unwind: "$products" },
{
$project: {
company_name: 1,
products: 1,
totalVotes: {
$sum: "$products.user_votes.vote"
}
}
},
{ $sort: { totalVotes: -1 } },
{
$group: {
_id: "$_id",
company_name: { $first: "$company_name" },
products: { $push: "$products" }
}
}
])
would output
{
"_id" : "17846384es",
"company_name" : "company1",
"products" : [
{
"product_id" : "98765",
"product_name" : "product2",
"user_votes" : [
{
"user_id" : 5,
"vote" : 3
},
{
"user_id" : 3,
"vote" : 3
}
]
},
{
"product_id" : "123785",
"product_name" : "product1",
"user_votes" : [
{
"user_id" : 1,
"vote" : 1
},
{
"user_id" : 2,
"vote" : 2
}
]
}
]
}
In case you want to keep the sum of the votes at the product level as shown in your expected output simply modify the $project stage as follows
...
{
$project: {
company_name: 1,
products: {
product_id: 1,
product_name: 1,
user_votes: 1,
votes: { $sum: "$products.user_votes.vote" }
}
}
}
...