MongoDB Aggregation - производительность поиска в $ - PullRequest
0 голосов
/ 19 октября 2018

Я использую агрегацию 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});

У меня вопрос: нужно ли сохранять поля (страна, возраст, пол и пол) также в коллекции подписчиков?потому что если я запрашиваю без поиска коллекции пользователей, процесс будет достаточно быстрым.

Или есть ли лучший способ улучшить производительность, используя мою текущую схему?

Спасибо большое:)

Редактировать: просто чтобы принять во внимание, что пользователь может быть подписан на несколько каналов, и поэтому подписка не сохраняется в коллекции пользователей

1 Ответ

0 голосов
/ 29 октября 2018

Ну, может быть, это не самый лучший метод, но я просто включил необходимые поля из UserSchema в SuscriptionUsersSchema.Это заметно быстрее для целей аналитики.Кроме того, я выяснил, что аналитическая запись должна быть неизменной во времени, чтобы сохранить данные такими, какими они были созданы в данный момент.Таким образом, используя данные таким образом, даже если пользователь изменяет свою информацию или удаляет учетную запись, данные останутся без изменений.Если у вас есть какие-либо советы, пожалуйста, не стесняйтесь поделиться ими:)

Просто для справки, моя SuscriptionUsersSchema теперь выглядит так:

    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},
  gender: { type: String, enum: ['male', 'female', 'unknown'], default: 'unknown'},
  age_range: { type: String, enum: [12, 16, 18], default: 18},
  country: {type:String, default:'co'}
  unsubscribed_at: Date,
  subscribed_at: Date
});
...