I have a collection:
{
"_id": 1,
"_deleted": false,
"customFields": [{
"fieldName": "sapID",
"value": ""
}, {
"fieldName": "salesTerritory",
"value": ""
}, {
"fieldName": "clientType",
"value": "Corporate"
}],
}
How can I project(aggregate) only the value field of the element with fieldName = "clientType":
db.collection.aggregate(
{
$project:{value:<code>}
}
)
I tried $filter but it does not work
db.collection.aggregate([
{
$project: {
"customFields.value": 1
}
}
])
mongoplayground
db.collection.aggregate([
{
$set: {
customFields: {
$map: {
input: "$customFields",
as: "c",
in: {
$cond: {
if: { "$eq": [ "$$c.fieldName", "clientType" ] },
then: { value: "$$c.value" },
else: "$$c"
}
}
}
}
}
}
])
mongoplayground
What about this?
db.collection.aggregate([
{
$project: {
customFields: {
$filter: {
input: "$customFields",
$cond: { $eq: ["$$this.fieldName", "clientType"] }
}
}
}
}
])
Mongo Playground
{
$cond: {
if: { $in: [`$$${field.fieldName}`, ['', null]] },
then: [],
else: `$$${field.fieldName}`,
},
},
In this condition, I also want to check the field.fieldName exits.
$exists: true,
then it will something like
if ( !filed.fieldName || field.fieldName === null || field.fieldName === '') then []
I am not finding any way to add $exits: true or false in this.
Please help if can add something to this. Or any alternate way to achieve ?
You can use $or
$ifNull
Evaluates an expression and returns the value of the expression if the expression evaluates to a non-null value. If the expression evaluates to a null value, including instances of undefined values or missing fields, returns the value of the replacement expression.
{ $ifNull: [ <expression>, <replacement-expression-if-null> ] }
{
$cond: {
if: {
$or : [
{ $in: [`$$${field.fieldName}`, ['', null]] },
{ $ifNull: [`$$${field.fieldName}`, ""] }
]},
then: [],
else: `$$${field.fieldName}`,
}
},
You just need to put $ifNull in else part, if it's not exists it will return [],
{
$cond: {
if: {
$in: [`$$${field.fieldName}`, ["", null]]
},
then: [],
else: { $ifNull: [`$$${field.fieldName}`, []] }
}
}
Playground
Input:
[
{ "key": null },
{ "key": "" },
{ "A": "a" }
]
Result:
[
{
"key": null,
"status": []
},
{
"key": "",
"status": []
},
{
"status": []
}
]
Second approach, if you want to add an existing condition in if part you can try condition with $or,
$type will return the field's data type the missing field type is "missing"
{
$cond: {
if: {
$or: [
{ $eq: [{ $type: `$$${field.fieldName}` }, "missing"] },
{ $in: [`$$${field.fieldName}`, ["", null]] }
]
},
then: [],
else: `$$${field.fieldName}`
}
}
Playground
Bear in mind, "field.fieldName exists" might be different to "field.fieldName is not null".
Consider special values like this:
db.collection.insertMany([
{ _id: 1, a: 1 },
{ _id: 2, a: '' },
{ _id: 3, a: undefined },
{ _id: 4, a: null },
{ _id: 5 }
])
db.collection.aggregate([
{
$set: {
type: { $type: "$a" },
ifNull: { $ifNull: ["$a", true] },
defined: { $ne: ["$a", undefined] },
existing: { $ne: [{ $type: "$a" }, "missing"] }
}
}
])
{ _id: 1, a: 1, type: double, ifNull: 1, defined: true, existing: true }
{ _id: 2, a: "", type: string, ifNull: "", defined: true, existing: true }
{ _id: 3, a: undefined, type: undefined, ifNull: true, defined: false, existing: true }
{ _id: 4, a: null, type: null, ifNull: true, defined: true, existing: true }
{ _id: 5, type: missing, ifNull: true, defined: false, existing: false }
So, condition could be this one, depending on your requirements:
{
$cond: {
if: { $ne: [{ $type: "$field.fieldName" }, "missing"] },
then: [],
else: "$field.fieldName",
}
}
For sake of completeness: With db.collection.find():
db.collection.find({ a: { $exists: false } })
{ _id: 5 }
db.collection.find({ a: { $exists: true} })
{ _id: 1, a: 1 },
{ _id: 2, a: '' },
{ _id: 3, a: undefined },
{ _id: 4, a: null }
db.collection.find({ a: null })
{ _id: 3, a: undefined },
{ _id: 4, a: null },
{ _id: 5 }
db.collection.find({ a: {$ne: null} })
{ _id: 1, a: 1 },
{ _id: 2, a: '' },
db.collection.find({ a: {$type: "null"} })
{ _id: 4, a: null }
Try this:
{
$cond: {
if: { $ne : [`$$${field.fieldName}`, undefined] },
then: [],
else: `$$${field.fieldName}`,
},
}
I am trying to match nested fields in a document in mongo but my query is not working.
Configuration:
[
{
linenumber: "car",
type: "004",
nested: {
info: {
subinfo: "B"
}
},
},
{
linenumber: "car",
type: "005",
nested: {
info: {
subinfo: "G"
}
},
}
]
query:
db.collection.aggregate([
{
$match: {
$and: [
{
linenumber: "car"
},
{
nested: {
info: {
subinfo: {
$in: [
"B",
"G"
]
}
}
}
}
]
}
},
{
$group: {
_id: null,
types: {
$addToSet: "$type"
}
}
}
])
I am using the mongoplayground to test. Here is the link:
https://mongoplayground.net/p/nND59iO1339
I am getting no documents found.
You are almost there. You need to use dot notation.
play
db.collection.aggregate([
{
$match: {
$and: [
{
linenumber: "car"
},
{
"nested.info.subinfo": { //Dot notation
$in: [
"B",
"G"
]
}
}
]
}
},
{
$group: {
_id: null,
types: {
$addToSet: "$type"
}
}
}
])
I need to filter all documents by using multiple conditions.
{
"name": "Maths",
"excludeUserIds": [
{
"_id": 33,
"groupIds": [
"a",
"b",
"c"
]
}
]
},
{
"name": "Science",
"excludeUserIds": [
{
"_id": 24,
"groupIds": [
"a",
"x",
"b"
]
}
]
},
{
"name": "Health",
"excludeUserIds": []
},
{
"name": "English",
"excludeUserIds": [
{
"_id": 33,
"groupIds": []
}
]
}
The documents need to be returned if excludeUserIds._id not equal to given value AND excludeUserIds.groupIds are not in given array.
The mongo script I've written is
{
"$match": {
"excludeUserIds": {
"$elemMatch": {
"_id": {
$ne: 33
},
"groupIds": {
$nin: [
"a"
]
}
}
}
}
}
I expect this should return all document without Maths. But i doesn't work as I expected. Obviously I missed something. Thanks in advance. I created a Mongo playground
If the requirement is to return all subjects except Maths, your logic should be
if excludeUserIds._id not equal to given value AND OR excludeUserIds.groupIds are not in given array.
Which can be translated to the following code, which convert the logic from !A || !B to the equivalent !(A && B) for shorter code.
db.collection.aggregate([
{
$match: {
excludeUserIds: {
$not: {
$elemMatch: {
_id: 33,
groupIds: { $all: ["a"] }
}
}
}
}
}
])
Playground
If you only need to match excludeUserIds.groupIds against one value, instead of an array, you can do the following
db.collection.aggregate([
{
$match: {
excludeUserIds: {
$not: {
$elemMatch: {
_id: 33,
groupIds: "a"
}
}
}
}
}
])
Playground
Try using this
db.collection.find({
"excludeUserIds._id": { $ne: 33 },
"excludeUserIds.groupIds": { $nin: ["a"] }
})
This is probably not the solution but will give required result.
db.collection.aggregate([
{
"$match": {
"excludeUserIds": {
"$elemMatch": {
"_id": {
$ne: 33
},
"groupIds": {
"$elemMatch": {
$nin: [
"a"
]
}
}
}
}
}
}
])
Play
You need to use $elemMatch for nested array as well.
EDIT:
Updated Play
db.collection.aggregate([
{
"$match": {
"$or": [
{
"excludeUserIds": {
"$elemMatch": {
"_id": {
$ne: 33
},
"groupIds": {
"$elemMatch": {
$nin: [
"a"
]
}
}
}
}
},
{
"$or": [
{
excludeUserIds: {
$exists: false
}
},
{
excludeUserIds: null
},
{
excludeUserIds: {
$size: 0
}
}
]
},
{
"$or": [
{
"excludeUserIds.groupIds": {
$exists: false
}
},
{
"excludeUserIds.groupIds": null
},
{
"excludeUserIds.groupIds": {
$size: 0
}
}
]
}
]
}
}
])
You can check for existence/empty to get those as well.
I have collections in mongoDb like this:
{
_id: ObjectId(""),
fieldA: "123",
fieldB: {
subFieldB:[
{
fieldC: ""
},
{
fieldC: ""
},
{
fieldC: ""
}
]
}
},
{
_id: ObjectId(""),
fieldA: "123",
fieldB: {
subFieldB:[
{
fieldC: ""
},
{
fieldC: ""
},
{
fieldC: ""
}
]
}
},
{
_id: ObjectId(""),
fieldA: "456",
fieldB: {
subFieldB:[
{
fieldC: ""
},
{
fieldC: ""
},
{
fieldC: ""
}
]
}
}
I want to get sum of number of subFieldB where fieldA is "123"
So the goal is getting something like {sumOfSubFieldB: 6}
I tried aggregate like:
db.collection.aggregate([
{
$match : { "fieldA" : "123" }
},
{
$project: {
numberOfFieldB: { $cond: { if: { $isArray: "$fieldB.subFieldB" }, then: { $size: "$fieldB.subFieldB" }, else: "NA"} },
}
}
] )
So far I get sum of fieldB per doc like:
{
"_id" : ObjectId(""),
"numberOfFieldB" : 3
}
{
"_id" : ObjectId(""),
"numberOfFieldB" : 3
}
How should I do the query to get total number of fieldB?
Thank you
Try this one, or something similar with $group:
db.collection.aggregate(
[{
$match: {
"fieldA": "123"
}
}, {
$group: {
_id: "$fieldA",
count: {
$sum: { $size: "$fieldB.subFieldB" } }
}
}]
);