Я использую агрегацию MongoDB 3.6 с поиском, чтобы объединить две коллекции (пользователи и пользователи подписки).
var UserSchema = mongoose.Schema({
email:{
type: String,
trim: true,
unique: true,
},
name: {
type: String,
required: true,
trim: true,
},
password: String,
gender: { type: String, enum: ['male', 'female', 'unknown'], default: 'unknown'},
age_range: { type: String, enum: [12, 16, 18], default: 18},
country: {type:String, default:'co'}
});
var SuscriptionUsersSchema = mongoose.Schema({
user_id: {
ref: 'Users',
type: mongoose.Schema.ObjectId
},
channel_id: {
ref: 'Channels',
type: mongoose.Schema.ObjectId
},
subscribed: {type: Boolean, default:false},
unsubscribed_at: Date,
subscribed_at: Date
});
Моя цель - запросить пользователей-подписчиков и объединить коллекцию пользователей, сопоставляя начало и конецдата, чтобы получить некоторую аналитику подписок, таких как страна, возрастной диапазон и пол подписчиков, и показать данные в виде линейного графика.Я делаю так:
db.getCollection('suscriptionusers').aggregate([
{$match: {
'channel_id': ObjectId('......'),
'subscribed_at': {
$gte: new Date('2018-01-01'),
$lte: new Date('2019-01-01'),
},
'subscribed': true
}},
{
$lookup:{
from: "users",
localField: "user_id",
foreignField: "_id",
as: "users"
}
},
/* Implementing this form instead the earlier (above), make the process even slower :(
{$lookup:
{
from: "users",
let: { user_id: "$user_id" },
pipeline: [
{ $match:
{ $expr:
{$eq: [ "$_id", "$$user_id" ]}
}
},
{ $project: { age_range:1, country: 1, gender:1 } }
],
as: "users"
}
},*/
{$unwind: {
path: "$users",
preserveNullAndEmptyArrays: false
}},
{$project: {
'users.age_range': 1,
'users.country': 1,
'users.gender': 1,
'_id': 1,
'subscribed_at': { $dateToString: { format: "%Y-%m", date: "$subscribed_at" } },
'unsubscribed_at': { $dateToString: { format: "%Y-%m", date: "$unsubscribed_at" } }
}},
])
Основная проблема связана с производительностью.Например, для примерно 150 000 подписчиков запрос занимает около 7 ~ 8 секунд для извлечения информации, и я боюсь того, что может случиться с миллионами подписчиков, даже если я обусловлю ограничение для записей (например, извлечение только данныхмежду двумя месяцами), существует вероятность появления сотен подписчиков в этот период.
Я уже пытался создать индекс для коллекции подписчиков, для поля user_id, однако улучшения нет.
db.getCollection('suscriptionusers').ensureIndex({user_id: 1});
У меня вопрос: нужно ли сохранять поля (страна, возраст, пол и пол) также в коллекции подписчиков?потому что если я запрашиваю без поиска коллекции пользователей, процесс будет достаточно быстрым.
Или есть ли лучший способ улучшить производительность, используя мою текущую схему?
Спасибо большое:)
Редактировать: просто чтобы принять во внимание, что пользователь может быть подписан на несколько каналов, и поэтому подписка не сохраняется в коллекции пользователей