Заказ коллекции по нескольким полям и условию в Мондоиде

У меня есть 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 
     }
])

Рекомендации:

  1. https://docs.mongodb.com/manual/reference/operator/aggregation/cond/
  2. 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. На следующем этапе у нас есть проекция обязательных полей, за которой следует предельная оговорка. Вместо числовых, если бы эти поля были строками, потребовалось бы дополненное конкатенация полей.