问题描述
总的来说,我试图找到一种系统设计来快速查找其元数据与传入事件中捆绑的数据相匹配的存储对象。但是,哪些字段是必需的,它们本身就是存储对象的一部分,而不是我可以硬编码到查找查询中的字段。
我的系统有一个 policies
集合存储在 MongoDB 中,其中的文档如下所示:
{
id: 123,name: "Jason's Policy",requirements: {
"var1": "aaa","var2": "bbb"
// Could have any number more,and each policy can have different field/values under requirements
}
}
我的系统接收到如下所示的事件:
// Event 1 - matches all requirements under above policy
{
id: 777,"var1": "aaa","var2": "bbb"
}
// Event 2 - does not match all requirements from above policy since var1 is undefined
{
id: 888,"var2": "bbb","var3": "zzz"
}
当我接收事件时,如何有效地查找其要求被事件中接收到的值完全满足的所有策略?
例如,在上面的示例数据中,事件 1 应该返回政策(因为 var1 和 var2 符合政策要求),但事件 2 应该不返回政策(因为 var1 不匹配/丢失)。
我可以想到在应用服务器本身上执行此操作的蛮力方法(想想嵌套的 for
循环),但效率将是关键,因为我们每秒接收数百个事件。
我愿意接受可以满足一般问题的文档架构更改建议(根据我们文档中定义的标准本身查找文档)。我也愿意接受任何解决该问题的总体设计建议(也许有更好的方法来构建我们的系统以触发政策操作以响应事件)。
谢谢!
解决方法
不确定具体场景是什么,但可以在这里想到 2,
- 您需要完全匹配。为此,您可以运行以下
querydb.getCollection('test').find({'requirements':{'var1':'aaa','var2':'bbb'}})
要运行上述查询,您需要在对它的键 var1 和 var2 进行排序后保存需求对象。 - 您需要匹配所有存在的属性,并且不关心策略集合中是否有任何额外的内容。您需要更改存储为的策略,
{
"_id" : ObjectId("603250b0775428e32b9b303f"),"id" : 123,"name" : "Jason's Policy","requirements" : {
"var1" : "aaa","var2" : "bbb"
},"requirements_search" : [
"var1aaa","var2bbb","var3ccc"
]
}
然后你可以运行下面的查询,
db.getCollection('test').find({'requirements_search':{'$all' : ['var1aaa','var2bbb']}})
,
我在另一篇文章中找到了我的问题的答案:Find Documents in MongoDB whose with an array field is a subset of a query array。
MongoDB 提供了一个 $setIsSubset
运算符,可以检查文档的数组值是否是查询中数组值的子集。转化为我的用例:如果给定策略的要求是事件元数据的子集,那么我知道事件数据完全满足该策略的要求。
为了完整起见,下面是解决了我的问题的 MongoDB 聚合。我仍然需要研究是否有更高效的整体系统设计来满足我的需求,但至少,这个 Mongo 聚合将获取我需要的结果。
// Requires us to flatten policy requirements into an array like the following
//
// {
// "id" : 123,// "name" : "Jason's Policy",// "requirements" : [
// "var1_aaa",// "var2_bbb"
// ]
// }
//
// Event matches all policy requirements and has extra unrelated attributes
// {
// id: 777,// "var1": "aaa",// "var2": "bbb",// "var3": "ccc"
// }
db.collection.aggregate([
{$project: {
doc: '$$ROOT',isSubset: {$setIsSubset: ['$requirements',['var1_aaa','var2_bbb','var3_ccc']]}
}},{$match: {isSubset: true}},{$project: {_id: 0,'doc.name': 1}}
])