Reputation: 1311
I have a query that I need to write where I need to determine the number of elements in an array (which I know can be done through $project
with $size
and then $match
) but then I need to be able to process the array items that meet this condition, and if they don't meet the condition, leave them. I don't want to exclude the items that don't meet the criteria through a $match
Here is the standard approach to counting items in the array that cannot work for what I need:
db.collection.aggregate([
{ $project: { _id:1,
themeArraySize: { $gt: [ {$size: "$themes" }, 1 ] } } },
{ $match: { themeArraySize : true }} ,
])
I need to wrap that in a condition, and not exclude anything through match
The "logic" for what I want to achieve for my themes array is this:
themes
arrayThe part i'm struggling with is the how to process the true logic
Putting it in a sentence:
"If there is a theme in the array that is '[]' and this is the only theme, leave it alone. But if '[]' is present, and there are other themes, remove '[]' from the array"
I've made a start:
{
"$project": {
"conversationid": 1,
"themes": {
"$cond": [
{
"$and": [
{
"$or": [
{
"$in": [
"[]",
"$themes"
]
},
{
"$in": [
"NO THEMES SELECTED",
"$themes"
]
}
]
},
{
"$themes": {
"$gte": [
******I WANT TO SAY THAT THE THEMES ARRAY MUST BE >1 IN SIZE HERE******
]
}
}
]
},
{
"$pull": {
"$themes": {
"$in": [
"NO THEMES SELECTED",
"[]"
]
}
}
},
"$themes"
]
}
}
},
But I getting the sense that what I want to do isn't possible
Thanks
Upvotes: 2
Views: 6168
Reputation: 1311
All credit and many thanks to @mickl. I had to make a slight change to their answer as I previously was getting the error "The argument to $size must be an array, but was of type: missing"
To combat this I added a project stage to count the number of items in the array, then added a match to ensure that the number was greater than 0. This meant that I can only be passing an array to @mickl's solution
Full code:
{
"$project": {
"item": 1,
"themes": 1,
"conversationid": 1,
"numberOfItemsInArray": {
"$cond": {
"if": {
"$isArray": "$themes"
},
"then": {
"$size": "$themes"
},
"else": 0
}
}
}
},
{
"$match": {
"numberOfItemsInArray": {
"$gt": 0
}
}
},
{
"$addFields": {
"themes": {
"$cond": [
{
"$eq": [
{
"$size": "$themes"
},
1
]
},
"$themes",
{
"$filter": {
"input": "$themes",
"cond": {
"$and": [
{
"$ne": [
"$$this",
"[]"
]
},
{
"$ne": [
"$$this",
"NO THEMES SELECTED"
]
}
]
}
}
}
]
}
}
},
{
"$unwind": "$themes"
},
Upvotes: 1
Reputation: 49985
You can use $addFields to replace existing themes
field, $cond to define your logic and $filter to remove some specific items from themes
array, try:
db.collection.aggregate([
{
$addFields: {
themes: {
$cond: [
{ $eq: [ { $size: "$themes"} , 1 ] },
"$themes",
{
$filter: {
input: "$themes",
cond: {
$and: [
{ $ne: [ "$$this", "[]" ] },
{ $ne: [ "$$this", "NO THEMES SELECTED" ] }
]
}
}
}
]
}
}
}
])
for below data:
db.collection.save({ themes: [ "something" ] })
db.collection.save({ themes: [ "[]" ] })
db.collection.save({ themes: [ "NO THEMES SELECTED" ] })
db.collection.save({ themes: [ "Something else", "NO THEMES SELECTED" ] })
db.collection.save({ themes: [ "Something else (2)", "[]" ] })
returns:
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a6"), "themes" : [ "something" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a7"), "themes" : [ "[]" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a8"), "themes" : [ "NO THEMES SELECTED" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6a9"), "themes" : [ "Something else" ] }
{ "_id" : ObjectId("5c6d75246fb49f04a0a1f6aa"), "themes" : [ "Something else (2)" ] }
Upvotes: 4