#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 способа создания новостной ленты:
- Разветвление при методе записи (push)
- Разветвление при методе чтения (извлечения)
Разветвление при записи
Во-первых, вам понадобится другая коллекция:
const Newsfeed = new mongoose.model('newsfeed', {
owner: {type: mongoose.Types.ObjectId, required: true},
post: {type: mongoose.Types.ObjectId, required: true}
});
Когда пользователь публикует что-то:
- Получить n подписчиков
- Отправьте (разветвите) этот пост n подписчикам
Когда пользователь получает канал:
- Получить из
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();
});
Разветвление при чтении
Когда пользователь публикует что-то:
- Сохранить
Когда пользователь получает ленту
- Получаем n следующих
- Получать сообщения из 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 #