【问题标题】:How can I query an Array for a subdocument with mongoose?如何使用 mongoose 查询数组以获取子文档?
【发布时间】:2017-11-27 05:58:26
【问题描述】:

我正在寻找一个包含数组的子文档。

您可以将其视为类似于 medium.com 或 tumblr.com 的应用程序:

我有一个用户架构,并且用户有很多帖子。我的帖子模式有一个“标签”键,它是一个数组,我正在尝试创建一个路由,该路由将显示所有具有特定标签的帖子。

例如,

GET /user/posts/:tag
GET /user/posts/dogs

可能会返回所有带有“dogs”标签的帖子(由任何用户发布)的数据

{
  description: 'dogs are cool',
  title: 'huskies',
  tags: ['dogs','animals','outdoors'],
  original_poster: '34255352426'
},{
  description: 'My puppies are nice',
  title: 'Puppies',
  tags: ['pugs','dogs','small'],
  original_poster: '44388342426'
},{
  description: 'I like cats more than dogs',
  title: 'Cats',
  tags: ['kittens','cats','dogs'],
  original_poster: '15708213454'
} 

这是我的用户架构:

const mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    PostSchema = require('./post-model.js');

let UserSchema = new Schema({
    email: {
        type: String,
        required: true,
        unique: true
    },
    username: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: false, //only required for local users
        unique: false,
        default: null
    },
    following: {
        type: [Number], 
        required: false,
        unique: false
    },
    followers: {
        type: [Number],
        required: false,
        unique: false
    },
    posts: [PostSchema],
})

module.exports = mongoose.model('User',UserSchema);

这是我的帖子架构:

const mongoose = require('mongoose'),
    Schema = mongoose.Schema;

let PostSchema = new Schema({
    description: {
        type: String,
        required: false,
        unique: false
    },
    title: {
        type: String,
        required: false,
        unique: false
    },
    tags: {
        type: [String],
        required: true
    },
    original_poster: { 
      type: String,
      required: true
    }
});

module.exports = PostSchema;

编辑:

为了澄清我的问题,假设这是一个常规的 js 对象,这是一个返回与特定标签相关的所有数据的函数:

let users = [];
let user1 = {
  username: 'bob',
  posts: [
    {
      title: 'dogs r cool',
      tags: ['cats','dogs']
    },
    {
      title: 'twd is cool',
      tags: ['amc','twd']
    }]
};
let user2 = {
  username: 'joe',
  posts: [{
    title: 'mongodb is a database system that can be used with node.js',
    tags: ['programming','code']
  },
  {
    title: 'twd is a nice show',
    tags: ['zombies','horror-tv','amc']
  },
    {
      title: 'huskies are my favorite dogs',
      tags: ['huskies','dogs']
    }]
}

users.push(user1);
users.push(user2);

function returnPostByTag(tag) {
  let returnedPosts = [];

  users.forEach((user,i) => {
    user.posts.forEach((post) => {
      if(post.tags.includes(tag)) {
        let includespost = {}
        includespost.title = post.title;
        includespost.tags = post.tags;
        includespost.original_poster = user.username;
        returnedPosts.push(includespost);
      }
    })
  })
  return returnedPosts;
}

如果你想看到一个完整的 repl,我在这里使用一个普通的 js 示例,它是:https://repl.it/repls/LavenderHugeFireant

【问题讨论】:

    标签: node.js mongodb mongoose


    【解决方案1】:

    您可以使用 Mongoose 执行以下操作,以返回帖子数组中任何子文档中的 tags 数组包含 tag 的所有用户:

    User.find({ 'posts.tags': tag }, function(err, users) {
      if (err) {
        // Handle error
      }
    
      let filteredPosts = users
        .map(user => {
          user.posts.forEach(post => post.original_poster = user.username)
          return user
        })
        .map(user => user.posts)
        .reduce((acc, cur) => acc.concat(cur), [])
        .filter(post => post.tags.includes(tag))
    
      // Do something with filteredPosts
      // ...such as sending in your route response
    })
    

    ...其中 User 是您的 Mongoose 模型,tag 包含您要查询的标签字符串(可能来自您的路由请求)。

    如果你使用 Promise,那么你可以这样做:

    User.find({ 'posts.tags': tag })
      .then(users => {
    
        let filteredPosts = users
          .map(user => {
            user.posts.forEach(post => post.original_poster = user.username)
            return user
          })
          .map(user => user.posts)
          .reduce((acc, cur) => acc.concat(cur), [])
          .filter(post => post.tags.includes(tag))
    
        // Do something with filteredPosts
        // ...such as sending in your route response
    
      })
      .catch(err => {
        // Handle error
      })
    

    【讨论】:

    • 这将返回所有发布过该标签的用户。例如,如果我在上面的示例中运行 'User.find({'post.tags':'dogs'})',它将返回两个用户对象,包括他们没有标签的帖子小狗'。有没有办法让我只取回带有指定标签的帖子?
    • 您可以尝试使用聚合框架来获取过滤后的数组:docs.mongodb.com/manual/aggregation。但是,在这种情况下,您可以将找到的文档处理成您想要的格式。我已经用一个可能的解决方案修改了我的答案。
    猜你喜欢
    • 2017-02-28
    • 2021-04-28
    • 2015-04-27
    • 2020-10-26
    • 2016-03-24
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-16
    相关资源
    最近更新 更多