Given a list of documents like:
[
{
_id: 1,
field1: "test",
field2: "value2",
fields: [
"field1",
"field2"
]
},
{
_id: 2,
field1: "value1",
field2: "test",
fields: [
"field1",
"field2"
]
},
{
_id: 3,
field1: "test",
field2: "value2",
fields: [
"field2"
]
},
{
_id: 4,
field1: "value1",
field2: "value2",
fields: [
"field1"
]
},
{
_id: 5,
field1: "test",
field2: "test",
fields: []
}
]
I want to retrieve all documents that have any of the specified fields in the array, with a value “test”. For instance, in document with _id: 1
, field2
is a value of fields
array, and field2
has test
as value. In contrast, document with _id: 5
has keys field1
and field2
with test value, but none of this fields is present in fields array. So, given the previous list of documents, only documents with _id: 1
and _id: 2
should be returned.
a possible approach as steps
field1
,field2
.. so that it can be filtered/searchedAn approach with $anyElementTrue
in the match stage - demo
db.collection.aggregate([
{
$addFields: {
keyValuePairArray: {
$filter: {
input: { $objectToArray: "$$ROOT" },
cond: { $not: { $in: [ "$$this.k", ["_id", "fields"] ] } }
}
}
}
},
{
$match: {
$expr: {
$anyElementTrue: {
$map: {
input: "$keyValuePairArray",
in: {
$and: [
{ $in: [ "$$this.k", "$fields" ] },
{ $eq: [ "$$this.v", "test" ] }
]
}
}
}
}
}
},
{ $unset: "keyValuePairArray" }
])
Another approach with $filter
in the match stage by checking its size - demo
db.collection.aggregate([
{
$addFields: {
keyValuePairArray: {
$filter: {
input: { $objectToArray: "$$ROOT" },
cond: { $not: { $in: [ "$$this.k", ["_id", "fields"] ] } }
}
}
}
},
{
$match: {
$expr: {
$gt: [
{
$size: {
$filter: {
input: "$keyValuePairArray",
cond: {
$and: [
{ $in: [ "$$this.k", "$fields" ] },
{ $eq: [ "$$this.v", "test" ] }
]
}
}
}
},
0
]
}
}
},
{ $unset: "keyValuePairArray" }
])
EDIT - you might not even need the temporary field by removing the _id
and fields
since they will not affect in the $match
comparison anyway - check demo
db.collection.aggregate([
{
$match: {
$expr: {
$anyElementTrue: {
$map: {
input: { $objectToArray: "$$ROOT" },
in: {
$and: [
{ $in: [ "$$this.k", "$fields" ] },
{ $eq: [ "$$this.v", "test" ] }
]
}
}
}
}
}
}
])