我正在使用需要通过Mongo查询高效的大型数据集。该应用程序使用Ford-Fulkerson算法计算推荐值并在多项式时间内运行,因此效率非常重要。语法是ES6,但一切都基本相同。
这是我正在使用的数据的近似值。一系列项目和一个项目与其他项目匹配:
let items = ["pen", "marker", "crayon", "pencil"];
let match = "sharpie";
最终,我们将迭代match
并将配对的权重增加1.因此,在完成该功能后,我的理想数据如下所示:
{
sharpie: {
pen: 1,
marker: 1,
crayon: 1,
pencil: 1
}
}
为了进一步说明,每个键旁边的值是该关系的weight
,也就是说,这些项目已配对在一起的次数。我想要发生的是这样的事情:
// For each in the items array, check to see if the pairing already
// exists. If it does, increment. If it does not, create it.
_.each(items, function(item, i) {
Database.upsert({ match: { $exist: true }}, { match: { $inc: { item: 1 } } });
})
问题当然是Mongo不允许使用括号表示法,也不允许将变量名称作为键(match
)。正如我所知,另一个问题是Mongo在深层嵌套$inc
运算符('The dollar ($) prefixed field \'$inc\' in \'3LhmpJMe9Es6r5HLs.$inc\' is not valid for storage.' }
)方面也存在问题。
我能做些什么来尽可能少地进行查询?我愿意接受建议。
修改
我尝试创建要传递给Mongo查询的对象:
_.each(items, function(item, i) {
let selector = {};
selector[match] = {};
selector[match][item] = {};
let modifier = {};
modifier[match] = {};
modifier[match]["$inc"] = {};
modifier[match]["$inc"][item] = 1
Database.upsert(selector, modifier);
不幸的是,它仍然不起作用。 $inc
打破了查询,它不会让我超过1级更改任何内容。
解决方案
这是我最终实现的功能。它就像一个魅力!谢谢马特。
_.each(items, function(item, i) {
let incMod = {$inc:{}};
let matchMod = {$inc:{}};
matchMod.$inc[match] = 1;
incMod.$inc[item] = 1;
Database.upsert({node: item}, matchMod);
Database.upsert({node: match}, incMod);
});
答案 0 :(得分:2)
我认为麻烦来自你的ER模型。 sharpie
不是独立实体,锐利是一个项目。 1项与其他项之间的关系是1项具有多项(1:M递归)并且每项配对具有权重。
完全标准化,你有一个物品表&重量表。 items表将包含这些项目。权重表会有item1
,item2
,weight
(在这样做时,您可以使用非对称加权,例如sharpie:pencil = 1
,pencil:sharpie = .5
,这是在计算FFA中的后推时很有用,但我不认为这适用于您的情况。
很好,现在让我们把它搞砸了。
当我们说1项有很多项目时,“很多”可能不会超过几千(想想16MB文件上限)。这意味着它实际上是1对少,这意味着我们可以使用subdocs或字段来嵌套数据。
所以,让我们看看那个架构!
doc =
{
_id: "sharpie",
crayon: 1,
pencil: 1
}
我们看到了什么? sharpie
不是密钥,而是值。这使一切变得简单。我们将这些项目保留为字段。我们不使用对象数组的原因是因为它更快和更快。更清洁(无需迭代数组以找到匹配的_id
)。
var match = "sharpie";
var items = ["pen", "marker", "crayon", "pencil"];
var incMod = {$inc:{}};
var matchMod = {$inc:{}};
matchMod.$inc[match] = 1;
for (var i = 0; i < items.length; i++) {
Collection.upsert({_id: items[i]}, matchMod);
incMod.$inc[items[i]] = 1;
}
Collection.upsert({_id: match}, incMod);
这很容易。困难的部分是弄清楚为什么要使用FFA作为建议引擎:-P。