在node + Mongodb + Redis中构建一个简单的新闻源

时间:2016-10-09 05:22:15

标签: node.js mongodb express redis node-redis

我的目标是在mongodb和redis的帮助下在node.js中构建一个简单的新闻源。它类似于twitter

因此,一旦用户A关注用户B,情况就非常简单。稍后用户的新闻提要(主页)将显示用户B的活动,就像他发布的那样。

用户架构

const UserSchema = new Schema({
  email: { type: String, unique: true, lowercase: true},
});



const followSchema = new Schema(
    {
        user: { type: Schema.Types.ObjectId, required: true, ref: 'User' },
        target: { type: Schema.Types.ObjectId, required: true, ref: 'User' },
    });

目前我的用户架构的设计非常简单,当我关注其他用户时,我将创建关注架构对象

还有另一个架构,即帖子架构

/* This is similar like the Tweet */
var PostSchema = new Schema({
  // Own by the user
  creator: { type: Schema.Types.ObjectId, ref: 'User' }
  body: String,
});

此架构供用户发布任何内容,类似于Twitter发布。

假设我已经关注了一堆用户

{
   user: 'me',
   target: 'draco'
},

{
  user: 'me',
  target: 'donald'
},

{
  user: 'me',
  target: 'joker'
}

然后让我的一个粉丝发帖。如何将其呈现给我当前的新闻源?

/* I'm following Joker */
app.post('/follow', (req, res, next) => {
   let follow = new Follow();
   follow.user = "me";
   follow.target = "joker";
   // Do i need to use redis to subscribe to him?
   follow.save();
})


/* Joker posted something */
app.post('/tweet',(req, res, next) => {
   let post = new Post();
   post.creator = "joker";
   post.body = "Hello my name is joker"
   post.save();
   // Do i need to publish it using redis so that I will get his activity?

});

这是我的尝试

app.get('/feed', function(req, res, next) {

     // Where is the redis part?
     User.findOne({ _id: req.user._id }, function(err, foundUser) {
        // this is pretty much my attempt :(
     })
})

我什么时候应该使用redis实际上做pub和sub?这样我就可以把我的一个粉丝的内容带到我的时间线上了?

2 个答案:

答案 0 :(得分:5)

我已经建立了一个有新闻源的社交网络。我就是这样做的。

基本上,您有2种方法可以构建新闻源:

  1. Fanout on write(push)方法
  2. 读取(拉动)方法的扇出
  3. 编写扇出

    首先,您需要另一个集合:

    const Newsfeed = new mongoose.model('newsfeed', {
      owner: {type: mongoose.Types.ObjectId, required: true},
      post: {type: mongoose.Types.ObjectId, required: true}
    });
    

    当用户发布内容时:

    1. 获得追随者
    2. 将此帖子推送(扇出)给n个粉丝
    3. 当用户获得Feed:

      1. Newsfeed收藏
      2. 获取

        实施例

        router.post('/tweet', async (req, res, next) => {
          let post = await Post.create({});
        
          let follows = await Follow.find({target: req.user.id}).exec();
        
          let newFeeds = follows.map(follow => {
            return {
              user: follow.user,
              post: post.id
            }
          });
          await Newsfeed.insertMany(newFeeds);
        });
        
        router.get('/feed', async (req, res, next) => {
          let feeds = await Newsfeed.find({user: req.user.id}).exec();
        });
        

        读取扇出

        当用户发布内容时:

        1. 保存
        2. 当用户获得Feed

          1. 获取以下
          2. 从以下
          3. 获取帖子

            实施例

            router.post('/tweet', async (req, res, next) {
              await Post.save({});
            });
            
            router.get('/feeds', async (req, res, next) {
              let follows = await Follow.find({user: req.user.id}.exec();
            
              let followings = follows.map(follow => follow.target);
            
              let feeds = await Post.find({user: followings}).exec();
            });
            

            您不需要Redis或pub / sub来实现新闻源。但是,为了提高性能,您可能需要Redis为此实现某种缓存。

            有关更多信息或先进技术,您可能需要查看this

答案 1 :(得分:3)

用户架构:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

const userSchema = new Schema({
     name:{type:String},
     email: { type: String, unique: true, lowercase: true},
  },{
    collection: 'User'
  });

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

关注架构:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var followSchema = new Schema(
    {
        follow_id: { type: Schema.Types.ObjectId, required: true, ref: 'User'  },
        leader_id: { type: Schema.Types.ObjectId, required: true, ref: 'User' }
    },{
        collection:'Follow'
    });

 var Follow = module.exports = mongoose.model('Follow', followSchema);

发布架构:

 var mongoose = require('mongoose');
 var Schema = mongoose.Schema;

var postSchema = new Schema({
  creator: { type: Schema.Types.ObjectId, ref: 'User' }
  body: {type: String , required:true},
  created_at :{type:Date , default:Date.now}
},{
    collection:'Post'
  });

var Post = module.exports = mongoose.model('Post', postSchema);

现在假设您在User集合中有3个用户:

{ _id: ObjectID('5a2ac68d1413751391111111') ,name:'John' , email:'john@gmail.com'}

{ _id: ObjectID('5a2ac68d1413751392222222') ,name:'Morgan' , email:'morgan@yahoo.com'}

{ _id: ObjectID('5a2ac68d1413751393333333') ,name:'Emily' , email:'emily@outlook.com'}
  

现在约翰跟随摩根和艾米丽:

所以在Follow集合中有两条记录

1)follow_id = John's IDleader_id = Morgan's ID

