Mongodb add an element to an array in specific order - arrays

I would like to push a new value to an array in a specific position but although I tried differents things I can do it. Can someone help me?
My document is:
{
"_id" : ObjectId("55528f000000000000000000"),
"contractId" : "55528f000000000000000000",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"5312",
"5310"
]
}
}
]
}
I've tried with set, but I didn't find a way to add the element in the position I want, then I tried with push but it's not working. I did something like that:
db.collection.update({
"_id": ObjectId("55528f000000000000000000"),
},
{
"$push": {
"field1.$[i].data.$.defaultOrder": {
"$each": [
"9999"
],
"$position": 2
}
}
},
{
arrayFilters: [
{
"i.entities": {
$elemMatch: {
"name": "example"
}
}
}
]
})
And although it gives me a match it doesn't make any change
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Expected outcome:
{
"_id" : ObjectId("55528f000000000000000000"),
"contractId" : "55528f000000000000000000",
"field1": [
{
"name":"example",
"backendData": {
"map": {
"7552" : "RTEST",
"3511" : "TESTR",
"5312" : "JKTLE",
"5310" : "INVTS"
}
},
"data": {
"defaultOrder": [
"7552",
"3511",
"9999",
"5312",
"5310"
]
}
}
]
}
Thank in advance

Please try this (edited both 2nd and 3rd parts):
db.collection.update({"_id": ObjectId("55528f000000000000000000")},
{"$push": {"field1.$[i].data.defaultOrder":
{"$each": ["9999"], "$position": 2}}},
{arrayFilters: [ { "i.name": "example" } ]})
See:
https://mongoplayground.net/p/8o57t4tlnm6

Related

How to remove a document inside an array in mongodb using $pull

I have the following document structure
{
"_id" : ObjectId("5ffef283f1f06ff8524aa2c2"),
"applicationName" : "TestApp",
"pName" : "",
"environments" : [],
"stages" : [],
"createdAt" : ISODate("2021-01-15T09:51:35.546Z"),
"workflows" : [
[
{
"pName" : "Test1",
"wName" : "TestApp_Test1",
"agent" : ""
},
{
"pName" : "Test2",
"wName" : "TestApp_Test2",
"agent" : ""
}
],
[
{
"pName" : "Test1",
"wName" : "TestApp_Test1",
"agent" : ""
}
]
],
"updatedAt" : Date(-62135596800000)
}
I wish to remove the occurrences of
{
"pName" : "Test1",
"wName" : "TestApp_Test1",
"agent" : ""
}
The resultant document should look like
{
"_id" : ObjectId("5ffef283f1f06ff8524aa2c2"),
"applicationName" : "TestApp",
"pName" : "",
"environments" : [],
"stages" : [],
"createdAt" : ISODate("2021-01-15T09:51:35.546Z"),
"workflows" : [
[
{
"pName" : "Test2",
"wName" : "TestApp_Test2",
"agent" : ""
}
]
],
"updatedAt" : Date(-62135596800000)
}
I've tried the below mongo query
db.getCollection('workflows').update({_id:ObjectId('5ffef283f1f06ff8524aa2c2')},
{$pull:{workflows: { $elemMatch: {pipelineName: 'Test1'}}}} )
This is removing all the documents from workflows field including Test2 since Test1 is matched.
How can we remove only the entries for Test1 and keep the others?
You can do it using the positional operator "$[]" :
db.getCollection('workflows').update({_id: ObjectId("5ffef283f1f06ff8524aa2c2") }, {$pull: {"workflows.$[]":{pName:"Test1" } } } )
but the schema looks abit strange and after the update you will have empty arrays inside workflows if all elements got deleted in the sub-array.
To fix the empty sub-arrays you will need to perform second operation to remove them:
db.getCollection('workflows').update({_id: ObjectId("5ffef283f1f06ff8524aa2c2") }, {$pull: {"workflows":[] } } )
You cannot use $elemMatch as it returns the first matching element in the array.
I am not sure there is another best way to do this with the provided schema design.
play
db.collection.aggregate({
"$unwind": "$workflows"
},
{
"$unwind": "$workflows"
},
{
"$match": {
"workflows.pName": {
"$ne": "Test1"
}
}
},
{
"$group": {
"_id": "$_id",
workflows: {
$push: "$workflows"
},
applicationName: {
"$first": "$applicationName"
}
}
},
{
"$group": {
"_id": "$_id",
workflows: {
$push: "$workflows"
},
applicationName: {
"$first": "$applicationName"
}
}
})
unwind twice required to de-normalize the data
match to filter out the unnecessary doc
group twice required to bring the required output
You can save this to a collection using $out as last stage.

mongodb, how can I project or push root field on nested array using aggregate?

I have the following example document with four nested arrays
{
"_id" : ObjectId("5ed6bd9596908c36f4200980"),
"attr2" : "hello",
"attr3" : 1234,
"attr1" : {
"firstArray" : [
{
"secondArray" : [
{
"secondAttr1" : "world",
"secondAttr2" : 456,
"secondAttr3" : [
{
"finalArray" : [
{
"finalAttr1" : "alex",
"finalAttr2" : 9876
}
]
}
]
}
]
}
]
}
}
and this aggregate query that finds through nested arrays, unwinding and creating new root for each array processed, returning elements from "finalArray"
db.AssetTest.aggregate(
[
{'$match':{'$and':[{'attr2': {'$eq':'hello'}}]}},
{'$project': {'values': '$attr1.firstArray'}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}},
{'$project': {'values': {'$filter': {'input': '$secondArray','cond': {'$and':[{'$eq':['$$this.secondAttr1', 'world']}]}}}}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}},
{'$project': {'values': '$secondAttr3'}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}},
{'$project': {'values': {'$filter': {'input': '$finalArray','cond': {'$and':[{'$eq':['$$this.finalAttr1', 'alex']}]}}}}},
{'$unwind':'$values'},
{'$replaceRoot':{'newRoot':'$values'}}
]
)
the result for this aggregate is
{
"finalAttr1" : "alex",
"finalAttr2" : 9876
}
my question is, how can I project root field "attr2" through all aggregation stages so it will be on result array?
{
"attr2" : "hello",
"finalAttr1" : "alex",
"finalAttr2" : 9876
}
I'm new to mongo and I'm completely lost on this so any help will be very appreciated.
By the way I'm using mongo 3.4.15.
Many Thanks!!
In your case all you need is $addFields which got introduced in MongoDB version 3.4 :
I've cut down few stages & operators which doesn't seems to be useful, Check this latest aggregation pipeline :
db.collection.aggregate([
{
"$match": { "attr2": "hello" }
},
/** This stage adds new field to the place where you want */
{
$addFields: { "attr1.firstArray.secondArray.secondAttr3.finalArray.attr2": "$attr2" }
},
{
"$unwind": "$attr1.firstArray"
},
{
"$replaceRoot": { "newRoot": "$attr1.firstArray" }
},
{
"$project": {
"secondArray": {
"$filter": { "input": "$secondArray", "cond": { "$eq": [ "$$this.secondAttr1", "world" ] } }
}
}
},
{
"$unwind": "$secondArray"
},
{
"$replaceRoot": { "newRoot": "$secondArray" }
},
{
"$unwind": "$secondAttr3"
},
{
"$replaceRoot": { "newRoot": "$secondAttr3" }
},
{
"$project": {
"finalArray": { "$filter": { "input": "$finalArray", "cond": { "$eq": [ "$$this.finalAttr1", "alex" ] } } }
}
},
{
"$unwind": "$finalArray"
},
{
"$replaceRoot": { "newRoot": "$finalArray"}
}
])
Test : mongoplayground
Ref : $addFields
Note :
You don't need to use $and on single conditions (Refer your 1st stage).
You don't need to multiple $project stages in the middle for conversion (Refer your 2nd stage).

$in requires an array as a second argument, found: missing

can anybody please tell me what am i doing wrong?
db document structure:
{
"_id" : "module_settings",
"moduleChildren" : [
{
"_id" : "module_settings_general",
"name" : "General",
},
{
"_id" : "module_settings_users",
"name" : "Users",
},
{
"_id" : "module_settings_emails",
"name" : "Emails",
}
],
“permissions” : [
"module_settings_general",
"module_settings_emails"
]
}
pipeline stage:
{ $project: {
filteredChildren: {
$filter: {
input: "$moduleChildren",
as: "moduleChild",
cond: { $in : ["$$moduleChild._id", "$permissions"] }
}
},
}}
I need to filter "moduleChildren" array to show only modules which ids are in "permissions" array. Ive tried "$$ROOT.permissions" and "$$CURRENT.permissions" but none of them is working. I always get an error that $in is missing array as argument. It works when i hardcode the array like this: cond: { $in : ["$$moduleChild._id", [“module_settings_general", "module_settings_emails”]] } so it seems the problem is in passing of the array.
Thanks for any advices!
First option --> Use aggregation
Because your some of the documents in your collection may or may not contain permissions field or is type not equal to array that's why you are getting this error.
You can find the $type of the field and if it is not an array or not exists in your document than you can add it as an array with $addFields and $cond aggregation
db.collection.aggregate([
{ "$addFields": {
"permissions": {
"$cond": {
"if": {
"$ne": [ { "$type": "$permissions" }, "array" ]
},
"then": [],
"else": "$permissions"
}
}
}},
{ "$project": {
"filteredChildren": {
"$filter": {
"input": "$moduleChildren",
"as": "moduleChild",
"cond": {
"$in": [ "$$moduleChild._id", "$permissions" ]
}
}
}
}}
])
Second option -->
Go to your mongo shell or robomongo on any GUI you are using and run
this command
db.collection.update(
{ "permissions": { "$ne": { "$type": "array" } } },
{ "$set": { "permissions": [] } },
{ "multi": true }
)

