简体   繁体   中英

mongodb aggregate - match $nin array regex values

Must work in mongo version 3.4
Hi, As part of aggregating relevant tags, I would like to return tags that have script_url that is not contained in the whiteList array.
The thing is, i want to compare script_url to the regex of the array values.
I have this projection:

{
    "script_url" : "www.analytics.com/path/file-7.js",
    "whiteList" : [ 
        null, 
        "www.analytics.com/path/*", 
        "www.analytics.com/path/.*", 
        "www.analytics.com/path/file-6.js", 
        "www.maps.com/*", 
        "www.maps.com/.*"
    ]
}

This $match compares script_url to exact whiteList values. So the document given above passes when it shouldn't since it has www.analytics.com/path/. * in whiteList

{
    "$match": {
        "script_url": {"$nin": ["$whiteList"]}
    }
}

How do i match script_url with regex values of whiteList ?

update

I was able to reach this stage in my aggregation:

{
    "script_url" : "www.asaf-test.com/path/file-1.js",
    "whiteList" : [ 
        "http://sd.bla.com/bla/878/676.js", 
        "www.asaf-test.com/path/*"
    ],
    "whiteListRegex" : [ 
        "/http:\/\/sd\.bla\.com\/bla\/878\/676\.js/", 
        "/www\.asaf-test\.com\/path\/.*/"
    ]
}

But $match is not filtering out this script_url as it suppose to because its comparing literal strings and not casting the array values to regex values. Is there a way to convert array values to Regex values in $map using v3.4 ?

I know you specifically mentioned v3.4, but I can't find a solution to make it work using v3.4.

So for others who have less restrictions and are able to use v4.2 this is one solution.

For version 4.2 or later only

The trick is to use $filter on whitelist using $regexMatch (available from v4.2) and if the filtered array is empty, that means script_url doesn't match anything in whitelist

db.collection.aggregate([
  {
    $match: {
      $expr: {
        $eq: [
          {
            $filter: {
              input: "$whiteList",
              cond: {
                $regexMatch: { input: "$script_url", regex: "$$this" }
              }
            }
          },
          []
        ]
      }
    }
  }
])

Mongo Playground

It's also possible to use $reduce instead of $filter

db.collection.aggregate([
  {
    $match: {
      $expr: {
        $not: {
          $reduce: {
            input: "$whiteList",
            initialValue: false,
            in: {
              $or: [
                {
                  $regexMatch: { input: "$script_url", regex: "$$this" }
                },
                "$$value"
              ]
            }
          }
        }
      }
    }
  }
])

Mongo Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM