MongoDB、Mongoose:如何在找到的文档中查找子文档?

25

我卡在了尝试在找到的文档中通过_id获取子文档的步骤。

示例架构

var User = mongoose.Schema({
        name:       String,
        photos:    [{src: String, title: String}]
    });
var Team = db.model('Team', Team);

现在我正在获取一个用户:

myUser = User.findOne(...)...

我该如何通过其 _id(或title)来获取他的照片的src

类似于:

myUser.photos.findOne({'_id': myId})
2个回答

61

你需要为嵌套文档创建一个新的模式,或者将类型声明留空数组,这样 mongoose 就会将其解释为 Mixed 类型。

var userSchema = new mongoose.Schema({
  name: String,
  photos: []
});
var User = mongoose.model('User', userSchema);

-- 或者 --

var userSchema = new mongoose.Schema({
  name: String,
  photos: [photoSchema]
});

var photoSchema = new mongoose.Schema({
  src: String,
  title: String
});

var User = mongoose.model('User', userSchema);

然后你可以这样保存:

var user = new User({
  name: 'Bob',
  photos: [ { src: '/path/to/photo.png' }, { src: '/path/to/other/photo.png' } ]
});

user.save();
从这里开始,您只需使用数组基元即可找到嵌入式文档:
User.findOne({name: 'Bob'}, function (err, user) {

  var photo = user.photos.filter(function (photo) {
    return photo.title === 'My awesome photo';
  }).pop();

  console.log(photo); //logs { src: '/path/to/photo.png', title: 'My awesome photo' }
});

-- 或者 --

您可以在嵌入式文档中使用特殊的id()方法按照ID进行查找:

User.findOne({name: 'Bob'}, function (err, user) {
    user.photos.id(photo._id);
});

您可以在这里阅读更多:http://mongoosejs.com/docs/subdocs.html

请确保不要在Mongoose中注册模式,否则它将创建一个新的集合。同时请记住,如果经常搜索子文档,最好使用refs和population,如下所示。即使因为索引需要访问数据库两次,但速度仍然更快。另外,mongoose不支持嵌套多层子文档(即子文档还有子文档的情况)。

var user = mongoose.Schema({
  name: String,
  photos: [{ type: Schema.Types.ObjectId, ref: 'Photo' }]
});

var photo = mongoose.Schema({
  src: String,
  title: String
});

User
  .findOne({ name: 'foo' })
  .populate('photos')
  .exec(function (err, user) {
    console.log(user.photos[0].src);
  });

相关文档可以在这里找到 http://mongoosejs.com/docs/populate.html


这是一个好的、正确的解决方案,但是……假设我已经找到了用户,并且不想再次查询数据库。 - gial
就是这样!筛选解决方案对我来说是最好的选择。以前从未使用过它。谢谢! - gial
1
如果您的嵌入式文档没有模式,或者它们是 Mixed 类型,我认为您不能像上面描述的那样使用内置的 .id() 函数,因为 Mongoose 不知道数据是否实际具有 _id。在这种情况下,您应该使用 .filter() 技术。 - Tom Spencer
1
我知道这已经过时了,但我想指出这个答案中关于使用populate的最后一个注释并不是最佳实践(当然取决于您的用例)。jibsales说经常应该引用“searched”,但为了清晰起见,应该引用经常“编辑”的文档。虽然很小,但这是一个重要的区别。 - meteorainer
1
如果您不注册模式,Mongoose 如何将 Photo 模式与 user.Photos 连接起来? - user1790300

3

在srquinn的回答基础上,根据我的有限经验,我认为populate是用于将来自不同集合的文档连接在一起的?

我认为在这里你可以只做User.findOne({ name: 'foo' }, 'photos'),这是一种简写方式:

const query = User.findOne({ name: 'foo' })
query.select('photos')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接