Сортировка списка объектов по списку строк с помощью MongoDB.Driver [duplicate]

Включить переменную строки подключения перед запросом MySQL. Например, $connt в этом коде:

$results = mysql_query($connt, "SELECT * FROM users");
62
задан styvane 2 November 2015 в 07:18
поделиться

8 ответов

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

Если вам нужно сохранить этот порядок, то у вас в основном есть два варианта.

Итак, скажем, что вы сопоставляли значения _id в своих документах с массивом, который будет передан в $in как [ 4, 2, 8 ].

Подход с использованием Aggregate


var list = [ 4, 2, 8 ];

db.collection.aggregate([

    // Match the selected documents by "_id"
    { "$match": {
        "_id": { "$in": [ 4, 2, 8 ] },
    },

    // Project a "weight" to each document
    { "$project": {
        "weight": { "$cond": [
            { "$eq": [ "$_id", 4  ] },
            1,
            { "$cond": [
                { "$eq": [ "$_id", 2 ] },
                2,
                3
            ]}
        ]}
    }},

    // Sort the results
    { "$sort": { "weight": 1 } }

])

Таким образом, это будет расширенная форма. Что в основном происходит здесь, так это то, что так же, как массив значений передается в $in, вы также создаете «вложенный» $cond оператор для проверки значений и назначения соответствующего веса. Поскольку это «весовое» значение отражает порядок элементов в массиве, вы можете передать это значение на этап сортировки, чтобы получить ваши результаты в требуемом порядке.

Конечно, вы действительно «строите» «оператор конвейера в коде, что-то вроде этого:

var list = [ 4, 2, 8 ];

var stack = [];

for (var i = list.length - 1; i > 0; i--) {

    var rec = {
        "$cond": [
            { "$eq": [ "$_id", list[i-1] ] },
            i
        ]
    };

    if ( stack.length == 0 ) {
        rec["$cond"].push( i+1 );
    } else {
        var lval = stack.pop();
        rec["$cond"].push( lval );
    }

    stack.push( rec );

}

var pipeline = [
    { "$match": { "_id": { "$in": list } }},
    { "$project": { "weight": stack[0] }},
    { "$sort": { "weight": 1 } }
];

db.collection.aggregate( pipeline );

Подход с использованием mapReduce


Конечно, если все это кажется вам полезным для вашей чувствительности, вы можете сделать то же самое, используя mapReduce, который выглядит проще, но, скорее всего, будет работать несколько медленнее.

var list = [ 4, 2, 8 ];

db.collection.mapReduce(
    function () {
        var order = inputs.indexOf(this._id);
        emit( order, { doc: this } );
    },
    function() {},
    { 
        "out": { "inline": 1 },
        "query": { "_id": { "$in": list } },
        "scope": { "inputs": list } ,
        "finalize": function (key, value) {
            return value.doc;
        }
    }
)

И это в основном полагается на испускаемые «ключевые» значения, находящиеся в «индексном порядке» того, как они встречаются во входе array.


Таким образом, это, по сути, ваши способы поддержания порядка списка входных данных в $in, когда у вас уже есть этот список в определенном порядке.

63
ответ дан Robert Rossmann 26 August 2018 в 10:46
поделиться

Легкий способ упорядочить результат после того, как mongo возвращает массив, состоит в том, чтобы сделать объект с идентификатором в качестве ключей, а затем отобразить на заданный _id, чтобы вернуть упорядоченный массив.

async function batchUsers(Users, keys) {
  const unorderedUsers = await Users.find({_id: {$in: keys}}).toArray()
  let obj = {}
  unorderedUsers.forEach(x => obj[x._id]=x)
  const ordered = keys.map(key => obj[key])
  return ordered
}
0
ответ дан Arne Jenssen 26 August 2018 в 10:46
поделиться

Я знаю, что это старый поток, но если вы просто возвращаете значение Id в массиве, возможно, вам придется выбрать этот синтаксис. Поскольку мне не показалось, что значение indexOf соответствует формату Mongo ObjectId.

  obj.map = function() {
    for(var i = 0; i < inputs.length; i++){
      if(this._id.equals(inputs[i])) {
        var order = i;
      }
    }
    emit(order, {doc: this});
  };

Как преобразовать mongo ObjectId .toString без включения 'ObjectId ()' wrapper - только значение?

4
ответ дан Community 26 August 2018 в 10:46
поделиться

Вы можете гарантировать порядок с $ или предложением.

. Вместо этого используйте $or: [ _ids.map(_id => ({_id}))].

0
ответ дан d3t5411 26 August 2018 в 10:46
поделиться

Всегда? Никогда. Порядок всегда один и тот же: неопределенный (возможно, физический порядок хранения документов). Если вы не сортируете его.

2
ответ дан freakish 26 August 2018 в 10:46
поделиться

Если вы не хотите использовать aggregate, другое решение должно использовать find, а затем сортировать результаты на стороне клиента с помощью array#sort :

Если значения $in являются примитивными типами, такими как числа, вы можете использовать такой подход, как:

var ids = [4, 2, 8, 1, 9, 3, 5, 6];
MyModel.find({ _id: { $in: ids } }).exec(function(err, docs) {
    docs.sort(function(a, b) {
        // Sort docs by the order of their _id values in ids.
        return ids.indexOf(a._id) - ids.indexOf(b._id);
    });
});

Если значения $in являются непримитивными типами, например ObjectId s, требуется другой подход поскольку indexOf сравнивается по ссылке в этом случае.

Если вы используете Node.js 4.x +, вы можете использовать Array#findIndex и ObjectID#equals , чтобы отрегулировать это, изменив функцию sort на:

docs.sort((a, b) => ids.findIndex(id => a._id.equals(id)) - 
                    ids.findIndex(id => b._id.equals(id)));

Или с любой версией Node.js, с подчеркиванием / lodash's findIndex :

docs.sort(function (a, b) {
    return _.findIndex(ids, function (id) { return a._id.equals(id); }) -
           _.findIndex(ids, function (id) { return b._id.equals(id); });
});
17
ответ дан JohnnyHK 26 August 2018 в 10:46
поделиться

Другой способ использования запроса агрегирования применим только для версии MongoDB> 3.4 -

. Благодарим вас за это сообщение .

Примеры документов для выбирается в этом порядке -

var order = [ "David", "Charlie", "Tess" ];

Запрос -

var query = [
             {$match: {name: {$in: order}}},
             {$addFields: {"__order": {$indexOfArray: [order, "$name" ]}}},
             {$sort: {"__order": 1}}
            ];

var result = db.users.aggregate(query);

Еще одна цитата из сообщения, объясняющая эти операторы агрегирования -

Этап «$ addFields» является новым в версии 3.4 и позволяет вам «создавать» новые поля для существующих документов, не зная всех других существующих полей. Новое выражение «$ indexOfArray» возвращает позицию определенного элемента в заданном массиве.

В основном оператор addToSet добавляет новое поле order в каждый документ, когда он его находит, и это Поле order представляет собой первоначальный порядок нашего массива, который мы предоставили. Затем мы просто сортируем документы на основе этого поля.

13
ответ дан Jyotman Singh 26 August 2018 в 10:46
поделиться

Это решение кода после того, как результаты получены из Mongo. Использование карты для хранения индекса и затем замены значений.

catDetails := make([]CategoryDetail, 0)
err = sess.DB(mdb).C("category").
    Find(bson.M{
    "_id":       bson.M{"$in": path},
    "is_active": 1,
    "name":      bson.M{"$ne": ""},
    "url.path":  bson.M{"$exists": true, "$ne": ""},
}).
    Select(
    bson.M{
        "is_active": 1,
        "name":      1,
        "url.path":  1,
    }).All(&catDetails)

if err != nil{
    return 
}
categoryOrderMap := make(map[int]int)

for index, v := range catDetails {
    categoryOrderMap[v.Id] = index
}

counter := 0
for i := 0; counter < len(categoryOrderMap); i++ {
    if catId := int(path[i].(float64)); catId > 0 {
        fmt.Println("cat", catId)
        if swapIndex, exists := categoryOrderMap[catId]; exists {
            if counter != swapIndex {
                catDetails[swapIndex], catDetails[counter] = catDetails[counter], catDetails[swapIndex]
                categoryOrderMap[catId] = counter
                categoryOrderMap[catDetails[swapIndex].Id] = swapIndex
            }
            counter++
        }
    }
}
0
ответ дан Prateek 26 August 2018 в 10:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: