Meteor 1.0 - 使用变量作为键的Mongo查询,包括$ inc

时间:2015-06-20 11:07:48

标签: javascript mongodb meteor

我正在使用需要通过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);
  });

1 个答案:

答案 0 :(得分:2)

我认为麻烦来自你的ER模型。 sharpie不是独立实体,锐利是一个项目。 1项与其他项之间的关系是1项具有多项(1:M递归)并且每项配对具有权重。

完全标准化,你有一个物品表&重量表。 items表将包含这些项目。权重表会有item1item2weight(在这样做时,您可以使用非对称加权,例如sharpie:pencil = 1pencil: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。