我正在设计一个通用通知订阅系统,用户可以根据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,并且可以指定复合规则。
答案 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,也不支持任何包管理器。所以很难使用任何第三方库。
我自己建议的解决方案,尚未确认:
撰写符合json-query语法
到达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), ...);
将查询组成json查询语法而不是MongoDB查询语法的原因只是json-query提供了尝试将一个规则与一个对象匹配的jsonQuery
方法。对于满足相关要求的上述代码,必须满足以下假设:
mapReduce
mapReduce
中,我可以使用json-query
之类的外部库,这意味着库代码必须分发给所有MongoDB节点,可能是因为关闭。