Это хорошая идея, чтобы перенести старые версии в Android-студии?

Я не знаю о лучшем, но есть несколько разных способов приблизиться к этому, и в зависимости от версии MongoDB у вас есть.

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

Так что, если вы действительно хотели документ чтобы содержать все эти элементы, тогда оператор $all был бы очевидным выбором:

db.collection.find({ "users.user": { "$all": [ 1, 5, 7 ] } })

Но работая с предположением о том, что ваша логика на самом деле предназначена, по крайней мере в соответствии с предложением вы можете «фильтровать» эти результаты, комбинируя с оператором $in , чтобы на вашем было меньше документов $where ** условие в оценке JavaScript:

db.collection.find({
    "users.user": { "$in": [ 1, 5, 7 ] },
    "$where": function() { 
        var ids = [1, 5, 7];
        return this.users.every(function(u) { 
            return ids.indexOf(u.user) !== -1;
        });
    }
})

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

Или, возможно, вы рассматриваете логическую абстракцию оператора $and , используемого в комбинации с $or и, возможно, оператор $size в зависимости от ваших реальных условий массива:

db.collection.find({
    "$or": [
        { "users.user": { "$all": [ 1, 5, 7 ] } },
        { "users.user": { "$all": [ 1, 5 ] } },
        { "users.user": { "$all": [ 1, 7 ] } },
        { "users": { "$size": 1 }, "users.user": 1 },
        { "users": { "$size": 1 }, "users.user": 5 },
        { "users": { "$size": 1 }, "users.user": 7 }
    ]
})

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

ПРИМЕЧАНИЕ. На самом деле полный сбой в этом случае, поскольку это делает что-то совершенно другое и фактически приводит к логическому $in

Альтернативы с каркасом агрегации, ваш пробег может отличаться от того, что наиболее эффективно из-за количества документов в вашей коллекции, одного подхода с MongoDB 2.6 и выше:

db.problem.aggregate([
    // Match documents that "could" meet the conditions
    { "$match": { 
        "users.user": { "$in": [ 1, 5, 7 ] } 
    }},

    // Keep your original document and a copy of the array
    { "$project": {
        "_id": {
            "_id": "$_id",
            "date": "$date",
            "users": "$users"
        },
        "users": 1,
    }},

    // Unwind the array copy
    { "$unwind": "$users" },

    // Just keeping the "user" element value
    { "$group": {
        "_id": "$_id",
        "users": { "$push": "$users.user" }
    }},

    // Compare to see if all elements are a member of the desired match
    { "$project": {
        "match": { "$setEquals": [
            { "$setIntersection": [ "$users", [ 1, 5, 7 ] ] },
            "$users"
        ]}
    }},

    // Filter out any documents that did not match
    { "$match": { "match": true } },

    // Return the original document form
    { "$project": {
        "_id": "$_id._id",
        "date": "$_id.date",
        "users": "$_id.users"
    }}
])

So этот подход использует некоторые недавно введенные операторы set , чтобы сравнить содержимое, хотя, конечно, вам нужно реструктурировать массив, чтобы сделать е. сравнение.

Как указывалось, в $setIsSubset имеется прямой оператор, который делает эквивалент комбинированных операторов выше в одном операторе:

db.collection.aggregate([
    { "$match": { 
        "users.user": { "$in": [ 1,5,7 ] } 
    }},
    { "$project": {
        "_id": {
            "_id": "$_id",
            "date": "$date",
            "users": "$users"
        },
        "users": 1,
    }},
    { "$unwind": "$users" },
    { "$group": {
        "_id": "$_id",
        "users": { "$push": "$users.user" }
    }},
    { "$project": {
        "match": { "$setIsSubset": [ "$users", [ 1, 5, 7 ] ] }
    }},
    { "$match": { "match": true } },
    { "$project": {
        "_id": "$_id._id",
        "date": "$_id.date",
        "users": "$_id.users"
    }}
])

Или с другим подходом, все еще использующим оператор $size от MongoDB 2.6:

db.collection.aggregate([
    // Match documents that "could" meet the conditions
    { "$match": { 
        "users.user": { "$in": [ 1, 5, 7 ] } 
    }},

    // Keep your original document and a copy of the array
    // and a note of it's current size
    { "$project": {
        "_id": {
            "_id": "$_id",
            "date": "$date",
            "users": "$users"
        },
        "users": 1,
        "size": { "$size": "$users" }
    }},

    // Unwind the array copy
    { "$unwind": "$users" },

    // Filter array contents that do not match
    { "$match": { 
        "users.user": { "$in": [ 1, 5, 7 ] } 
    }},

    // Count the array elements that did match
    { "$group": {
        "_id": "$_id",
        "size": { "$first": "$size" },
        "count": { "$sum": 1 }
    }},

    // Compare the original size to the matched count
    { "$project": { 
        "match": { "$eq": [ "$size", "$count" ] } 
    }},

    // Filter out documents that were not the same
    { "$match": { "match": true } },

    // Return the original document form
    { "$project": {
        "_id": "$_id._id",
        "date": "$_id.date",
        "users": "$_id.users"
    }}
])

