Mongodb 同一架构中的引用字段

Mongodb 同一架构中的引用字段,mongodb,mongoose,Mongodb,Mongoose,是否可以引用同一架构中的字段?请参见下面的示例。还是我走错了方向 var UserSchema = new mongoose.Schema ({ username: String, password: String, email: String, foods: [{ name: String, category: String, ingredients: // how to reference values in the ing

是否可以引用同一架构中的字段?请参见下面的示例。还是我走错了方向

var UserSchema = new mongoose.Schema ({
    username: String,
    password: String,
    email: String,
    foods: [{
      name: String,
      category: String,
      ingredients: // how to reference values in the ingredients array?
    }], 
    ingredients: [{
      name: String,
      category: String
    }]  
});

简短回答

这是MongoDB的核心设计决策:

存储对对象的引用,而不是像在关系数据库中那样存储对象的独立副本,这在MongoDB中是可能的,而且通常都是这样,当您需要查找对象时,它只会导致越来越复杂的查询

长答案

如果目标只是保持成分模式的定义一致,那么可以定义一个模式并使用两次。成分将作为独立副本储存,例如:

[{  username: 'bob', 
    ingredients: [  { name: 'Carrot', category: 'Vegetable' } , ...],
    foods:  [ { name: 'Salad', category: 'Lunch', ingredients: [ { name: 'Carrot', category: 'Vegetable'}, ...]}]
}, ...]


var IngredientSchema = new mongoose.Schema({
  name: String,
  category: String,
});


var UserSchema = new mongoose.Schema ({
    username: String,
    password: String,
    email: String,
    foods: [{
      name: String,
      category: String,
      ingredients: [IngredientSchema] // brackets indicates it's an array, 
    }], 
    ingredients: [IngredientSchema]  
});
或者,您可以通过objectId引用成分:

var UserSchema = new mongoose.Schema ({
     username: String,
     password: String,
    email: String,
    foods: [{
      name: String,
      category: String,
      ingredients: [mongoose.Schema.Types.ObjectId] // IDs reference individual ingredients, 
    }], 
    ingredients: [IngredientSchema]  
});
通过显式定义IngreditSchema,每个Component对象在声明时都会获得自己的ObjectId。存储成分ID(而不是成分对象的副本)的好处是更简洁和一致的存储。缺点是会有越来越复杂的查询

[{  username: 'bob', 
    ingredients: [  { _id: ObjectId('a298d9ef898afc98ddf'), name: 'Carrot', category: 'Vegetable' } , ...],
    foods:  [ { name: 'Salad', category: 'Lunch', ingredients: [ {$oid: 'a298d9ef898afc98ddf'}, ]}]
}, ...]
如果您想存储对配料的引用,更好的方法可能是将配料存储为自己的第一类集合。当您想按配料或按食品配料查找食品时,仍然会有许多单独的查询,但查询会更简单

var UserSchema = new mongoose.Schema ({
     username: String,
     password: String,
     email: String,
     foods: [{
      name: String,
      category: String,
      ingredients: [mongoose.Schema.Types.ObjectId] // IDs reference individual ingredients, 
    }], 
    ingredients: [mongoose.Schema.Types.ObjectId]  
});
如果目标是存储对配料的规范化引用,并根据它们搜索食品,那么引用另一个[SO post][1],“这是关系数据库真正发挥作用的案例之一”

有关按Id查询子文档的信息,请参见本SO帖子:


正如一位受访者指出的,“这是关系数据库真正闪耀的案例之一”

这是否意味着如果我想使用引用而不是嵌入,我必须创建两个模式?我最初的想法是将“成分”字段保留在用户模式下,因为每个用户都可以定义自己的成分集。现在有了新的IngreditSchema,我将把所有用户的成分存储在一个模式(集合)下。那不是我真正需要的。难道没有其他方法吗?另外,试着理解为什么您建议引用foods.Components字段和Components字段中的InCreditSchema。你能给我解释一下吗?在我给出的示例中,配料只有一个模式,但配料对象在每种食物中都是以副本而不是引用的形式逐字存储的。但我知道你想通过ID来引用成分,而不是重复。将编辑上面的答案…非常感谢更新的答案,它为我的问题提供了一些线索。我之所以希望参考成分而不是存储副本,是因为我希望用户能够轻松地更新成分(例如,用户意识到成分中存在拼写错误并希望更正)。更新每个成分的多个副本可能太复杂了。除非有一个神奇的MongoDB操作符,我还没有发现,它可以很容易地更新整个文档中出现的字符串:)我有一个类似的情况,出于许多原因喜欢MongoDB。我建议将配料作为一级集合保存,并将_id属性存储在配料数组中。如果以后需要查找含有特定成分的用户或食品,请使用$in查询运算符。