mongoDB $concat string with array item - mongodb

This is my collection:
[
{
"_id": {
"$oid": "6332dda58121948311cbdb67"
},
"date": "2022-09-13",
"file": "xxx",
"package": 1,
"userList": [
{
"userName": "user_1",
"crDate": "2022.09.28",
"boolId": 1
}
]
},
{
"_id": {
"$oid": "6332dda58121948311cbdb68"
},
"date": "2022-09-13",
"file": "xxx",
"package": 2,
"userList": []
}
]
My desired output would be this (if all of the userList array is empty):
[
{
"_id": "2022-09-13",
"items": [
{
"fileName": "xxx",
"items": [
{
"package": "1 - na"
},
{
"package": "2 - na"
}
]
}
]
}
]
And this if I would have an object inside the userList array:
[
{
"_id": "2022-09-13",
"items": [
{
"fileName": "xxx",
"items": [
{
"package": "1 - user_1"
},
{
"package": "2 - na"
}
]
}
]
}
]
I try to run this aggregate:
db.collection.aggregate([
{
$group: {
_id: {
"date": "$date",
"file": "$file",
},
"items": {
$push: {
"package": {
$concat: [
{
$toString: "package"
},
" - ",
{
$toString: {
$arrayElemAt: [
"$userList",
0
]
}
}
]
}
}
}
}
},
{
$group: {
_id: "$_id.date",
"items": {
$push: {
"fileName": "$_id.file",
"items": "$items"
}
}
},
},
])
It's running if the userList array is empty, but it's not returning the desired output and if the userList array is not empty it throws this error:
Mongo Server error (MongoCommandException): Command failed with error 241 (ConversionFailure): 'Unsupported conversion from object to string in $convert with no onError value' on server
Here comes an example: mongo_playground
How should I modify the aggregate?

It requires 2 fixes in the first $group stage,
missed the $ sign in package property name, in { $toString: "$package" }
use property name userName while accessing first element in $userList.userName
use $ifNull operator to check if the property is not present then it returns "na" string
db.collection.aggregate([
{
$group: {
_id: {
"date": "$date",
"file": "$file"
},
"items": {
$push: {
"package": {
$concat: [
{ $toString: "$package" }, // <====== here
" - ",
{
$toString: {
$ifNull: [
{ $arrayElemAt: ["$userList.userName", 0] }, // <====== here
"na"
]
}
}
]
}
}
}
}
},
{
$group: {
_id: "$_id.date",
"items": {
$push: {
"fileName": "$_id.file",
"items": "$items"
}
}
}
}
])
Playground

Related

mongo aggregate - group - show number fo childrens

