MongoDB - 插入两个集合,一个集合引用另一个作为子文档 [英] MongoDB - insert into two collections, one collection referencing the other as subdocument

查看:75
本文介绍了MongoDB - 插入两个集合,一个集合引用另一个作为子文档的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

New to Meteor and MongoDB, coming from a relational database background. This question actually reflects my puzzle at how relations are handled in MongoDB.

示例:我想将一个食谱插入到一个食谱集合中,在本例中,它是由多个成分组成的逗号分隔字符串.但是,我想同时将这些成分插入到成分集合中.而且我想让配方参考成分集合中的成分,这样,比如说,如果我第一次弄错了成分的拼写,我可以稍后在成分集合中更新它,并拥有使用该成分的所有食谱也更新了.

Example: I want to insert a recipe into a collection of recipes, which in this case is a comma delimited string of multiple ingredients. I want, however, to insert these ingredients into a collection of ingredients at the same time. And I would like to have the recipe refer to the ingredients in the ingredients collection, so that, say, if I got an ingredient's spelling wrong the first time, I could update it in the ingredients collection later and have all recipes that use that ingredient updated as well.

似乎这样做的方法是将成分集合作为子文档包含在食谱集合中.

It seems that the way to do it is to include the ingredients collections as subdocuments in the recipes collection.

但是,我不确定如何实际实施.使用 Meteor 的 JS 示例代码如下:

However, I'm not sure how I can actually implement that. Sample code in JS using Meteor follows:

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 放入配方集合中?在插入配料时,我是否将整个配料文档作为配方文档的一部分插入到配方集合中?

Obviously, the above doesn't do the job correctly. It severs the relationship between the ingredients and the recipes. But how can I maintain the relationship? Do I put the Ids of the ingredients into the recipes collection at the time of inserting the ingredients? Do I have the entire ingredient document inserted as a part of a recipe document into the recipes collection, at the time of inserting the ingredients?

推荐答案

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

It sounds like you need a simple relational model between two collections. This is typically accomplished by storing the _id of one collection as a value in another. In your case, I'd recommend storing the ingredient ids as an array in the recipe. I see a few issues with your initial attempt:

  1. 在插入之前未检查成分是否存在.因此,使用糖"的两个食谱会插入两个糖文件 - 我认为这不是您的意图.

  1. The existence of an ingredient is not being checked prior to insertion. So two recipes using "sugar" would insert two sugar documents - I assume this is not your intention.

插入是在客户端上进行的,但除非您要发布整个成分集合,否则客户端不能成为实际存在成分的权威(从 1 开始).

The insert is happening on the client, but unless you are publishing your whole ingredient collection, the client can't be the authority on which ingredients actually exist (following from 1).

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

You are using the client's timestamp when doing the insert. What if their clock is wrong? There's actually a package that deals with this, but we can solve all of the above using a method.

<小时>

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


I'd recommend splitting the text input on the client and then issuing a Meteor.call('recipes.insert', ingredientNames), where the method implementation looks something like this:

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 on relational models in meteor.

这篇关于MongoDB - 插入两个集合,一个集合引用另一个作为子文档的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