MongoDB - 插入到两个集合中,一个集合引用另一个作为子文档

MongoDB - insert into two collections, one collection referencing the other as subdocument

Meteor 的新手和 MongoDB,来自关系数据库背景。这个问题其实反映了我对MongoDB中关系是如何处理的困惑。

示例:我想将一个食谱插入到一个食谱集合中,在本例中是一个以逗号分隔的包含多种成分的字符串。但是,我想同时将这些配料插入到配料集合中。我想让食谱参考配料集合中的配料,这样一来,如果我第一次拼错了配料,我可以稍后在配料集合中更新它,并拥有所有使用该配料的食谱也更新了。

似乎要做到这一点的方法是将配料集合作为子文档包含在食谱集合中。

但是,我不确定如何才能真正实现它。 JS中使用Meteor的示例代码如下:

Recipes = new Mongo.Collection("recipes");
Ingredients = new Mongo.Collection("ingredients");

Template.body.events({
    "submit .new-recipe": function(event) {
        // Prevent default browser form submit
        event.preventDefault();

        // Get value from form element
        var text = event.target.text.value;
        var splitText = text.split(",");
        var nInputs = splitText.length;
        var recipe = [];
        for (var i = 0; i < nInputs; i++) {
            // Insert an ingredient into the ingredients collection
            Ingredients.insert({
                itemName: splitText[i].trim(),
                createdAt: new Date() 
            });
            recipe.push(splitText[i]);
        }
        // Insert the list of ingredients as a recipe into the recipes collection
        Recipes.insert({
            recipe: recipe,
            createdAt: new Date()
        });
        // Clear form
        event.target.text.value = "";
    }
});

显然,上面的方法并没有正确完成工作。它切断了成分和食谱之间的关系。但是我怎样才能维持这种关系呢?我是否在插入配料时将配料的 ID 放入食谱集合中?在插入配料时,我是否将整个配料文档作为食谱文档的一部分插入到食谱集合中?

听起来您需要两个集合之间的简单关系模型。这通常是通过将一个集合的 _id 存储为另一个集合中的值来实现的。对于您的情况,我建议将成分 ID 存储为配方中的数组。我发现您的初始尝试存在一些问题:

  1. 在插入之前未检查成分的存在。所以使用 "sugar" 的两个食谱会插入两个糖文件 - 我想这不是你的意图。

  2. 插入发生在客户端,但除非您发布整个成分集合,否则客户端不能成为成分实际存在的权威(来自 1 ).

  3. 您在插入时使用了客户端的时间戳。如果他们的时钟不对怎么办?实际上有 a package 可以解决这个问题,但是我们可以使用一种方法解决上述所有问题。


我建议在客户端拆分文本输入,然后发出 Meteor.call('recipes.insert', ingredientNames),方法实现如下所示:

Meteor.methods({
  'recipes.insert': function(ingredientNames) {
    // Make sure the input is an array of strings.
    check(ingredientNames, [String]);

    // Use the same createdAt for all inserted documents.
    var createdAt = new Date;

    // Build an array of ingredient ids based on the input names.
    var ingredientIds = _.map(ingredientNames, function(ingredientName) {
      // Trim the input - consider toLowerCase also?
      var name = ingredientName.trim();

      // Check if an ingredient with this name already exists.
      var ingredient = Ingrediends.findOne({itemName: name});
      if (ingredient) {
        // Yes - use its id.
        return ingredient._id;
      } else {
        // Insert a new document and return its id.
        return Ingrediends.insert({
          itemName: name,
          createdAt: createdAt
        });
      }
    });

    // Insert a new recipe using the ingredient ids to join the
    // two collections.
    return Recipes.insert({
      ingredientIds: ingredientIds,
      createdAt: createdAt
    });
  }
});

推荐阅读:

  • This question 关于 meteor 中的关系模型。