This is how my documents looks like:
[
{
"_id": {
"$oid": "633a982186c443b693dc240c"
},
"date": "2022-09-27",
"file": "file_1",
"package": 1,
"packagecode": "xy/1",
"pshIdList": [],
"userList": [
{
"userName": "user_1",
"crDate": "2022.09.28",
"boolId": 1
}
]
},
{
"_id": {
"$oid": "633a982186c443b693dc240d"
},
"date": "2022-09-27",
"file": "file_2",
"package": 2,
"packagecode": "xy/2",
"pshIdList": [],
"userList": []
}
]
Because of the appearance of the frontend (i have to display a tree structure) i need to group by the documents this way:
db.collection.aggregate([
{
$set: {
"username": {
$filter: {
input: "$userList",
as: "user",
cond: {
"$eq": [
"$$user.boolId",
1
]
}
}
}
}
},
{
$set: {
"username": {
$arrayElemAt: [
"$username",
0
]
}
}
},
{
$set: {
"username": {
$ifNull: [
"$username.userName",
"na"
]
}
}
},
{
$group: {
_id: {
"date": {
$concat: [
"date: ",
"$date",
]
},
"file": {
$concat: [
"file: ",
"$file"
]
},
},
"items": {
$push: {
"items": {
$concat: [
"$packagecode",
" - ",
"$username"
]
}
}
}
}
},
{
$group: {
_id: "$_id.date",
"items": {
$push: {
"file": "$_id.file",
"items": "$items"
}
}
}
}
])
This is the result of the aggregate:
[
{
"_id": "date: 2022-09-27",
"items": [
{
"file": "file: file_1",
"items": [
{
"items": "xy/1 - user_1"
}
]
},
{
"file": "file: file_2",
"items": [
{
"items": "xy/2 - na"
}
]
}
]
}
]
Also need to display the number of childrens of each level, so the output should look like this:
[
{
"_id": "date: 2022-09-27 - 2",
"items": [
{
"file": "file: file_1 - 1",
"items": [
{
"items": "xy/1 - user_1"
}
]
},
{
"file": "file: file_2 - 1",
"items": [
{
"items": "xy/2 - na"
}
]
}
]
}
]
I have no clue how to solve it. I was trying with $set operator, but cannot use group inside that. This is an example playground
You can use $map, to loop over the items array and append the number of children in file, like this:
db.collection.aggregate([
{
$set: {
"username": {
$filter: {
input: "$userList",
as: "user",
cond: {
"$eq": [
"$$user.boolId",
1
]
}
}
}
}
},
{
$set: {
"username": {
$arrayElemAt: [
"$username",
0
]
}
}
},
{
$set: {
"username": {
$ifNull: [
"$username.userName",
"na"
]
}
}
},
{
$group: {
_id: {
"date": {
$concat: [
"date: ",
"$date",
]
},
"file": {
$concat: [
"file: ",
"$file"
]
},
},
"items": {
$push: {
"items": {
$concat: [
"$packagecode",
" - ",
"$username"
]
}
}
}
}
},
{
$group: {
_id: "$_id.date",
"items": {
$push: {
"file": "$_id.file",
"items": "$items"
}
}
}
},
{
"$set": {
"items": {
"$map": {
"input": "$items",
"as": "element",
"in": {
items: "$$element.items",
file: {
"$concat": [
"$$element.file",
" - ",
{
"$toString": {
"$size": "$$element.items"
}
}
]
}
}
}
},
_id: {
"$concat": [
"$_id",
" - ",
{
"$toString": {
"$sum": {
"$map": {
"input": "$items",
"as": "element",
"in": {
"$size": "$$element.items"
}
}
}
}
}
]
}
}
}
])
Playground link.

mongodb populate with aggreagation pipeline via objectid (Fill object data in array using objectid)

I tried using the aggregation lookup but it didn't work, when I try to replace the objectId or at least replace it the old data get overridden and I loose the the "users" array.
I would like to get some help.
This is my current DB.
db={
"Persons": [
{
"_id": {
"$oid": "62d7b53009b0fd96353c043f"
},
"Name": "Alice",
"petName": "Alice jr"
},
{
"_id": {
"$oid": "62e7cc0e264d240455f66110"
},
"Name": "Bob",
"Age": 27
}
],
"Accounts": [
{
"_id": {
"$oid": "62e788b5264d240455f6610c"
},
"banned": [],
"active": [
{
"users": [
{
"first": "BobRules",
"second": "BobTheThird"
}
],
"person": {
"$oid": "62e7cc0e264d240455f66110"
},
},
{
"users": [
{
"first": "AliceRules",
"second": "AliceTheSecond"
},
{
"first": "SecretAlice",
"second": "AliceAndEve"
}
],
"person": {
"$oid": "62d7b53009b0fd96353c043f"
}
}
]
}
]
}
And this is the output I would like to get.
{
"_id": {
"$oid": "62e788b5264d240455f6610c"
},
"banned": [],
"active": [
{
"users": [
{
"first": "BobRules",
"second": "BobTheThird"
}
],
"person": {
"$oid": "62e7cc0e264d240455f66110",
"Name": "Bob",
"Age": 27
},
},
{
"users": [
{
"first": "AliceRules",
"second": "AliceTheSecond"
},
{
"first": "SecretAlice",
"second": "AliceAndEve"
}
],
"person": {
"$oid": "62d7b53009b0fd96353c043f",
"Name": "Alice",
"petName": "Alice jr"
}
}
]
}
Finally I was able to get the wanted result, thanks to the answer in this question: MongoDB join data inside an array of objects
The aggregation had to be:
[{
$addFields: {
active: {
$ifNull: [
'$active',
[]
]
}
}
}, {
$lookup: {
from: 'Persons',
localField: 'active.person',
foreignField: '_id',
as: 'persons'
}
}, {
$addFields: {
active: {
$map: {
input: '$active',
'in': {
$mergeObjects: [
'$$this',
{
person: {
$arrayElemAt: [
'$persons',
{
$indexOfArray: [
'$persons._id',
'$$this.person'
]
}
]
}
}
]
}
}
}
}
}, {
$project: {
persons: 0
}
}]

