I have a collection of documents like this:
[{
"_id" : ObjectId("6347e5aa0c009a37b81da700"),
"testField1" : "1000",
"testField2" : "2000",
"testField3" : NumberInt(1)
},
{
"_id" : ObjectId("6347e5890c009a37b81da701"),
"testField2" : 2000,
"testField3" : NumberInt(2)
},
{
"_id" : ObjectId("6347e5960c009a37b81da702"),
"testField3" : NumberInt(3)
}]
I need to retrieve documents in the below precedence.
- if testField1 and testField2 exist and match their values, the query should return that document.
- Otherwise, if testField2 exists and matches its value, the query should return that document,
- Otherwise it should return the last document, where testField1 & testField2 do not exist.
I tried the below query, but it returns all the documents.
db.getCollection("TEST_COLLECTION").aggregate([
{
$match: {
$expr: {
$cond: {
if: {
$and: {"testField1": "1000", "testField2": "2000"}
},
then: {
$and: {"testField1": "1000", "testField2": "2000"}
},
else : {
$cond: {
if: {
$and: {"testField1": null, "testField2": "2000"}
},
then: {
$and: {"testField1": null, "testField2": "2000"}
},
else : {
$and: {"testField1": null, "testField2": null}
}
}
}
}
}
}
}
])
2
Answers
There are definitely still some open questions from the comments. @ray has an interesting approach linked in there that uses
$setWindowFields
which may be appropriate depending on exactly what you’re looking for.I took a different approach (and perhaps interpretation) and built out the following aggregation that uses
$unionWith
:Basically the aggregation will internally issue three queries, one corresponding with each of three precedence conditions. Similar to @ray’s solution, it creates a field to sort on (
sortOrder
in mine) since the ordering of$unionWith
is unspecified otherwise per the documentation. After the$sort
we can$limit
to a single result and$unset
the temporary sorting field prior to returning the result to the client. Depending on the version you are running, you could consider adding a couple of inline$limit
s for each of the subpipelines to reduce the amount of work being done. Along with appropriate indexes (perhaps just{ testField2: 1, testField: 1 }
), this operation should be reasonably efficient.Here is the playground link.
If there are several groups and you need to return the wanted document per group, I would go with @ray’s answer. If there is only one group (as implies on your comment, and on @user20042973’s nice answer), I would like to point another obvious option:
See how it works on the playground example