skip to Main Content

I have subscriptions collection in the database, e.g:

[{
  _id: new ObjectId(...),
  query: {
    size: { $in: [1, 2, 3] }
  }
}, {
  _id: new ObjectId(...),
  query: {
    name: { $eq: 'Shirt' },
  }
}]

I need to find all subscriptions whose query matches the object. Is it possible? E.g:

Subscriptions.aggregate([{
  $reverseMatch: {
    size: 1,
    name: 'Shirt',
  },
}]);

2

Answers


  1. Subscriptions.aggregate([
      {
        $match: {
          $nor: [
            { "query.size": 1 }, // Exclude documents where size is 1
            { "query.size": 2 }, // Exclude documents where size is 2
            { "query.size": 3 }  // Exclude documents where size is 3
          ]
        }
      }
    ]);
    

    This query effectively filters out subscriptions where the size field inside the query object matches any of the specified values (1, 2, or 3), thereby giving us the subscriptions that do not match the specified object. It’s a neat way to handle such filtering requirements in MongoDB aggregation pipelines.

    Login or Signup to reply.
  2. Assuming OP is asking about functionality similar to percolate query on ElasticSearch, it’s a design flaw, but rather a niche requirement.

    The short answer is "NO" – there is no way to implement implement it with an arbitrary query stored in a document.

    The problem lays in the separation between data and commands on protocol level. A json stored as a value remains data for life. There is no way to use it as a command / filter / query or any other control structure. Basically it’s fundamentally not possible to store a query / filter with native mongodb syntax in the database to run it against a document / collection on db side itself.

    What you can do:

    implement own dsl similar to mongodb syntax

    generally impossible task, but if you can limit query syntax to reasonable operations you can end up with something useful. An example below implements $eq and $in queries as in the OP. I didn’t test it thoughtfully, it’s there just to demonstrate scale of complexity of the approach.

    db.Subscriptions.aggregate([
      {
        "$addFields": {
          "query": {
            "$objectToArray": "$query"
          },
          "doc": {                               // <== the document to query against
            $objectToArray: {
              "size": 1,
              "name": "Shirt"
            }
          }
        }
      },
      {
        "$addFields": {
          "match": {
            "$reduce": {                        // <== query parser
              "input": "$query",
              "initialValue": true,
              "in": {
                $and: [                         //  <== logical AND for all fields in the query
                  "$$value",
                  {
                    $gt: [
                      {
                        $size: {
                          "$filter": {            // <== number of matching fields in the document
                            "input": "$doc",
                            "as": "prop",
                            "cond": {
                              $and: [
                                {
                                  "$eq": [
                                    "$$prop.k",
                                    "$$this.k"
                                  ]
                                },
                                {
                                  $or: [                // <== filtering engine
                                    {
                                      "$eq": [          // <== $eq matcher
                                        "$$prop.v",
                                        "$$this.v.eq"    
                                      ]
                                    },
                                    {
                                      "$in": [          // <== $in matcher
                                        "$$prop.v",
                                        {
                                          "$ifNull": [
                                            "$$this.v.in",
                                            []
                                          ]
                                        }
                                      ]
                                    }
                                  ]
                                }
                              ]
                            }
                          }
                        }
                      },
                      0
                    ]
                  }
                ]
              }
            }
          }
        }
      },
      {
        "$match": {
          "match": true
        }
      },
      {
        "$project": {
          "doc": 0,
          "match": 0
        }
      }
    ])
    

    And even for this approach the syntax for the "query" field in the subscription document should be adjusted to avoid $ sign:

    [{
      _id: new ObjectId(...),
      query: {
        size: { in: [1, 2, 3] }
      }
    }, {
      _id: new ObjectId(...),
      query: {
        name: { eq: 'Shirt' },
      }
    }]
    

    implement matching engine in javascript

    Using $function gives you much more flexibility. It would be easier to implement the matching engine there, but it comes with massive performance impact, and it will likely be disabled for security, so won’t go into details on this one. Just mentioning it as an option. Again, reaching 100% compatibility for arbitrary query is an enormous task, so you will need to put reasonable limitations on supported syntax.

    implement on application level

    If the subscription collection is reasonably small (like 1,000s to 10,000s of documents), you can load all documents to the serverside application, and run the queries in parallel. By default driver’s pull is 100 connections, which let you run 100 subscription queries at a time.

    You will need to store the

    {
        size: 1,
        name: 'Shirt',
    }
    

    document in a temporary collection, and run the subscription queries against it.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search