How to use mongodb projection query in array elements - arrays

I would like to project only "type":"A" in menu. is there a way to achieve this?
"_id" : "1",
"menu" : [
{
"type" : "A",
"items" : [
{
"key" : "Add",
"enabled" : true,
} ]
},
{
"type" : "B",
"items" : [
{
"key" : "Add",
"enabled" : true,
} ]
}
]
}

If you want tou output only the menu which type is A you can use $elemMatch in a find query (take care because this only return the first matched element)
db.collection.find({},
{
"menu": {
"$elemMatch": {
"type": "A"
}
}
})
Example here
Or $filter in an aggregate query (this returns all objects with type: "A" in the array):
db.collection.aggregate([
{
"$project": {
"menu": {
"$filter": {
"input": "$menu",
"cond": {
"$eq": [
"$$this.type",
"A"
]
}
}
}
}
}
])
Example here

Related

Nested Query on Array in MongoDB collection

I have below User collection and I want find/update query to update nested array elements.
{
_id:"000-0000-0001",
Roles :{
0000-0000-0011: //EngagementId
["0000-0000-0111", "0000-0000-0112", "0000-0000-3333"],//RoleId
"0000-0000-0012" :
["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002",
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112", "0000-0000-3333"]
}
}
Requirement: I want to pull RoleId 0000-0000-3333 if the array have combination of 0000-0000-3333 and 0000-0000-0112
Below is expected result :
{
_id:"000-0000-0001",
Roles :{
"0000-0000-0011" : ["0000-0000-0111", "0000-0000-0112"],
"0000-0000-0012" : ["0000-0000-0121", "0000-0000-0112"]
}
},
{
_id:"000-0000-0002"
Roles :{
"0000-0000-0021" : [ "0000-0000-0222", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"],
"0000-0000-0022" : [ "0000-0000-0121", "0000-0000-0112"]
}
}
Note : Find/update will work on Key:value or if it is nested then key.key:value, but in above example we have key.value.[values]:$pull(value) and that's the challaenge.
The data model necessitates a complicated update with a pipeline.
Here' one way to do it.
db.collection.update({
"Roles": {"$exists": true}
},
[
{
"$set": {
"Roles": {
"$arrayToObject": {
"$map": {
"input": {"$objectToArray": "$Roles"},
"as": "roleKV",
"in": {
"k": "$$roleKV.k",
"v": {
"$cond": [
{
"$and": [
{"$in": ["0000-0000-0112", "$$roleKV.v"]},
{"$in": ["0000-0000-3333", "$$roleKV.v"]}
]
},
{
"$filter": {
"input": "$$roleKV.v",
"cond": {"$ne": ["$$this", "0000-0000-3333"]}
}
},
"$$roleKV.v"
]
}
}
}
}
}
}
}
],
{"multi": true}
)
Try it on mongoplayground.net.

MongoDB 4.4: input to $filter must be an array not long

I've implemented the following aggregation in MongoDB 4.4.2 and it works fine:
[{
$match: {
"$expr": {
"$and": [{
"$not": "$history_to"
}, ],
},
}
}, {
$unwind: {
"path": "$used",
"preserveNullAndEmptyArrays": true,
}
}, {
$project: {
"ticket": "$$ROOT",
"status": {
"$map": {
"input": {
"$filter": {
"input": "$status",
"as": "cstatus",
"cond": {
"$not": "$$cstatus.history_to",
},
},
},
"as": "status",
"in": "$$status.value",
},
},
}
}]
But when I try it in MongoDB 4.4.4 I encounter input to $filter must be an array not long.
If it's any help, I figured that the cause of this error has something to do with:
"cond": {
"$not": "$$cstatus.history_to",
},
Since when I comment $not it works fine; At first I thought that maybe $not is not supported anymore but it is supported so I'm out of ideas.
Some example documents
{
"_id" : ObjectId("6083ca1ce151beea45602e5d"),
"created_at" : ISODate("2021-04-24T07:34:52.947Z"),
"ticket_id" : ObjectId("6083ca1c68badcedddd875ba"),
"expire_at" : ISODate("2021-04-24T19:30:00Z"),
"history_from" : ISODate("2021-04-24T07:34:52.992Z"),
"created_by" : ObjectId("604df7857d58ab06685ed02e"),
"serial" : "116627138",
"status" : [
{
"history_from" : ISODate("2021-04-24T07:34:52.985Z"),
"history_created_by" : ObjectId("604df7857d58ab06685ed02e"),
"value" : NumberLong(1)
}
],
"history_created_by" : ObjectId("604df7857d58ab06685ed02e")
},
{
"_id" : ObjectId("60713b0fe151beea45602e56"),
"created_by" : ObjectId("604df7857d58ab06685ed02e"),
"ticket_id" : ObjectId("60713b0f68badcedddd875b8"),
"created_at" : ISODate("2021-04-10T05:43:43.228Z"),
"history_created_by" : ObjectId("604df7857d58ab06685ed02e"),
"status" : [
{
"history_created_by" : ObjectId("604df7857d58ab06685ed02e"),
"value" : NumberLong(1),
"history_from" : ISODate("2021-04-10T05:43:43.277Z")
}
],
"serial" : "538142578",
"expire_at" : ISODate("2021-04-10T19:30:00Z"),
"history_from" : ISODate("2021-04-10T05:43:43.281Z"),}
Your query looks good as per your example documents you can check playground,
But when I try it in MongoDB 4.4.4, I encounter input to $filter must be an array not long.
It is not any MongoDB version specific issue, The error says provided field status as input in $filter is not array type it is long type, and $filter input should be an array type.
Definitely there are/is some document(s) having non array value in status field.
If you want to check you can match condition before $filter operation,
{ $match: { status: { $type: "array" } } } // 4 = "array"
This will filter documents by status, it should be an array type.

$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 }
)

How to match and retrieve specific Sub-document value from Mongo

How can I search and retrieve only "Stats.item.id" from this collection who have greater than zero "Stats.item.p" value. I am also facing problem in unwinding this collection.
{
"_id" : "8643",
"Stats" : [
{
"date" : ISODate("2014-02-01"),
"Stats" : {
"item" : [
{
"id" : "4356"
},
{
"id" : "9963",
"p" : NumberInt(1)
}
]
}
}
]
}
{
"_id" : "8643",
{
"date" : ISODate("2014-02-01"),
"Stats" : {
"item" : [
{
"id" : "9963",
"p" : NumberInt(1)
}
}
This is the output I expect. Can anyone help me write this aggregation? oooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooo oooooooooooooooooooo oooooooooooo oooooooo
Hope this aggregation query will work
db.collection.aggregate([
{$unwind:'$Stats'},
{$unwind:'$Stats.Stats'},
{$unwind:'$Stats.Stats.item'},
{$match:{
'Stats.Stats.item.p':{$gt:0}
}},
{$group:{
_id:{
date:'$Stats.date'
},
item:{$push:'$Stats.Stats.item'},
_ids:'$_id'
}},
{$group:{
_id:{_ids:'$_ids',
date:'$_id.date'},
Stats:{$push:{
item:'$item'
}},
}},
{$project:{
_id:'$_id._ids',
Stats:{
date:'$_id.date'
Stats:'$Stats'
}
}}
])
Use $elemMAtch for searching in the array.
db.getCollection('CollectionName').find({
"Stats": {
"$elemMatch": {
"Stats.item": {
"$elemMatch": {
"p": 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