MongoDB match filters with grouping and get total count

My sample data:
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_2",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
{
"_id": "random_id_3",
"priority": "P2",
"owners": ["user-1", "user-2"],
},
I want to run an aggregation pipeline on the data involving match filters and grouping, also I want to limit the number of groups returned as well as the number of items in each group.
Essentially, if limit=2, limit_per_group=1, group_by=owner, priority=P1, I want the following results:
[
{
"data": [
{
"group_key": "user-1",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
{
"group_key": "user-2",
"total_items_in_group": 2,
"limited_items": [
{
"_id": "random_id_1",
"priority": "P1",
"owners": ["user-1", "user-2"],
},
],
},
]
},
{
"metadata": {
"total_items_matched": 2,
"total_groups": 2
}
},
]
Need some help on how to write an aggregation pipeline to get the required result.
My current query is as follows:
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 1,
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched"
}
]
}
}
Mongo playground link
I am unable to calculate the total number of groups.
add new stage of $addfields at the end of pipeline
db.collection.aggregate([
{
"$match": {
"priority": "P1"
}
},
{
"$facet": {
"data": [
{
$addFields: {
"group_by_owners": "$owners"
}
},
{
$unwind: "$group_by_owners"
},
{
$group: {
"_id": "$group_by_owners",
"total_items_in_group": {
$sum: 1
},
"items": {
$push: "$$ROOT"
}
}
},
{
$sort: {
"total": -1
}
},
{
$unset: "items.group_by_owners"
},
{
$project: {
"_id": 0,
"group_key": "$_id",
"total_items_in_group": 1,
"limited_items": {
$slice: [
"$items",
1
]
}
}
},
{
"$limit": 2
}
],
"metadata": [
{
$count: "total_items_matched",
}
]
}
},
{
"$addFields": {
"metadata.total_groups": {
"$size": "$data"
}
}
}
])
https://mongoplayground.net/p/y5a0jvr6fxI

How to remove obejct from multi level array in nodejs using mongo?

{
"_id" : ObjectId("52f504bb2f9dd91186211537"),
"Data": {
"Stage": {
"FirstArray": [
{
"Name": "FirstLevelArray-FirstObject",
"_id": ObjectId("5fe1a5fa2d8e360ac4093b7e"),
"SecondArray": [
{
"Name": "1-SecondLevelArray-FirstObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b81")
},
{
"Name": "1-SecondLevelArray-SecondObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b82")
}
]
},
{
"Name": "FirstLevelArray-SecondObject",
"_id": ObjectId("5fdc9dced45fa417d417c441"),
"SecondArray": [
{
"Name": "2-SecondLevelArray-FirstObject",
"_id": ObjectId("5fde08564d28f313acc0c93b")
},
{
"Name": "2-SecondLevelArray-SecondObject",
"_id": ObjectId("5fde08d64d28f313acc0c93c")
}
]
}
]
}
}
}
This is the sample format of my code.
I want to delete this object { "Name": "2-SecondLevelArray-SecondObject", "_id": ObjectId("5fde08d64d28f313acc0c93c") } from this record.
I tried this query
model.update(
{ $and: [{ "_id": ObjectId("52f504bb2f9dd91186211537") }},
{"Data.Stage.FirstArray.SecondArray._id":ObjectId("5fde08d64d28f313acc0c93c")}] ,
{ $pull:{
"Data.Stage.FirstArray.$.SecondArray._id": ObjectId("5fe1a7a52d8e360ac4093b82")
}
},
{new:true,upsert:false})
How would I achieve this in MongoDB ?
Here is the expected result of find({"_id" : ObjectId("52f504bb2f9dd91186211537")}) after the update
EDIT: {
"_id" : ObjectId("52f504bb2f9dd91186211537"),
"Data": {
"Stage": {
"FirstArray": [
{
"Name": "FirstLevelArray-FirstObject",
"_id": ObjectId("5fe1a5fa2d8e360ac4093b7e"),
"SecondArray": [
{
"Name": "1-SecondLevelArray-FirstObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b81")
},
{
"Name": "1-SecondLevelArray-SecondObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b82")
}
]
},
{
"Name": "FirstLevelArray-SecondObject",
"_id": ObjectId("5fdc9dced45fa417d417c441"),
"SecondArray": [
{
"Name": "2-SecondLevelArray-FirstObject",
"_id": ObjectId("5fde08564d28f313acc0c93b")
}
]
}
]
}
}
}
model.update({ _id: ObjectId("52f504bb2f9dd91186211537"), Data.Stage.FirstArray:{ $elemMatch: { SecondArray:{$elemMatch:{"_id":ObjectId("5fde08d64d28f313acc0c93c")}}}}},
{ $pull:{ "Data.Stage.FirstArray.$.SecondArray":{"_id": ObjectId("5fde08d64d28f313acc0c93c") }}},{new:true,upsert:false})
My idea is to filter out those with your given Name field
model.updateMany({}, {
$set: { "Data.Stage.FirstArray.SecondArray": { $filter: {
input: "$Data.Stage.FirstArray.SecondArray",
as: "item",
cond: { $eq: [ "$$item.Name", '2-SecondLevelArray-SecondObject' ] }
} } },
});
Honestly, I'm not sure it will be working but it worths a try.

