Заказ коллекции по нескольким полям и условию в Мондоиде
У меня есть 4 поля в документе, который является name
, online
, like
и score
. Я хочу заказать несколько полей и условий для сбора миллионов документов с разбивкой на страницы.
Пример некоторых документов:
Мои пользовательские документы:
{ "_id": 1, "name": "A", "online": 1, "like": 10, "score": 1 },
{ "_id": 2, "name": "B", "online": 0, "like": 9, "score": 0 },
{ "_id": 3, "name": "C", "online": 0, "like": 8, "score": 1 },
{ "_id": 4, "name": "D", "online": 1, "like": 8, "score": 0 },
{ "_id": 5, "name": "E", "online": 1, "like": 7, "score": 1 },
{ "_id": 6, "name": "F", "online": 0, "like": 10, "score": 0 },
Я объясню свою мысль следующим примером (пример с использованием массива).
Пример в рубиновом языке, у меня структура массива выглядит так:
[["A", 1, 10, 1],
["B", 0, 9, 1],
["C", 0, 8, 1],
["D", 1, 8, 0],
["E", 1, 7, 1],
["F", 0, 10, 0]]
Если на online
есть 1
должен быть своего родом снова низведения like
, но когда online
в 0
должно быть своего родом снова низведения score
.
пример сортировки:
list.sort{|a, b| a[1] == 1 ? ([-a[1], -a[2]] <=> [-b[1], -b[2]]) : ([-a[1], -a[3]] <=> [-b[1], -b[3]]) }
Результат:
[["A", 1, 10, 1],
["D", 1, 8, 0],
["E", 1, 7, 1],
["B", 0, 9, 1],
["C", 0, 8, 1],
["F", 0, 10, 0]]
Это сортировка массива, но моя проблема в том, что у меня есть коллекция mongodb и миллионов документов, я не могу использовать сортировку массива, потому что она будет тяжелой загрузкой в базу данных, должна получить все документы и преобразовать в массив (включая сортировку) и paginate их, я думаю, что плохая идея.
Я попробовал с order()
/order_by()
mongoid необязательный метод вроде:
User.
order_by([:online, :desc], [:like, :desc], [:score, :desc]).
hint(online: -1, like: -1, score: -1).
page(1).per(10)
Но этот запрос - это только порядок в online
и score
, есть ли метод сортировки в mongoid, например сортировка массива? или что-то вроде пузыря, сортирующегося в монгодбе?
такая же проблема: Ruby on Rails: merge
результаты монгоидных критериев и подкачки, метод merge
не помог мне, потому что он может заменить первые критерии.
Ответы
Ответ 1
Использование агрегации $cond
User.collection.aggregate([
{
"$project" => {
"id" => 1,
"name" => 1,
"online" => 1,
"like" => 1,
"score" => 1,
"sort" => {
"$cond" => {
"if" => {
"$eq" => ["$online", 1]
},
"then" => "$like",
"else" => "$score"
}
}
}
},
{
"$sort" => {
"online" => -1,
"sort" => -1,
"id" => 1
}
},
{
"$skip" => 0
{
"$limit" => 12
}
])
Рекомендации:
- https://docs.mongodb.com/manual/reference/operator/aggregation/cond/
- https://www.oodlestechnologies.com/blogs/How-to-use-Conditional-Statements-for-sorting-data-in-MongoDB
Ответ 2
Предполагая, что оценки полей, например, и онлайн, являются числовыми, на основе условия мы можем создать вспомогательное поле, которое является суммой онлайн и как, например, онлайн, и оценка. После чего мы можем сортировать новое поле и любое другое обязательное поле.
db.collection aggregate([ {"$addFields": {"refCount":{"$cond":{"if":{"$eq":["$online", 1]}, "then":{"$add": ["$online", "$like"]}, "else": {"$add": ["$online", "$score"]} } } }},{ "$sort": { "refCount": -1 ,"id":1} },{"$project":{"id": 1, "name": 1, "online": 1, "like": 1,"score":1}},{ "$limit": 10 } ])
На первом этапе трубопровода строится вспомогательная коррекция поля. Это поле затем используется в $ sort вместе с id. На следующем этапе у нас есть проекция обязательных полей, за которой следует предельная оговорка. Вместо числовых, если бы эти поля были строками, потребовалось бы дополненное конкатенация полей.