2)follow_id = John's IDleader_id = Emily's ID

{ 
  _id: ObjectID('5a2ac68d141375139999999'),
  follow_id : ObjectID('5a2ac68d1413751391111111'),
  leader_id : ObjectID('5a2ac68d1413751392222222')
},
  {
     _id: ObjectID('5a2ac68d1413751393333333'),
    follow_id : ObjectID('5a2ac68d1413751391111111'),
    leader_id : ObjectID('5a2ac68d1413751393333333')
  }
  

现在,如果您想获得用户的关注:

app.get('/following/:user_id',function(req,res){
      var userid=req.params.user_id;
      Follow.find({follow_id:mongoose.mongo.ObjectID(userid)})
      .populate('leader_id')
      .exec(function(err,followings){
       if(!err && followings){ 
          return res.json({followings:followings});
       }
      });
});
  

获取用户的关注者:

   app.get('/followers/:user_id',function(req,res){
      var userid=req.params.user_id;
      Follow.find({leader_id:mongoose.mongo.ObjectID(userid)})
      .populate('follow_id')
      .exec(function(err,followers){
       if(!err && followers){ 
          return res.json({followers:followers});
       }
      });
});
  

npm install redis

你的app.js中的

var redis = require('redis');
var client = redis.createClient();

当一个用户创建帖子时:

app.post('/create_post',function(req,res){

       var creator=new mongoose.mongo.ObjectID(req.body.creator);
       var postbody=req.body.body;


       async.waterfall([
          function(callback){

          // find followers of post creator

          Follow.find({leader_id:creator})
              .select({ "follow_id": 1,"leader_id":0,"_id": 0})
              .exec(function(err,followers){
                      if(!err && followers){ 
                      callback(null,followers);
                      }
              });
         },
        function(followers, callback){

          // saving the post

         var post=new Post({
            creator: creator,
            body: postbody
         });
          post.save(function(err,post){
             if(!err && post){

   // adding newly created post id to redis by key userid , value is postid

                for(var i=0;i<followers.length;i++){
                   client.sadd([followers[i].follow_id,post.id]);
                }


                 callback(null,post);
             }
         });   
      }
   ], function (err, result) {
             if(!err && result){
                return res.json({status:"success",message:"POST created"});
             }
       });
    });

现在获取用户新闻Feed:

1)首先从userid的redis键获取postid数组

2)循环通过postid并从mongo获取帖子

userid获取新闻的功能:

app.get('/newsfeed/:user_id',function(req,res){
    var userid=req.params.user_id;
    client.smembers(userid,function(err, reply) {
        if(!err && reply){
            console.log(reply);

            if(reply.length>0){
               var posts=[];

               for(var i=0;i<reply.length;i++){
                  Post.findOne({_id:new mongoose.mongo.ObjectID(reply[i])}).populate('creator').exec(function(err,post){        
                 posts.push(post);
      });
               }

              return res.json({newsfeed:posts});

            }else{
              // No News Available in NewsFeed
            }
        }
    });

});

这里我们使用redis来存储新闻源的[userid,postid数组], 但是如果你不想使用redis,只需使用新的Feedfeed模型,并将user_id和post_id存储到新创建的帖子中,然后显示它。

NewsFeed架构:

 var mongoose = require('mongoose');
 var Schema = mongoose.Schema;

var newsFeedSchema = new Schema({
  user_id: {type: Schema.Types.ObjectId, refer:'User' , required:true}
  post_id: {type: Schema.Types.ObjectId, refer:'Post' , required:true},
},{
    collection:'NewsFeed'
  });

var NewsFeed = module.exports = mongoose.model('NewsFeed', newsFeedSchema);

Redis的有用链接:https://www.sitepoint.com/using-redis-node-js/

for Async:https://caolan.github.io/async/docs.html#