反向JSON查询:查找与对象匹配的集合中的所有查询

时间:2016-08-01 02:01:12

标签: mongodb mongodb-query nosql

我正在设计一个通用通知订阅系统,用户可以根据MongoDB查询或更一般的json查询在订阅时指定复合规则。订阅数据存储在MongoDB集合中。例如,

{ "userId": 1, "rule": {"p1": "a"} }
{ "userId": 2, "rule": {"p1": "a", "p2": "b"} }
{ "userId": 3, "rule": {"p3": {$gt: 3} } }

稍后当json对象形式的事件(如下所示)到达时,我想查找事件匹配的所有用户规则:

{"p1": "a", "p3": 4}

上述事件应与示例中userId 1和3指定的规则相匹配。事件对象不必存储在MongoDB中。

虽然我可以通过在应用层编写循环来满足要求。为了提高效率,我真的想在db层实现它,最好由于卷和延迟要求而允许分布式(分片)执行。

可以实现吗?任何帮助表示赞赏。事实上,只要支持动态事件模式,我就可以使用其他NOSQL dbs,并且可以指定复合规则。

3 个答案:

答案 0 :(得分:7)

至少在MongoDB中,你想要实现的目标是不可能的。

如果您推断查询引擎的工作原理,您会发现这不是一个简单的解决方案。

在高级术语中,引擎将从您的查询生成条件对象,然后将针对集合中的每个文档对其进行评估,这将导致布尔值,该布尔值确定文档是否属于结果集。 在你的情况下,你想要反过来做,你想要根据文档生成一个条件对象,然后将它应用于你给它的东西(例如一个对象)。

即使有可能,在DB上执行此操作的成本也会过高,因为它需要为每个对象编译表达式函数并执行它,并且无法优化查询的执行。 在数据库之外实际执行此操作更为合理,您可以在其中创建表达式函数。

答案 1 :(得分:2)

您无法在mongo数据库中存储“比较查询运算符”,但您可以这样做:

{ "userId": 1, "rule": {"p1": "a"} }
{ "userId": 2, "rule": {"p1": "a", "p2": "b"} }
{ "userId": 3, "rule": {"p3":  {"value": 3, "operator":"gt"} } }

您以字符串形式存储值AND OPERATOR,您可以进行如下查询:

db.test.find({"rule.p3.comparator":"gt", "rule.p3.value":{$lt:4}})

请注意,如果“运营商” “gt”,则必须在查询中使用 $ lt (相反的比较运算符)

你的完整例子是这样的:

db.test.find({$or:[{"rule.p3.comparator":"gt", "rule.p3.value":{$lt:4}}, {"rule.p1":"a"}]})

此查询与您想要的用户ID 1和3匹配

答案 2 :(得分:1)

更新:以下解决方案不起作用。 mongodb的问题在于它不使用NodeJ来运行map-reduce javascript,也不支持任何包管理器。所以很难使用任何第三方库。

我自己建议的解决方案,尚未确认

  1. 撰写符合json-query语法

  2. 的查询
  3. 到达evt后,在用户规则集合上调用MongoDB mapReduce函数以在mapper中调用jsonQuery

     var jsonQuery = require('json-query')
     var mapper = function(evt) {
         function map() {
             if(jsonQuery(this.rule, {data: evt}){
                 emit(this.userId);
             }
         }
         return map;
     };
     db.userRules.mapReduce(mapper(evt), ...);
    
  4. 将查询组成json查询语法而不是MongoDB查询语法的原因只是json-query提供了尝试将一个规则与一个对象匹配的jsonQuery方法。对于满足相关要求的上述代码,必须满足以下假设:

    1. MongoDB可以在分布式节点上执行mapReduce
    2. mapReduce中,我可以使用json-query之类的外部库,这意味着库代码必须分发给所有MongoDB节点,可能是因为关闭。