Filter and update embedded Array in mongodb mongoose - mongodb

The MongoDb model looks like this :
{
"_id": {
"$oid": "5ee269c949c9d58970528d1e"
},
"parent": [
{
"_id": {
"$oid": "5ee269c949c9d58970528d1d"
},
"uType": "parent",
"fName": "p1",
"lName": "p1",
"num": "123"
}
],
"child": [
{
"_id": {
"$oid": "5ee269c949c9d58970528d1c"
},
"uType": "child",
"fName": "c1",
"lName": "c1",
"num": "8963",
"email": "c1#gmail.com",
"gender": "male"
}
]
}
I have an incoming object that looks like this -
{
"uType":"child",
"fName": "c2",
"lName": "c2",
"num": "98733",
"email": "c2#gmail.app",
"invite": {
"uType": "parent",
"fName": "p1",
"lName": "p1",
"num": "123"
}
}
I have to filter for the incoming object such that I get the response like below- which means for the incoming object, there is a matching parent but there is not a matching child.
Tried using $or and provided multiple conditions but every time ended up getting a record in child array. I gave it a shot using aggregate as well but could not find a possible solution. Can someone tell me what is the best way to do this?
{
"_id": {
"$oid": "5ee269c949c9d58970528d1e"
},
"parent": [
{
"isActive": false,
"_id": {
"$oid": "5ee269c949c9d58970528d1d"
},
"uType": "parent",
"fName": "p1",
"lName": "p1",
"num": "123"
}
],
"child": [ ]
}
UPDATE: Tried using projection, but still no luck -
Model.aggregate([
{
$project: {
child: {
$filter: {
input: "$child",
as: "ch",
cond: { $eq: ["$ch.num", this.num] }
}
}
}
}
])

Actually, you are so close to the solution, if you use "$$ch.num" instead of "$ch.num" it should work.
const match = {
"uType":"child",
"fName": "c2",
"lName": "c2",
"num": "98733",
"email": "c2#gmail.app",
"invite": {
"uType": "parent",
"fName": "p1",
"lName": "p1",
"num": "123"
}
}
Model.aggregate([
{
$match: {
$or: [
{
"parent.num": match.invite.num
},
{
"child.num": match.num
}
]
}
},
{
$project: {
parent: {
$filter: {
input: "$parent",
as: "p",
cond: {
$eq: [
"$$p.num",
match.invite.num
]
}
}
},
child: {
$filter: {
input: "$child",
as: "ch",
cond: {
$eq: [
"$$ch.num",
match.num
]
}
}
}
}
}
])
But you can do something similar without aggregation, but you will not get an empty array when there is no match, you will just don't get the key.
Model.find({
$or: [
{
"parent.num": match.invite.num
},
{
"child.num": match.num
}
]
},
{
parent: {
$elemMatch: {
num: match.invite.num
},
},
child: {
$elemMatch: {
num: match.num
}
}
})

Related

How can get particular object data from double nested array in mongodb

i m new in mongodb, i want only single object data which id is 5689746, by which way can i get this?
[
{
"_id": 8965651651,
"orditems": [
{
"_id": 65748413141,
"itms": [
{
"item1": "aa",
"item2": "bb",
"item3": "cc",
"_id": 7894567
},
{
"item1": "dd",
"item2": "ee",
"item3": "ff",
"_id": 5689746
},
{
"item1": "gg",
"item2": "hh",
"item3": "ii",
"_id": 1644824
}
]
},
{
"_id": 87448413141,
"itms": [
{
"item1": "jj",
"item2": "kk",
"item3": "ll",
"_id": 9874567
},
{
"item1": "mm",
"item2": "nn",
"item3": "oo",
"_id": 8659746
},
{
"item1": "pp",
"item2": "qq",
"item3": "rr",
"_id": 4614824
}
]
}
]
}
]
i m using $elemMatch but i didn't get result as expected
db.orders.findOne({
_id: 8965651651
},
{
orditems: {
$elemMatch: {
_id: 87448413141,
itms: {
$elemMatch: {
_id: 5689746
}
}
}
}
})
i want result like
{
"_id": 8965651651,
"orditems": [
{
"_id": 65748413141,
"itms": [
{
"item1": "dd",
"item2": "ee",
"item3": "ff",
"_id": 5689746
}
]
}
]
}
You need to chain up $filter and $mergeObjects as you are doubly nesting your array fields.
db.collection.aggregate([
{
$match: {
"_id": 8965651651
}
},
{
$set: {
orditems: {
"$filter": {
"input": "$orditems",
"as": "oi",
"cond": {
$eq: [
"$$oi._id",
65748413141
]
}
}
}
}
},
{
$set: {
orditems: {
"$map": {
"input": "$orditems",
"as": "oi",
"in": {
"$mergeObjects": [
"$$oi",
{
itms: {
"$filter": {
"input": "$$oi.itms",
"as": "i",
"cond": {
$eq: [
"$$i._id",
5689746
]
}
}
}
}
]
}
}
}
}
}
])
Mongo Playground

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
}
}]

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

Query MongoDB for nested Arrays

Need help for formatting query to find/get values using search parameters with nested Array.
I have an collection as follows
[
{
"_id": "5b3ad55f66479332a0482961",
"timestamp": "2018-06-17T00:30:00.000Z",
"deviceid": "123456",
"values": [
{
"minval": 1,
"minvalues": [
{
"secval": 51,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 1
}
},
{
"secval": 52,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 1
}
},
{
"secval": 56,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 0
}
}
]
}
]
}
]
need the out as follows with search properties as "values.minvalues.secvalues.priority"
[
{
"_id": "5b3ad55f66479332a0482961",
"timestamp": "2018-06-17T00:30:00.000Z",
"deviceid": "123456",
"values": [
{
"minval": 1,
"minvalues": [
{
"secval": 56,
"secvalues": {
"alt": "300",
"mcc": "404",
"mnc": "46",
"priority": 0
}
}
]
}
]
}
]
I tried the following query but with out success
dbRetval.db('ls_gpsdatabase').collection('gpsevent').aggregate([
{ "$match": { "deviceid": { "$in": idList}}},
{ "$sort": { "_id": -1} },
{"$unwind":"$values.minvalues.secvalues"},
//{"$project":{"deviceid":1,"values.minvalues.secvalues.lat":1,"values.minvalues.secvalues.min":1}} ,
{ "$match": { "values.minvalues.secvalues.priority": { "$eq": 1}}},
{ "$group": { "_id": "$deviceid" , "doc": { "$push": "$values.minvalues.secvalues" }}} ]).toArray();
If any can help that would be great full.
You can use $addFields to replace existing field. Since you have two levels of nested arrays you can use $map for outer and $filter for inner to check your condition:
db.col.aggregate([
{
$match: {
"_id": "5b3ad55f66479332a0482961",
"timestamp": "2018-06-17T00:30:00.000Z"
}
},
{
$addFields: {
values: {
$map: {
input: "$values",
as: "value",
in: {
minval: "$$value.minval",
minvalues: {
$filter: {
input: "$$value.minvalues",
as: "minvalue",
cond: {
$eq: [ "$$minvalue.secvalues.priority", 0 ]
}
}
}
}
}
}
}
}
])