Что, конечно, все еще можно сделать , хотя немного более длинные в версиях до 2.6:

db.collection.aggregate([
    // Match documents that "could" meet the conditions
    { "$match": { 
        "users.user": { "$in": [ 1, 5, 7 ] } 
    }},

    // Keep your original document and a copy of the array
    { "$project": {
        "_id": {
            "_id": "$_id",
            "date": "$date",
            "users": "$users"
        },
        "users": 1,
    }},

    // Unwind the array copy
    { "$unwind": "$users" },

    // Group it back to get it's original size
    { "$group": { 
        "_id": "$_id",
        "users": { "$push": "$users" },
        "size": { "$sum": 1 }
    }},

    // Unwind the array copy again
    { "$unwind": "$users" },

    // Filter array contents that do not match
    { "$match": { 
        "users.user": { "$in": [ 1, 5, 7 ] } 
    }},

    // Count the array elements that did match
    { "$group": {
        "_id": "$_id",
        "size": { "$first": "$size" },
        "count": { "$sum": 1 }
    }},

    // Compare the original size to the matched count
    { "$project": { 
        "match": { "$eq": [ "$size", "$count" ] } 
    }},

    // Filter out documents that were not the same
    { "$match": { "match": true } },

    // Return the original document form
    { "$project": {
        "_id": "$_id._id",
        "date": "$_id.date",
        "users": "$_id.users"
    }}
])

Это обычно округляет разные способы, пробует их и видит, что лучше всего подходит для вас. По всей вероятности, простая комбинация $in с вашей существующей формой, вероятно, будет лучшей. Но во всех случаях убедитесь, что у вас есть индекс, который можно выбрать:

db.collection.ensureIndex({ "users.user": 1 })

. Это даст вам лучшую производительность, если вы каким-то образом получаете доступ к этому, так как все примеры здесь.


Вердикт

Я был заинтригован этим, поэтому в конечном итоге наработал тестовый пример, чтобы узнать, что было лучше всего. Итак, сначала некоторые генерации тестовых данных:

var batch = [];
for ( var n = 1; n <= 10000; n++ ) {
    var elements = Math.floor(Math.random(10)*10)+1;

    var obj = { date: new Date(), users: [] };
    for ( var x = 0; x < elements; x++ ) {
        var user = Math.floor(Math.random(10)*10)+1,
            group = Math.floor(Math.random(10)*10)+1;

        obj.users.push({ user: user, group: group });
    }

    batch.push( obj );

    if ( n % 500 == 0 ) {
        db.problem.insert( batch );
        batch = [];
    }

} 

С 10000 документами в коллекции со случайными массивами от 1..10 в длине, содержащими случайные значения 1..0, я пришел к числу совпадений 430 документы (уменьшены с 7749 по совпадению $in) со следующими результатами (avg):

  • JavaScript с предложением $in: 420 мс
  • Совокупность с $size : 395ms
  • Агрегат с подсчетом группы: 650 мс
  • Совокупность с двумя операторами набора: 275 мс
  • Совокупность с $setIsSubset: 250 мс

Отмечая, что по образцам, выполненным все, кроме последних двух, была дисперсия peak примерно на 100 мс быстрее, а последние два показали 220 мс ответ. Наибольшие вариации были в запросе JavaScript, который также показал результаты на 100 мс медленнее.

Но дело здесь в том, что касается аппаратного обеспечения, которое на моем ноутбуке под VM не особенно велико, но дает представление.

Таким образом, совокупность и, в частности, версия MongoDB 2.6.1 с установленными операторами явно выигрывают от производительности с дополнительным небольшим усилением, поступающим от $setIsSubset как один оператор.

Это особенно интересно (как указано в методе, совместимом с 2.4) наибольшая стоимость этого процесса будет заключаться в заявлении $unwind (более 100 мс), поэтому при выборе $in, имеющем среднее значение около 32 мс, остальные этапы конвейера выполняются меньше чем 100 мс в среднем. Таким образом, это дает относительное представление об агрегации и производительности JavaScript.

0
задан PremiPrem 16 January 2019 в 16:56
поделиться