无需使用聚合即可简化深层嵌套的mongo查询

时间:2018-07-24 16:13:01

标签: mongodb mongoose mongoose-populate

我正在尝试为我的应用创建一种新闻源功能。我试图了解如何在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 个答案:

答案 0 :(得分:1)

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

  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)
    );
  });

答案 1 :(得分:0)

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) {
        .....
    })
})