猫鼬引用子文档的不同方式?

Posted

技术标签:

【中文标题】猫鼬引用子文档的不同方式?【英文标题】:mongoose different ways to reference subdocuments? 【发布时间】:2019-08-01 10:11:28 【问题描述】:

此语法直接来自关于子类型的 mongoose 文档。但是,我也看到了这个对子文档的替代参考。有什么区别?

https://mongoosejs.com/docs/subdocs.html

var childSchema = new Schema( name: 'string' );

var parentSchema = new Schema(
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
);

对子文档的另一种引用方式

var childSchema = new Schema( name: 'string' );

mongoose.model('children', childSchema);

var parentSchema = new Schema(
  children: 
    type: Schema.Types.ObjectId,
    ref: 'children'
  ,
);

【问题讨论】:

是的,所以在第二种情况下,您只是获取文档的 _id 而不是整个文档本身。所以它在文档存储方面要便宜得多,另外,您还可以使用.populate 方法执行类似联接的操作,这比 mongodb 聚合 $lookup 更快。 【参考方案1】:

上述两种语法完全不同,一种是实际的子文档(children)存储在父文档中,另一种是新文档存储在 children 集合中,并且只存储它的引用父文档。

案例 1:

var childSchema = new Schema( name: 'string' );

var parentSchema = new Schema(
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
);

在这个给定的语法中,父文档也会将子文档存储在父文档中。

父sollection 中的示例文档将如下所示:

 
  _id : "parent_random_generated_id"
  children :[ _id : "childid1" , name : "Captain America",
              _id : "childid2" , name : "Iron Man",...],
  child : _id : "childid3" , name : "Thor Odinson",
  ...

案例 2:

var childSchema = new Schema( name: 'string' );

mongoose.model('children', childSchema);

var parentSchema = new Schema(
  children: 
    type: Schema.Types.ObjectId,
    ref: 'children'
  ,
);

在这种语法中,子文档将单独存储,它们的引用id(_id)将存储在父文档中。

本例中的示例文档如下所示:

// Children documents
 _id : "childid1" , name : "Captain America"
 _id : "childid2" , name : "Iron Man"
 _id : "childid3" , name : "Thor Odinson"

//parent document
 
  _id : "parent_random_generated_id"
  children :["childid1","childid2",...],
  child : "childid3",
  ...

在第二种情况下,您可以在需要时使用 Mongodb $lookup 运算符填充子文档,使用 mongodb aggregation pipeline,或使用 .populate('children').populate('child') 填充特定的子文档。

我希望这能澄清你的疑问。

【讨论】:

感谢您的信息。我假设它们都很快,但第一个示例是不需要次要相关信息的更快搜索查找。可能会出现更多冗余。 是否有任何自动方法可以通过单个调用添加子文档和引用 @ravi-shankar-bharti,或者我应该先“插入”到子文档中,然后再添加对父文档的引用? 【参考方案2】:

我已经完成了 Ravi Shankar Bharti 案例 2 带有一些数据写入示例。让我们使用书籍作者的场景:

const authorSchema = new Schema( name: 'string' );
const authorModel = mongoose.model('authors', authorSchema);
    
const bookSchema = new Schema(
  title: String,
  author: 
    type: Schema.Types.ObjectId,
    ref: 'author'
  ,
);

const bookModel = mongoose.model('books', bookSchema)

const authorData =  name: "John Doe" 

// Check if author does exists. Insert if not or find if yes
const options = 
  upsert: true,
  new: true,
  setDefaultsOnInsert: true
;
const anAuthor = await authorModel.findOneAndUpdate(authorData, authorData, options)

// Insert a new book with reference to `anAuthor`
const aBook = new bookModel( title: "MyBook" )
aBook.set( author: anAuthor )
await aBook.save()

在这种语法中,子文档将单独存储,它们的引用id(_id)将存储在父文档中。

本例中的示例文档如下所示:

// authors documents
 _id : "authorId1" , name : "John Doe"
 _id : "authorId2" , name : "Iron Man"
 _id : "authorId3" , name : "Thor Odinson"

//books document
 
  _id : "parent_random_generated_id"
  title: "MyBook",
  author : "authorId1",
  ...

而对于阅读,您可以使用populate

let result = await bookModel.find()
result = await authorModel.populate(result,  path: 'author' )

【讨论】:

【参考方案3】:

区别很简单。 您只是为子定义架构不会为数据库中的子创建单独的集合,而是将整个子文档嵌入到父文档中。

后面的一个中,您通过调用 mongoose.model 为子架构定义一个模型,这会在数据库中创建一个单独的子架构集合,然后您就可以在父文档中引用子文档,而无需将整个子文档嵌入到父文档中,只需添加子 _id。

【讨论】:

这是有道理的。谢谢!

以上是关于猫鼬引用子文档的不同方式?的主要内容,如果未能解决你的问题,请参考以下文章

来自不同集合的子文档 ID 数组的猫鼬模式

猫鼬中嵌套文档的不同类型是啥?

如何在子文档中填充模型实例数组? MongoDB猫鼬

构建猫鼬模式时如何引用不同集合中的特定字段?

构建猫鼬模式时如何引用不同集合中的特定字段?

构建我的猫鼬模式的最佳方式:嵌入式数组、填充、子文档?