Mongo project test if value is in array - mongodb

In a project step of a mongo aggregation, I'd like to create a boolean field like:
{
$project: {
isInArray: { $cond: [
{ $in: ['$_id', ids] },
{ $const: true },
{ $const: false },
] },
},
},
but this is failing with
invalid operator $in
I could not find documentation on the correct syntax

You can use $setIsSubset operator
db.people.aggregate([
{ $project: {
isInArray: { $cond: [ {$setIsSubset: [['$_id'], ids]}, true, false ] }
}}
])

Related

MongoDB - $filter an array of objects with $exists

I have an array of objects and in the $project pipeline of MongoDB I have to choose the one element whose metadata does not exist. So, for example below are a few documents after my $group pipeline -
{
_id: {
genre: "suspense",
},
price: 10210.6,
data: [
{
subGenre: "Thriller",
flag: true,
},
{
subGenre: "jumpScare",
},
{
subGenre: "horror",
flag: true,
}
]
}
After this, I need to run a $project pipeline where I have to only project that element of the data array where the flag does not exist. My syntax for that is -
db.collection.aggregate([
{
"$project": {
"_id": 0,
"price": 1,
"data": {
"$getField": {
"field": "subGenre",
"input": {
"$first": {
"$filter": { input: "$data", cond: { "$exists": [ "$$this.flag", false ] } }
}
}
}
}
}
}
])
But this is throwing an error -
Invalid $project :: caused by :: Unrecognized expression '$exists'
The output should be -
{
price: 10210.6,
subGenre: "jumpScare"
}
Instead of using $exists which is invalid, you can compare the value with:
$eq: [
"$$this.flag",
undefined
]
This aims to check whether the flag field does not exist.
Demo # Mongo Playground
You may also work with checking the $type value is matched with "missing". (Tested this works in MongoDB Compass)
$eq: [
{ $type: "$$this.flag" },
"missing"
]

MongoDB - Update nested document based on its current content

I have a simple collection with a document like this one:
{
_id: ObjectId('62b196e43581370007773393'),
name: 'John',
jobs: [
{
company: 'ACME',
type: 'programmer',
technologies: [
{
level: 1,
name: 'Java'
},
{
level: 3,
name: 'MongoDB'
}
]
}
]
}
I'd like to collect all technologies into a new field in the "job" sub-document for "programmers" jobs only, to achieve this effect:
(...)
jobs: [
{
it_technologies : ["Java", "MongoDB"]
(...)
}
]
(...)
The problem is that I do not know how to refer to and collect proper elements of the documents with this query:
db.runCommand({update: "employees",
updates: [
{
q: {},
u: { $set: { "jobs.$[j].it_technologies": "<how to collect all technologies in this job?>" } },
upsert: false,
multi: true,
arrayFilters: [{
$and:
[
{"j.type": "programmer"},
{"j.technologies": {$exists : true, $ne: []} }
]
}]
}
] });
Is it at all possible?
I'd be grateful for any clue!
Think that you need to work the update with the aggregation pipeline.
map - Iterate with the jobs array and return a new array.
1.1. $cond - Check the condition (Match the current document of jobs type and technology is not an empty array). If fulfilled, then update the document, else remains existing.
1.1.1. $mergeObjects - Merge current document with the document with it_technologies.
db.collection.update({},
[
{
$set: {
"jobs": {
$map: {
input: "$jobs",
in: {
$cond: {
if: {
$and: [
{
$eq: [
"$$this.type",
"programmer"
]
},
{
$ne: [
"$$this.technologies",
[]
]
}
]
},
then: {
$mergeObjects: [
"$$this",
{
it_technologies: "$$this.technologies.name"
}
]
},
else: "$$this"
}
}
}
}
}
}
],
{
upsert: false,
multi: true,
})
Sample Mongo Playground

How to add new field conditionally in MongoDB?

I have an aggregation pipeline in which i want to add new field based on certain condition. My pipeline is like this
[
{ // match stage
$or:[
{
$and: [
{placement: {'$nin': [-1,-2]}},
{contract_proposal_metadata : {$exists: true}}
]
},
{
risk_info_request_metadata: {$exists: true}
}
]
}
]
Now i want to add a new field record_type based on the condition that if contract_proposal_metadata exists so record type will be 'renewal' and if risk_info_request_metadata is exists then record_type will be request.
How can i achieve this?
You are not adding new field conditionally. You are always adding the field, just with different values.
There is $cond operator which returns 1 of 2 values depending on condition in the first argument.
You already know $exist for the $match stage, and the equivalent operator to use in aggregation expression is $type
[
{ // match stage
.....
},
{ // adding the field
$addFields: {
record_type: { $cond: {
if: { $eq: [ { $type: "$contract_proposal_metadata" }, "missing" ] },
then: "request",
else: "renewal"
} }
}
}
]
You need to use aggregate update
db.collection.update({
placement: { //Your match goes here
"$nin": [
-1,
-2
]
},
},
[
{
$set: {
status: {
$switch: {
branches: [
{ //update condition goes here
case: {
$ifNull: [
"$contract_proposal_metadata",
false
]
},
then: "renewal"
},
{
case: {
$ifNull: [
"$risk_info_request_metadata",
false
]
},
then: "request"
},
],
default: ""
}
}
}
}
],
{
multi: true
})
It supported from mongo 4.2+
$exists cannot be used, hence $ifnull used
playground

