MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Сравнете поне N елемента от масив със списък от условия

Вашият въпрос има две възможности за мен, но може би някакво обяснение, за да започнете.

Преди всичко трябва да ви обясня, че не разбирате намерението на $elemMatch и в този случай се злоупотребява.

Идеята на $elemMatch е да се създаде "документ за заявка", който всъщност се прилага към елементите на масива. Намерението е, когато имате "множество условия" за документ в рамките на масива, за да го съпоставите дискретно в членския документ, а не в целия масив на външния документ. т.е.:

{
   "data": [
       { "a": 1, "b": 3 },
       { "a": 2, "b": 2 }
   ]
}

И следната заявка ще работи, въпреки че нито един действителен елемент в този масив не съвпада, но целият документ съвпада:

db.collection.find({ "data.a": 1, "data.b": 2 })

Но за да проверите дали действителен елемент отговаря и на двете условия, тук използвате $elemMatch :

db.collection.find({ "data": { "a": 1, "b": 2 } })

Така че няма съвпадение в тази извадка и ще съвпадне само там, където конкретен елемент от масива има и двата елемента.

Сега имаме $elemMatch обяснено, ето вашата опростена заявка:

db.collection.find({ "tracks.artist": { "$in": arr } })

Много по-просто и работи, като разглежда всички членове на масива по едно поле и връща където всеки елемент в документа съдържа поне един от тези възможни резултати.

Но не това, което питаш, така че с въпроса си. Ако прочетете последното твърдение, трябва да разберете, че $in всъщност е $or състояние. Това е просто съкратена форма за питане на „или“ над същия елемент в документа.

Имайки това предвид, в основата на това, което питате, е „и“ операция, където се съдържат всичките "три" стойности. Ако приемем, че изпращате само „три“ елемента в теста, тогава можете да използвате форма на $and което е в съкратената форма на $all :

db.collection.find({ "tracks.artist": { "$all": arr } })

Това ще ви върне само документите, в които елементът в членовете на този масив съответства на "всички" елементи, посочени в тестовото условие. Това може и да е това, което искате, но има случай, в който, разбира се, искате да посочите списък от да речем „четирима или повече“ изпълнители за тестване и искате само „трима“ или някакъв по-малък брой от тях, в който случай $all операторът е твърде кратък.

Но има логичен начин за решаване на това, просто отнема малко повече обработка с оператори, които не са достъпни за основните заявки, но са достъпни за рамка за агрегиране :

var arr = ["A","B","C","D"];     // List for testing

db.collection.aggregate([
    // Match conditions for documents to narrow down
    { "$match": {
        "tracks.artist": { "$in": arr },
        "tracks.2": { "$exists": true }      // you would construct in code
    }},

    // Test the array conditions
    { "$project": {
        "user": 1,
        "tracks": 1,                         // any fields you want to keep
        "matched": {
            "$gte": [
                 { "$size": {
                     "$setIntersection": [
                         { "$map": {
                             "input": "$tracks",
                             "as": "t",
                             "in": { "$$t.artist" }
                         }},
                         arr
                     ]
                 }},
                 3
             ]
        }
    }},

    // Filter out anything that did not match
    { "$match": { "matched": true } }
])

Първият етап изпълнява стандартна заявка $match условие, за да филтрирате документите само към тези, които е „вероятно“ да отговарят на условията. Логичният случай тук е да използвате $in както преди, той ще намери тези документи, където поне един от елементите, присъстващи във вашия "тестов" масив, присъства в рамките на поне едно от полетата на член в собствения масив на документи.

Следващата клауза е нещо, което в идеалния случай трябва да вграждате в код, тъй като се отнася до „дължината“ на масива. Идеята тук е, когато искате поне „три“ съвпадения, тогава масивът, който тествате в документа, трябва да има поне „три“ елемента, за да изпълни това, така че няма смисъл да извличате документи с „два“ или по-малко елемента от масив тъй като те никога не могат да съпоставят "три".

Тъй като всички MongoDB заявки са по същество само представяне на структура от данни, това прави това много лесно за изграждане. т.е. за JavaScript:

var matchCount = 3;    // how many matches we want

var match1 = { "$match": { "tracks.artist": { "$in": arr } } };

match1["$match"]["tracks."+ (matchCount-1)] = { "$exits": true };

Логиката там е, че формата "точкова нотация" с $съществува тества наличието на елемент на посочения индекс ( n-1 ) и той трябва да е там, за да има масивът поне с тази дължина.

Останалата част от стесняването в идеалния случай използва $ setIntersection метод, за да върне съвпадащите елементи между действителния масив и тествания масив. Тъй като масивът в документа не съответства на структурата за "тестовия масив", той трябва да бъде трансформиран чрез $map операция, която е настроена да връща само полето "изпълнител" от всеки елемент на масива.

Тъй като се прави "пресичането" на тези два масива, то накрая се тества за $size от получения списък с общи елементи, където тестът е приложен, за да се види, че „поне три“ от тези елементи са установени като общи.

Накрая просто „филтрирате“ всичко, което не е вярно, като използвате $match състояние.

В идеалния случай използвате MongoDB 2.6 или по-нова версия, за да имате налични тези оператори. За по-ранните версии на 2.2.x и 2.4.x все още е възможно, но само малко повече работа и обработка:

db.collection.aggregate([
    // Match conditions for documents to narrow down
    { "$match": {
        "tracks.artist": { "$in": arr },
        "tracks.2": { "$exists": true }      // you would construct in code
    }},

    // Unwind the document array
    { "$unwind": "$tracks" },

    // Filter the content
    { "$match": { "tracks.artist": { "$in": arr } }},

    // Group for distinct values
    { "$group": {
        "_id": { 
           "_id": "$_id",
           "artist": "$tracks.artist"
        }
    }},

    // Make arrays with length
    { "$group": {
        "_id": "$_id._id",
        "artist": { "$push": "$_id.artist" },
        "length": { "$sum": 1 }
    }},

    // Filter out the sizes
    { "$match": { "length": { "$gte": 3 } }}
])



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB не работи. ГРЕШКА:dbpath (/data/db) не съществува.

  2. връщане на документ с най-нов поддокумент само в mongodb агрегат

  3. Mongoose:Сравненията на ObjectId се провалят непоследователно

  4. Как да получите обяснение за броя на MongoDB?

  5. Преобразуване на R списък в JSON