MongoDB query subarray in sub array

i want fetch a unitHouse from my document which is an sub array of sub array
Here is the data
{
"_id" : ObjectId("5a17d305c438324308bffb19"),
"floorRow" : [
{
"floorRowNo" : "F1",
"floorRowInfo" : "Best Floor Ever that i have ever seen",
"_id" : ObjectId("5a17d333c438324308bffb1a"),
"unitHouse" : [
]
},
{
"floorRowNo" : "F2",
"floorRowInfo" : "view",
"_id" : ObjectId("5a1bdfbb4d63841c3cb6fc89"),
"unitHouse" : [
{
"unitHouseNo" : "Unit001",
"unitHouseType" : "OFFICE",
"unitHouseStatus" : "SELL",
"_id" : ObjectId("5a1d212bed3a552f0421fd6b"),
},
{
"unitHouseNo" : "Unit002",
"unitHouseType" : "CAT003",
"unitHouseStatus" : "SELL",
"_id" : ObjectId("5a1e3691af12544ff05690e3"),
}
]
}
],
}
Here is what I have queried so far, which i can get floor F2 that i wanted, but it came with both unit. I want only unitHouse with id : 5a1e3691af12544ff05690e3.
propertyDevModel.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId("5a17d305c438324308bffb19"),
}
},
{
$project: {
floorRow: {
$filter: {
input: '$floorRow',
as: 'floorRow',
cond: {
$eq: ['$$floorRow._id', mongoose.Types.ObjectId("5a1bdfbb4d63841c3cb6fc89")],
}
}
}
}
},
])
I have answered this q by myself, but i will keep this post for others who have the same problem.
db.aggregate([
{
$match: {
_id: projectId,
'floorRow._id': floorRowId
}
},
{$unwind: '$floorRow'},
{
$match: {
'floorRow._id': floorRowId
}
},
{$unwind: '$floorRow.unitHouse'},
{
$match: {
'floorRow.unitHouse._id': unitId
}
},
{
$project:{
'floorRow.unitHouse': 1
}
}
])

Mongo regex with array of elements not accurate

Document schema is as follows
"events": [
{
"title": "title1"
},
{
"title": "title2"
},
{
"title": "title3"
}
]
I have requirement to search (regex ) in events.title field and get the only matching element/object from the array . For that i am querying like this ,
db.collection.find({ "$or" : [ { "events.title" : { "$regex" : ".*title2.*" , "$options" : "i"}} , { "events.title" : { "$regex" : ".*title5.*" , "$options" : "i"}}] , "events" : { "$elemMatch" : { "title" : { "$ne" : null }}}},{"events.$":1,"_id":0});
I was expecting the result would be { "events" : [ { "title" : "title2"} ] } , but it returns
{ "events" : [ { "title" : "title1"} ] }
How can i update the query so that only matching element in array is returned in result ?
UPDATE
"events": {
$elemMatch: {
"$or": [{
"title": {
"$regex": ".*title2.*",
"$options": "i"
}
},
{
"title": {
"$regex": ".*title5.*",
"$options": "i"
}
}]
}
}
did the trick . Now it returns only matching element in the array irrespective of the position.
Thanks for the help
Try this
db.collection.find({events: {
$elemMatch: {
title: {$in: [/.*title2.*/i,
/.*title5.*/i]
}
}
}
},
{"events.$": 1, _id: 0});
{"events": {
$elemMatch: {
"$or": [{
"title": {
"$regex": ".*title2.*",
"$options": "i"
}
},
{
"title": {
"$regex": ".*title5.*",
"$options": "i"
}
}]
}
}
}
in projection did the trick . Now it returns only matching element in the array irrespective of the position

Resources