Using $exists in MongoDB $cond

For background, I have to create a field in a collection, but ONLY if another known field (an array) is not empty. I'm trying to make a boolean filter with the $and and some conditions:
$set: {
clientsAllowed: {
$cond: {
if: {
$and: [
{
businessAllowed: { $exists: true },
},
{
businessAllowed: { $type: "array" },
},
{
businessAllowed: { $ne: [] },
},
],
},
then: [...],
else: [...],
},
}
}
But I get this error:
Invalid $set :: caused by :: Unrecognized expression '$exists'
Is the $exists wrongly formatted? Or is the $and stage? Any ideas?
Type cond for businessAllowed is enough. You don't need to check exists or not.
db.collection.aggregate([
{
$set: {
clientsAllowed: {
$cond: {
if: {
$and: [
{
"$eq": [
{
$type: "$businessAllowed"
},
"array"
]
},
{
$ne: [
"$businessAllowed",
[]
]
}
]
},
then: [],
else: []
}
}
}
}
])
mongoplayground

MongoDB Aggregation project field to be conditional value

I try to project my latest aggregation artifact, into a new one, with an additional field, whose value should be conditional.
My latest artifact of the aggregation pipeline is:
[
server: {
_id: 6108d87b9255993b26796ca9,
version: '2.0.366',
name: 'aaaaaa',
variables: [
{
key: 'aKey',
value: 'aVal',
},
{
key: 'bKey',
value: 'bVal',
},
{
key: 'cKey',
value: 'cVal',
}
],
__v: 0
}
]
So basically I want to project the additional artifact with an additional field whose name is bucketName.
I want to check in the variables array, whether there is an item, whose key is pre-known of value aKey. If so, the result of the projected bucketName would be the value field: aVal. If could not find, value would be NOT_FOUND.
So the result of the sample above would be:
[
server: {
_id: 6108d87b9255993b26796ca9,
version: '2.0.366',
name: 'aaaaaa',
variables: [
{
key: 'aKey',
value: 'aVal',
},
{
key: 'bKey',
value: 'bVal',
},
{
key: 'cKey',
value: 'cVal',
}
],
bucketName: 'aVal',
__v: 0
}
]
My try was:
{
$project: {
bucketName: {
$cond: {
if: {
$eq : ['$server.variables.key', 'aKey'],
},
then: '$server.variables.value',
else: 'NOT_FOUND',
},
},
},
},
But that just didn't do the query correctly.
You should use $in operator because this variables key is array:
Then, use also $filter within the then to extract that value.
db.collection.aggregate([
{
"$set": {
"bucketName": {
"$cond": {
"if": {
"$in": [
"aKey",
"$server.variables.key"
]
},
"then": {
"$filter": {
"input": "$server.variables",
"as": "variable",
"cond": {
"$eq": [
"$$variable.key",
"aKey"
]
}
},
},
"else": "NOT_FOUND"
}
}
},
},
{
"$unwind": "$bucketName",
},
{
"$project": {
"bucketName": "$bucketName.value",
"server": 1
}
}
])
you can check $in condition because server.variables.key will return an array of string
$indexOfArray to get index of matching key element from array server.variables.key
$arrayElemAt to select value from server.variables.value by about returned index
db.collection.aggregate([
{
$project: {
bucketName: {
$cond: {
if: { $in: ["aKey", "$server.variables.key"] },
then: {
$arrayElemAt: [
"$server.variables.value",
{ $indexOfArray: ["$server.variables.key", "aKey"] }
]
},
else: "NOT_FOUND"
}
}
}
}
])
Playground
Your $server.variables.key is an array, is that why $eq fails, you are comparing an string with an array. You need to use $in. Check this example.
But also there is a problem, as $server.variables.key is an array with all keys values, the bucketName variable will be an array too.
You have to use the string aKey into then stage like this
db.collection.aggregate({
"$set": {
"bucketName": {
"$cond": {
"if": {
"$in": [
"aKey",
"$server.variables.key"
]
},
"then": "aKey",
"else": "NOT_FOUND"
}
}
}
})