代码之家  ›  专栏  ›  技术社区  ›  totalnoob

在不使用聚合的情况下简化深度嵌套的Mongo查询

  •  1
  • totalnoob  · 技术社区  · 6 年前

    我正在尝试为我的应用程序创建一种新闻订阅功能。我试图理解如何在mongo/mongoose中使用嵌套查询,而不是使用聚合。(应用程序的其余部分使用嵌套查询)如果我不需要的话,我宁愿不必用普通的javascript修改查询来获得完美的对象。

    我想得到当前用户的朋友,为每个朋友,得到他们所有的帖子,然后按日期排序。

    我想提高当前查询的效率,因为有一个额外的步骤

    我在蒙古斯的查询

        User.findOne({ _id: req.userId }, 'friends.user -_id')
        .populate({
            path: 'friends.user',
            model: 'User',
            select: 'posts -_id',
            populate: {
                path: 'posts',
                model: 'Post',
                select: 'date author user desc -_id',
                options: { sort: { date: -1 } },
                populate: {
                    path: 'author user',
                    model: 'User',
                    select: 'profile.firstname profile.lastname profile.avatar username'
                },
            },
        })
    

    结果

    {
    "newsfeed": [
            {
              "user": {
                "posts": [
                  {
                    "user": {
                      "profile": {
                        "firstname": "user2",
                        "lastname": "user2",
                        "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
                      },
                      "_id": "5b56549fcba9231e5d1e848d",
                      "username": "kenne"
                    },
                    "author": {
                      "profile": {
                        "firstname": "user1",
                        "lastname": "user1",
                        "avatar": "9e7b60e534cf761f41d6afe3d97295c9.jpg"
                      },
                      "_id": "5b562382a16cde19638e4bee",
                      "username": "user1"
                    },
                    "date": "2018-07-24T06:40:37.413Z",
                    "desc": "sdfsdf"
                  },
                  {
                    "user": {
                      "profile": {
                        "firstname": "user2",
                        "lastname": "user2",
                        "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
                      },
                      "_id": "5b56549fcba9231e5d1e848d",
                      "username": "user2"
                    },
                    "author": {
                      "profile": {
                        "firstname": "user1",
                        "lastname": "user1",
                        "avatar": "9e7b60e534cf761f41d6afe3d97295c9.jpg"
                      },
                      "_id": "5b562382a16cde19638e4bee",
                      "username": "user1"
                    },
                    "date": "2018-07-24T06:40:17.180Z"
                  },
                  {
                    "user": {
                      "profile": {
                        "firstname": "user2",
                        "lastname": "user2",
                        "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
                      },
                      "_id": "5b56549fcba9231e5d1e848d",
                      "username": "user2"
                    },
                    "author": {
                      "profile": {
                        "firstname": "user2",
                        "lastname": "user2",
                        "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
                      },
                      "_id": "5b56549fcba9231e5d1e848d",
                      "username": "user2"
                    },
                    "date": "2018-07-23T22:20:15.246Z",
                    "desc": "Gibberish"
                  }
                ]
              }
            },
            {
              "user": {
                "posts": [
                  {
                    "user": {
                      "profile": {
                        "firstname": "user3",
                        "lastname": "user3",
                        "avatar": "132df94df5733efd41609681bc7f71f9.jpeg"
                      },
                      "_id": "5b5382f3661a8d7023e1ae65",
                      "username": "user3"
                    },
                    "author": {
                      "profile": {
                        "firstname": "user3",
                        "lastname": "user3",
                        "avatar": "132df94df5733efd41609681bc7f71f9.jpeg"
                      },
                      "_id": "5b5382f3661a8d7023e1ae65",
                      "username": "user3"
                    },
                    "date": "2018-07-21T19:09:45.543Z",
                    "desc": "Gibberish"
                  }
                ]
              }
            }
          ]
        }
    

    因为我需要首先得到用户的朋友,所以当前的查询创建两个用户对象,每个用户对象都有自己的文章和作者以及其中的用户对象。

    eg. {"newsfeed": [{"user": {"bios": [...]}}], [{"user": {"bios": [...]}}] }
    

    我想要这样的东西

    {
    "newsfeed": [{
    
    "posts": [
      {
        "user": {
          "profile": {
            "firstname": "user2",
            "lastname": "user2",
            "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
          },
          "_id": "5b56549fcba9231e5d1e848d",
          "username": "user2"
        },
        "author": {
          "profile": {
            "firstname": "user1",
            "lastname": "user1",
            "avatar": "9e7b60e534cf761f41d6afe3d97295c9.jpg"
          },
          "_id": "5b562382a16cde19638e4bee",
          "username": "user1"
        },
        "date": "2018-07-24T06:40:37.413Z",
        "desc": "sdfsdf"
      },
      {
        "user": {
          "profile": {
            "firstname": "user2",
            "lastname": "user2",
            "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
          },
          "_id": "5b56549fcba9231e5d1e848d",
          "username": "user2"
        },
        "author": {
          "profile": {
            "firstname": "user1",
            "lastname": "user1",
            "avatar": "9e7b60e534cf761f41d6afe3d97295c9.jpg"
          },
          "_id": "5b562382a16cde19638e4bee",
          "username": "user1"
        },
        "date": "2018-07-24T06:40:17.180Z"
      },
      {
        "user": {
          "profile": {
            "firstname": "user2",
            "lastname": "user2",
            "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
          },
          "_id": "5b56549fcba9231e5d1e848d",
          "username": "user2"
        },
        "author": {
          "profile": {
            "firstname": "user2",
            "lastname": "user2",
            "avatar": "c66620a0ef057908a1663725956ac03a.jpg"
          },
          "_id": "5b56549fcba9231e5d1e848d",
          "username": "user2"
        },
        "date": "2018-07-23T22:20:15.246Z",
        "desc": "Gibberish"
      },
      {
        "user": {
          "profile": {
            "firstname": "user3",
            "lastname": "user3",
            "avatar": "132df94df5733efd41609681bc7f71f9.jpeg"
          },
          "_id": "5b5382f3661a8d7023e1ae65",
          "username": "user3"
        },
        "author": {
          "profile": {
            "firstname": "user3",
            "lastname": "user3",
            "avatar": "132df94df5733efd41609681bc7f71f9.jpeg"
          },
          "_id": "5b5382f3661a8d7023e1ae65",
          "username": "user3"
        },
        "date": "2018-07-21T19:09:45.543Z",
        "desc": "Gibberish"
      }
    ]}
    

    我怎么能不使用聚合来做到这一点呢?为了得到更像这样的东西

    eg. {"newsfeed": [{"posts": [...]}] }
    

    附加信息

    用户

    const UserSchema = new Schema({  
        username         : String,
        friends          : [{ user: { type: Schema.Types.ObjectId, ref: 'User'}, status: String }],
        profile: {
            name         : String,
            firstname    : String,
            lastname     : String,
            avatar       : String
        }),
        posts            : [{ type: Schema.Types.ObjectId, ref: 'Post' }]
    });
    

    帖子

    const PostsSchema  = new Schema({
        user       : { type: Schema.Types.ObjectId, ref: 'User' },
        author     : { type: Schema.Types.ObjectId, ref: 'User' },
        date       : Date,
        desc       : String
    });
    

    示例数据:用户

    {
        "_id" : ObjectId("5b51fd2a3f4ec33546a06648"),
        "profile" : {
            "firstname" : "user1",
            "lastname" : "user1"
            "avatar" : "user1.png"
        }
        "username" : "user1",
        "friends" : [ 
            {
                "_id" : ObjectId("5b51fd7c3f4ec33546a0664f"),
                "user" : ObjectId("5b51fd643f4ec33546a0664c")
            }, 
            {
                "_id" : ObjectId("5b51fdb53f4ec33546a06655"),
                "user" : ObjectId("5b51fd903f4ec33546a06652")
            }
        ]
     }
    
     {
        "_id" : ObjectId("5b51fd643f4ec33546a0664c"),
        "profile" : {
            "firstname" : "user2",
            "lastname" : "user2"
            "avatar" : "user2.png"
        }
        "posts" : [ 
            {
                "_id" : ObjectId("5b51fdcd3f4ec33546a06610"),
                "_id" : ObjectId("5b51fdcd3f4ec33546a06611"),
                "_id" : ObjectId("5b51fdcd3f4ec33546a06612"),
            }
        ],
        "__v" : 5,
        "username" : "user2"
     },
    
     {
        "_id" : ObjectId("5b51fd903f4ec33546a06652"),
        "profile" : {
            "firstname" : "user3",
            "lastname" : "user3"
            "avatar" : "user3.png"
        },
        "posts" : [ 
            {
                "_id" : ObjectId("5b51fdce3f4ec33546a06615"),
                "_id" : ObjectId("5b51fd2a3f4ec33546a06617")
            }
        ],
        "__v" : 5,
        "username" : "user3"
    }
    

    示例日期:帖子

    {
        "_id" : ObjectId("5b51fdcd3f4ec33546a06610"),
        "user" : ObjectId("5b51fd2a3f4ec33546a06648"),
        "author" : ObjectId("5b51fd2a3f4ec33546a06648"),
        "date" : ISODate("2018-07-20T15:18:02.962Z"),
        "desc" : "user1 gibberish",
    
        "__v" : 0
    }
    
    {
        "_id" : ObjectId("5b51fdcd3f4ec33546a06611"),
        "user" : ObjectId("5b51fd643f4ec33546a0664c"),
        "author" : ObjectId("5b51fd643f4ec33546a0664c"),
        "date" : ISODate("2018-07-20T15:19:00.968Z"),
        "desc" : "user2 gibberish",
        "__v" : 0
    }
    
    {
        "_id" : ObjectId("5b51fdcd3f4ec33546a06612"),
        "user" : ObjectId("5b51fd903f4ec33546a06652"),
        "author" : ObjectId("5b51fd903f4ec33546a06652"),
        "date" : ISODate("2018-07-20T15:19:44.102Z"),
        "desc" : "user3 gibberish",
        "__v" : 0
    }
    
    {
        "_id" : ObjectId("5b51fdce3f4ec33546a06615"),
        "user" : ObjectId("5b51fd643f4ec33546a0664c"),
        "author" : ObjectId("5b51fd643f4ec33546a0664c"),
        "date" : ISODate("2018-07-20T15:19:00.968Z"),
        "desc" : "more gibberish",
        "__v" : 0
    }
    
    {
        "_id" : ObjectId("5b51fdce3f4ec33546a06616"),
        "user" : ObjectId("5b51fd903f4ec33546a06652"),
        "author" : ObjectId("5b51fd903f4ec33546a06652"),
        "date" : ISODate("2018-07-20T15:19:44.102Z"),
        "desc" : "more gibberish",
        "__v" : 0
    }
    
    2 回复  |  直到 6 年前
        1
  •  0
  •   codechella    6 年前
    User.findOne({ _id: req.userId }, { "friends.user": 1 })
    .exec(function(err, { friends }) { // get the list of friends
        const friendIds = friends.map(({ user }) => user);
        Post.find({ user: { $in: friendIds } }) // find all posts of those friends
        .populate({
            path: 'user author',
            model: 'User',
            select: 'profile.firstname profile.lastname profile.avatar username -_id'
        })
        .sort('-date')
        .exec(function(err, response) {
            .....
        })
    })
    
        2
  •  1
  •   Manas Jayanth    6 年前

    您可以尝试使用以下代码展开应用程序层中的项

      User.find({ _id: mongoose.Types.ObjectId("5b51fd2a3f4ec33546a06648") }, 'friends.user -_id')
        .populate({
          path: 'friends.user',
          model: 'User',
          select: 'posts -_id',
        })
        .exec(function(err, results) {
    
          // Post.find({ _id: {$in: results
          const postIDs = results.reduce((acc, user) => {
            return acc.concat(
              ...user.friends.map(
                friend => friend.user.posts
              )
            )
          }, [])
    
          Post.find({ _id: { $in: postIDs } })
            .populate(
              {
                  path: 'author user',
                  model: 'User',
                  select: 'profile.firstname profile.lastname profile.avatar username'
              }
            )
            .exec(function(err, posts) {
            console.log(
              JSON.stringify(posts, null, 2)
            );
          })
        });
    

    另一种选择是map reduce,但由于您不喜欢聚合,所以我也不确定map reduce是否合适。

      const mapReduceConfig = {
        map: function() {
          var friends = this.friends;
          emit(
            this._id,
            friends.reduce(
              (acc, friend) => {
                return acc.concat(
                  friend.posts &&
                    friend.posts.reduce((pacc, p) => pacc.concat(p), [])
                ) || [];
              },
              []
            )
          );
        },
    
        reduce: function(k, vals) {
          return Array.sum(vals);
        }
      };
    
      User.mapReduce(mapReduceConfig, function(err, results) {
        console.log(
          JSON.stringify(results, null, 2)
        );
      });