Is there a way to apply an arbitrary condition to a MongoDB aggregation?

I have a MongoDB database where people can enter relatively arbitrary search conditions (in this case they are vetted, but not restricted conceptually). An example of a condition a user could specify may look like:

{"$and": [
  {"details.vulns": "cve-2019-19781"},
]}

This works well for searching, however, I want to be able to also generate statistics based on older data, for which I use an aggregation that looks something like:

db.hosts.aggregate([
    {"$sort": {"timestamp": 1}},
    {"$addFields": {
        "condition": {$cond: [
            {"$and": [
                {"details.vulns": "cve-2019-19781"},
            ]},
            1,
            0,
        ]}
    }},
    {"$project": {
        "ip":        "$ip",
        "timestamp": "$timestamp",
        "condition": "$condition",
    }},
]);

The problem with such a query is MongoDB does not seem to allow using a $cond with something like $details.vulns . I understand a standard approach would be to unwind, however, due to the conditions being not known before my code executes, I have no way to know what fields to unwind unless I re-implement a MongoDB parsing engine in my own code.

Is there an alternative approach here I am missing, or does MongoDB simply lack the ability to apply an arbitrary search clause during an aggregation? If there is a slow way of doing it, I would be fine with that.

Hello : )

I can’t say that i understand what you need,but in case you need to save mongoQL code in database you can use $literal,and save that condition in database as a embedded document.

Also the use of function call inside of json can provide “dynamic” queries,like generate
the query json,from a function.For example dont write by hand the condition,have a function
that generated the mongoQL code.

Or the combination,save the condition,and generate queries that use that condition.

To demonstrate the issue more clearly, here is some example data. Keep in mind that this is simplified – the real data contains many different fields that I may wish to query against, hence the above statements about unwind not being a satisfactory solution:

// Objects
{ "_id" : ObjectId("5cfc73657e2438b115888d1b"), "ip" : NumberLong("12345"), "timestamp" : ISODate("2019-06-09T02:45:45Z"), "vulns" : [ "cve-2019-19781" ] },
{ "_id" : ObjectId("5d04c5497e2438b115b06659"), "ip" : NumberLong("12345"), "timestamp" : ISODate("2019-06-15T10:13:33Z"), "vulns" : [ "" ] },
{ "_id" : ObjectId("5d108c52211d917c6ff48bfd"), "ip" : NumberLong("12345"), "timestamp" : ISODate("2019-06-24T08:37:31Z"), "vulns" : [ "cve-2019-19781", "other-vuln" ] },

// Desired output from aggregate
{"ip" : NumberLong("12345"), "timestamp" : ISODate("2019-06-09T02:45:45Z"), "condition": 1 },
{"ip" : NumberLong("12345"), "timestamp" : ISODate("2019-06-15T10:13:33Z"), "condition": 0 },
{"ip" : NumberLong("12345"), "timestamp" : ISODate("2019-06-24T08:37:31Z"), "condition": 1 },

Hi Stephen,

You can do this using $filter for an array.

Refer to this documentation for more info.