Предложения схемы уведомлений Mongoose/MongoDB
Мне было интересно, какая наилучшая схема для типа пользователя/уведомлений выглядит следующим образом:
- У вас несколько пользователей.
- У вас есть несколько уведомлений, которые могут быть для одного пользователя, для некоторых пользователей или для всех пользователей.
- Вам нужна запись "читать" в хранилище, чтобы узнать, прочитал ли пользователь уведомление или нет.
Вариант 1
Схема встроенных уведомлений
Notifications = new Schema ( {
message : String,
date : { type : Date, default: Date.now() }
read : { type: Boolean, default : false }
});
User = new Schema( {
username : String,
name : String,
notifications : [Notifications]
});
Плюсы:
- Очень легко отобразить данные, так как вызов User.find() будет отображать уведомления в виде объекта массива.
Минусы:
- Когда вы создаете уведомление для каждого пользователя, вам нужно сделать .push для всех встроенных уведомлений
- Несколько записей уведомлений для каждого пользователя (несколько данных в базе данных)
- Гигантский встроенный документ (я прочитал что-то о ограничении на 4 МБ)
- Так как это встроенный документ - (mongoose DocumentArray), вы не можете выполнить поиск или пропустить. Вы загружаете каждое уведомление каждый раз, когда получаете доступ к пользователю.
Вариант второй
Объекты Populate (DBRef like)
Notification = new Schema ({
message : String,
date : { type : Date, default : Date.now() }
});
UserNotification = new Schema ({
user : { type : Schema.ObjectId, ref : 'User' },
notification : { type : Schema.ObjectId, ref : 'Notification' },
read : { type : Boolean, default : false }
});
User = new Schema( {
username : String,
name : String,
notifications : [ { type : Schema.ObjectID, ref : 'UserNotification' } ]
});
Плюсы:
- Оптимально для запросов
- Нет повторяющихся данных
- Поддержка большого количества уведомлений
Минусы:
- У вас есть 3 коллекции, а не одна ( Вариант 1 имеет только одну коллекцию)
- У вас есть 3 запроса при каждом доступе к коллекции.
Вопросы
- Как вы думаете, самая лучшая схема из этих двух?
- Я пропустил что-то или какое-то базовое знание NoSQL?
- Может ли кто-нибудь предложить лучшую схему?
Спасибо, заранее, и я прошу прощения за длинный пост, но я думаю, что я не могу объяснить это проще.
Ответы
Ответ 1
Вариант 1 выглядит так, что это, вероятно, приведет к чрезмерному росту и перемещению документа, что будет плохо для производительности, поскольку большая часть ваших записей будет поступать во встроенный документ (уведомления).
Вариант 2 Я не совсем понимаю вашу стратегию - кажется, излишне иметь эти 3 коллекции, но также встраивать список уведомлений с помощью objectId, если вы уже ссылаетесь на пользователя по ID в таблице уведомлений. Вы можете индексировать пользователя в таблице "Уведомления", а затем исключить вложенный массив в таблице "Пользователи".
(РЕДАКТИРОВАТЬ)
Вот еще одна стратегия для рассмотрения.
Три коллекции, которые выглядят следующим образом:
Users:
_id: objectid
username : string
name: string
Notifications:
_id: objectid
to (indexed): objectid referencing _id in "users" collection
read: boolean
Global Notifications:
_id: objectid
read_by: [objectid referencing _id in users]
Для уведомлений, предназначенных для одного пользователя, вставьте в Уведомления для этого пользователя. Для нескольких пользователей вставьте один для каждого пользователя (поочередно, вы можете сделать "to" полевым массивом и сохранить _ids всех получателей, а затем сохранить другой список всех получателей, которые его прочитали). Чтобы отправить уведомление всем пользователям, вставьте в коллекцию глобальных уведомлений. Когда пользователь прочитает его, добавьте их user_id в поле read_by.
Итак, чтобы получить список всех непрочитанных уведомлений пользователя, вы выполняете два запроса: один для уведомлений и один для глобальных уведомлений.
Ответ 2
`// namespace like a channel
// we have a notification from specific channel or namespace
const NamespaceSchema = new Schema({
name: {
type: String,
unique: true,
required: true
},
author: {
type: String,
required: true
},
createdAt: {
type: Boolean,
default: Date.now()
},
notifications: [{
type: Schema.Types.ObjectId,
ref: 'Notification',
}]
});`
`// the notification schema have a subscribers to specific notification (objectId)
//
const NotificationSchema = new Schema({
title: {
type: String
},
message: {
type: String
},
read: {
type: Boolean,
default: false
},
subscribers: [{
type: Schema.Types.ObjectId,
ref: 'Subscriber'
}],
createdAt: {
type: Date,
default: Date.now()
}
});`
`// subscribers subscribe to a namespace or channel
const SubscriberSchema = new Schema({
subscriber: {
type: String,
required: true
},
namespaces: [{
type: Schema.Types.ObjectId,
ref: 'Namespace',
}]
});`