MongoDb aggregation with arrays inside an array possible

I am struggling to find some examples of using the mongo aggregation framework to process documents which has an array of items where each item also has an array of other obejects (array containing an array)
In the example document below what I would really like is an example that sums the itemValue in the results array of all cases in the document and accross the collection where the result.decision was 'accepted'and group by the document locationCode
However, even an example that found all documents where the result.decision was 'accepted' to show or that summmed the itemValue for the same would help
Many thanks
{
"_id": "333212",
"data": {
"locationCode": "UK-555-5566",
"mode": "retail",
"caseHandler": "A N Other",
"cases": [{
"caseId": "CSE525666",
"items": [{
"id": "333212-CSE525666-1",
"type": "hardware",
"subType": "print cartridge",
"targetDate": "2020-06-15",
"itemDetail": {
"description": "acme print cartridge",
"quantity": 2,
"weight": "1.5"
},
"result": {
"decision": "rejected",
"decisionDate": "2019-02-02"
},
"isPriority": true
},
{
"id": "333212-CSE525666-2",
"type": "Stationery",
"subType": "other",
"targetDate": "2020-06-15",
"itemDetail": {
"description": "staples box",
"quantity": 3,
"weight": "1.66"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-03-03",
"itemValue": "23.01"
},
"isPriority": true
}
]
},
{
"caseId": "CSE885655",
"items": [{
"id": "333212-CSE885655-1",
"type": "marine goods",
"subType": "fish food",
"targetDate": "2020-06-04",
"itemDetail": {
"description": "fish bait",
"quantity": 5,
"weight": "0.65"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-03-02"
},
"isPriority": false
},
{
"id": "333212-CSE885655-4",
"type": "tobacco products",
"subType": "cigarettes",
"deadlineDate": "2020-06-15",
"itemDetail": {
"description": "rolling tobbaco",
"quantity": 42,
"weight": "2.25"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-02-02",
"itemValue": "48.15"
},
"isPriority": true
}
]
}
]
},
"state": "open"
}
You're probably looking for $unwind. It takes an array within a document and creates a separate document for each array member.
{ foos: [1, 2] } -> { foos: 1 }, { foos: 2}
With that you can create a flat document structure and match & group as normal.
db.collection.aggregate([
{
$unwind: "$data.cases"
},
{
$unwind: "$data.cases.items"
},
{
$match: {
"data.cases.items.result.decision": "accepted"
}
},
{
$group: {
_id: "$data.locationCode",
value: {
$sum: {
$toDecimal: "$data.cases.items.result.itemValue"
}
}
}
},
{
$project: {
_id: 0,
locationCode: "$_id",
value: "$value"
}
}
])
https://mongoplayground.net/p/Xr2WfFyPZS3
Alternative solution...
We group by data.locationCode and sum all items with this condition:
cases[*].items[*].result.decision" == "accepted"
db.collection.aggregate([
{
$group: {
_id: "$data.locationCode",
itemValue: {
$sum: {
$reduce: {
input: "$data.cases",
initialValue: 0,
in: {
$sum: {
$concatArrays: [
[ "$$value" ],
{
$map: {
input: {
$filter: {
input: "$$this.items",
as: "f",
cond: {
$eq: [ "$$f.result.decision", "accepted" ]
}
}
},
as: "item",
in: {
$toDouble: {
$ifNull: [ "$$item.result.itemValue", 0 ]
}
}
}
}
]
}
}
}
}
}
}
}
])
MongoPlayground