2016-09-13 2 views
1

Я создаю приложение для ставок на футбол в колледже для своей семьи. Вот мои схемы:MongoosJS: Лучший подход к производному/вычисленному значению

const GameSchema = new mongoose.Schema({ 
    home: { 
     type: String, 
     required: true 
    }, 
    opponent: { 
     type: String, 
     required: true 
    }, 
    homeScore: Number, 
    opponentScore: Number, 
    week:{ 
     type: Number, 
     required: true 
    }, 
    winner: String, 
    userPicks: [ 
     { 
      user: { 
       type: mongoose.Schema.Types.ObjectId, 
       ref: 'User' 
      }, 
      choosenTeam: String 
     } 
    ] 
}); 

const UserSchema = new mongoose.Schema({ 
    name: String 
}); 

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

Я все еще очень новичок в MongoDB и Mongoose, поэтому я не уверен, как справиться с этой проблемой. Поскольку документ игры никогда не будет расти более чем за 200 записей, я думаю, что оба показателя должны быть получены или рассчитаны из данных, хранящихся в базе данных.

Вот возможные решения, которые я думал до сих пор:

  • Сделать обе оценки виртуальных атрибуты, не знают, как это будет работать для нескольких пользователей
  • упорствуют атрибуты документа, но используйте промежуточное программное обеспечение для пересчета баллов, когда результаты для игр недели сохраняются в базе данных.
  • Используйте статический метод для вычисления баллов.

Любой совет будет оценен по достоинству.

+0

привет, эта часть машинного обучения? как вы хотите рассчитать домашний счет? это прирост от предыдущего балла? – vdj4y

ответ

1

Вы можете использовать структуру агрегации для расчета агрегатов. Это более быстрая альтернатива Map/Reduce для общих операций агрегации. В MongoDB конвейер состоит из серии специальных операторов, применяемых к коллекции для обработки записей данных и возврата вычисленных результатов. Агрегация операций группирует значения из нескольких документов вместе и может выполнять множество операций над сгруппированными данными, чтобы вернуть один результат. Для получения дополнительной информации обратитесь к documentation.

Считайте, выполнив следующий трубопровод, чтобы получить желаемый результат:

var pipeline = [ 
    { "$unwind": "$userPicks" }, 
    { 
     "$group": { 
      "_id": { 
       "week": "$week", 
       "user": "$userPicks.user" 
      }, 
      "weeklyScore": { 
       "$sum": { 
        "$cond": [ 
         { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
         1, 0 
        ] 
       } 
      }   
     } 
    }, 
    { 
     "$group": { 
      "_id": "$_id.user", 
      "weeklyScores": { 
       "$push": { 
        "week": "$_id.week", 
        "score": "$weeklyScore" 
       } 
      }, 
      "totalScores": { "$sum": "$weeklyScore" } 
     } 
    } 
]; 

Game.aggregate(pipeline, function(err, results){ 
    User.populate(results, { "path": "_id" }, function(err, results) { 
     if (err) throw err; 
     console.log(JSON.stringify(results, undefined, 4)); 
    }); 
}) 

В вышеуказанном трубопроводе, первый шаг является $unwind оператор

{ "$unwind": "$userPicks" } 

, который поставляется в довольно когда данные хранятся в виде массива. Когда оператор разматывания применяется в поле данных списка, он будет генерировать новую запись для каждого элемента поля данных списка, на котором выполняется размотка. Это в основном сглаживает данные.

Это необходимая операция для следующего этапа трубопровода, этап $group, где группа уплощенных документов по полям week и "userPicks.user"

{ 
     "$group": { 
      "_id": { 
       "week": "$week", 
       "user": "$userPicks.user" 
      }, 
      "weeklyScore": { 
       "$sum": { 
        "$cond": [ 
         { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
         1, 0 
        ] 
       } 
      }   
     } 
    } 

Оператор конвейер $group похож на Предложение SQL GROUP BY. В SQL вы не можете использовать GROUP BY, если не используете какие-либо функции агрегации. Точно так же вы должны использовать функцию агрегации в MongoDB. Подробнее о функциях агрегации вы можете узнать here.

В этом $group операции, логика для расчета еженедельной счет каждого пользователя (то есть количество футбольных игр они предсказывают правильно каждую неделю) осуществляется через тройной оператор $cond, который принимает логическое условие, поскольку это первый аргумент (if), а затем возвращает второй аргумент, где оценка истинна (тогда) или третий аргумент, где false (else). Это делает истина/ложь возвращается в 1 и 0, чтобы питаться $sum соответственно:

"$cond": [ 
    { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
    1, 0 
] 

Так, если в документе, который обработал "$userPicks.chosenTeam" поле такое же, как "$winner" поле, $cond оператор кормами значение 1 в сумму иначе оно суммирует нулевое значение.

Вторые группа трубопроводы:

{ 
    "$group": { 
     "_id": "$user", 
     "weeklyScores": { 
      "$push": { 
       "week": "$_id.week", 
       "score": "$weeklyScore" 
      } 
     }, 
     "totalScores": { "$sum": "$weeklyScore" } 
    } 
} 

принимают документы из предыдущего трубопровода и групп их далее по user полю и вычисляют другую совокупность т.е. общего балла, используя оператор аккумулятора в $sum. В рамках одного и того же конвейера вы можете агрегировать список недельных оценок с помощью оператора $push, который возвращает массив значений выражений для каждой группы.

Следует отметить, что при выполнении трубопровода MongoDB соединяет операторы друг с другом. «Pipe» здесь означает значение Linux: выход оператора становится входом следующего оператора. Результатом каждого оператора является новая коллекция документов. Так Монго выполняет выше трубопровод следующим образом:

collection | $unwind | $group | $group => result 

Теперь при запуске трубопровода агрегации в Mongoose, результаты будут иметь _id ключ, который является идентификатором пользователя, и вам необходимо заполнить результаты на этом поле, т.е. Mongoose будет выполнять «соединение» с коллекцией пользователей и возвращать документы с пользовательской схемой в результатах.


В качестве примечания, чтобы помочь с пониманием трубопровода или отлаживать его вы должны получить неожиданные результаты, запустить агрегацию только с первым оператором трубопровода. Например, запустить агрегация в Монго оболочки, как:

db.games.aggregate([ 
    { "$unwind": "$userPicks" } 
]) 

Проверьте результат, чтобы увидеть, если userPicks массив деконструкции правильно. Если это дает ожидаемый результат, добавьте следующий:

db.games.aggregate([ 
    { "$unwind": "$userPicks" }, 
    { 
     "$group": { 
      "_id": { 
       "week": "$week", 
       "user": "$userPicks.user" 
      }, 
      "weeklyScore": { 
       "$sum": { 
        "$cond": [ 
         { "$eq": ["$userPicks.chosenTeam", "$winner"] }, 
         1, 0 
        ] 
       } 
      }   
     } 
    } 
]) 

Повторите шаги, пока вы получите до конечной стадии трубопровода.

+0

Я все еще новичок MongoDB/Mongoose. Не могли бы вы объяснить, что именно это делает? –

+0

@SierraGregg Я добавил несколько объяснений – chridam

+1

Спасибо! Использование агрегации намного чище, чем любая другая моя идея –

Смежные вопросы