Создание простой ленты новостей в node Mongodb Redis

#node.js #mongodb #express #redis #node-redis

#node.js #mongodb #экспресс #redis #узел-redis

Вопрос:

Моя цель — создать простую ленту новостей в node.js с помощью mongodb и redis. Это похоже на twitter

Таким образом, сценарий довольно прост, как только пользователь A последует за пользователем B. Позже в ленте новостей пользователя A (домашняя страница) будет показана активность пользователя 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' },
    });
  

В настоящее время дизайн схемы моего пользователя довольно прост, когда я подписываюсь на другого пользователя, я просто создаю объект схемы следования

и есть еще одна схема, которая является post schema

 /* 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? чтобы я мог взять контент одного из моих подписчиков и показать его на своей временной шкале?

Комментарии:

1. Вы ничего не делаете со своими подписчиками, а не с текущим пользователем в запросе

2. Что мне делать с моим массивом подписчиков, должен ли я зацикливать каждый из них?

Ответ №1:

Я создал социальную сеть, в которой тоже есть лента новостей. Вот как я это сделал.


В принципе, у вас есть 2 способа создания новостной ленты:

  1. Разветвление при методе записи (push)
  2. Разветвление при методе чтения (извлечения)

Разветвление при записи

Во-первых, вам понадобится другая коллекция:

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

Когда пользователь публикует что-то:

  1. Получить n подписчиков
  2. Отправьте (разветвите) этот пост n подписчикам

Когда пользователь получает канал:

  1. Получить из Newsfeed коллекции

Пример:

 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. Сохранить

Когда пользователь получает ленту

  1. Получаем n следующих
  2. Получать сообщения из n следующих

Пример

 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 реализовал для этого какой-то кэш.

Для получения дополнительной информации или продвинутой техники вы можете взглянуть на это.

Комментарии:

1. Привет, спасибо за помощь! Мне просто интересно, где часть redis спасибо!!

2. Ну, как я уже говорил ранее, вам действительно не нужен механизм Redis или pub / sub для реализации самой простой новостной ленты. Однако вы можете использовать Redis для реализации кэшей для повышения производительности. Вы хотите, чтобы я пролил некоторый свет на это?

3. @willie17 Было бы здорово, если бы вы уточнили часть Redis на приведенной выше диаграмме.

4. @willie17 Каковы плюсы и минусы каждого метода?

5. Ссылка ведет на 404.

Ответ №2:

Схема пользователя :

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

Теперь предположим, что у вас есть 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 ID и leader_id = Morgan's ID

2) follow_id = John's ID и leader_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 amp;amp; 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 amp;amp; followers){ 
          return res.json({followers:followers});
       }
      });
});
  

npm устанавливает 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 amp;amp; 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 amp;amp; 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 amp;amp; result){
                return res.json({status:"success",message:"POST created"});
             }
       });
    });
  

Теперь для получения пользовательской ленты новостей :

1) сначала получите массив postid из ключа redis идентификатора пользователя

2) перебираем postid и получаем post от mongo

Функция для получения ленты новостей по идентификатору пользователя :

 app.get('/newsfeed/:user_id',function(req,res){
    var userid=req.params.user_id;
    client.smembers(userid,function(err, reply) {
        if(!err amp;amp; 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, массив postids] для новостной ленты,
но если вы не хотите использовать redis, просто используйте приведенную ниже модель новостной ленты и сохраните user_id и post_id для вновь созданного сообщения, а затем отобразите его.

Схема ленты новостей :

  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 /

для асинхронности: https://caolan.github.io/async/docs.html #