MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Cocokkan setidaknya N elemen array dengan daftar kondisi

Pertanyaan Anda memiliki dua kemungkinan bagi saya, tetapi mungkin ada penjelasan untuk membantu Anda memulai.

Pertama-tama saya perlu menjelaskan kepada Anda bahwa Anda salah memahami maksud dari $elemMatch dan disalahgunakan dalam kasus ini.

Gagasan $elemMatch adalah membuat "dokumen kueri" yang sebenarnya diterapkan ke elemen array. Maksudnya adalah di mana Anda memiliki "beberapa kondisi" pada dokumen di dalam larik untuk mencocokkannya secara terpisah di dalam dokumen anggota, dan bukan di dalam seluruh larik dokumen luar. yaitu:

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

Dan kueri berikut akan berfungsi, meskipun tidak ada satu pun elemen aktual dalam larik itu yang cocok, tetapi seluruh dokumen berfungsi:

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

Tetapi untuk memeriksa apakah elemen aktual cocok dengan kedua kondisi tersebut, di sinilah Anda menggunakan $elemMatch :

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

Jadi tidak ada kecocokan dalam sampel itu, dan itu hanya akan cocok di mana elemen larik tertentu memiliki kedua elemen tersebut.

Sekarang kita memiliki $elemMatch dijelaskan, inilah kueri Anda yang disederhanakan:

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

Jauh lebih sederhana, dan bekerja dengan melihat semua anggota array dengan satu bidang dan mengembalikan di mana setiap elemen dalam dokumen berisi setidaknya satu dari hasil yang mungkin.

Tapi bukan apa yang Anda tanyakan, begitu seterusnya dengan pertanyaan Anda. Jika Anda membaca pernyataan terakhir itu, Anda akan menyadari bahwa $in sebenarnya adalah $or kondisi. Ini hanyalah bentuk singkat untuk menanyakan "atau" pada elemen yang sama dalam dokumen.

Dengan mengingat hal itu, inti dari apa yang Anda minta adalah "dan" operasi di mana semua "tiga" nilai terkandung. Dengan asumsi bahwa Anda hanya mengirim "tiga" item dalam pengujian, maka Anda dapat menggunakan formulir $and yang dalam bentuk singkat $all :

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

Itu hanya akan mengembalikan Anda dokumen yang memiliki elemen di dalam anggota larik yang cocok dengan "semua" elemen yang ditentukan dalam kondisi pengujian. Itu mungkin yang Anda inginkan, tetapi ada kasus di mana tentu saja Anda ingin menentukan daftar katakanlah, "empat atau lebih" artis untuk diuji dan hanya ingin "tiga" atau lebih sedikit dari itu, dalam hal ini sebuah $all operator terlalu singkat.

Tetapi ada cara logis untuk menyelesaikan ini, hanya perlu sedikit lebih banyak pemrosesan dengan operator yang tidak tersedia untuk kueri dasar tetapi yang tersedia untuk kerangka kerja agregasi :

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 } }
])

Tahap pertama mengimplementasikan kueri standar $match kondisi untuk memfilter dokumen hanya yang "mungkin" cocok dengan kondisi. Kasus logis di sini adalah menggunakan $in seperti sebelumnya, ia akan menemukan dokumen-dokumen di mana setidaknya salah satu elemen yang ada dalam larik "test" Anda ada dalam setidaknya satu bidang anggota dalam larik dokumen itu sendiri.

Klausa berikutnya adalah sesuatu yang idealnya Anda buat dalam kode karena berkaitan dengan "panjang" array. Idenya di sini adalah di mana Anda menginginkan setidaknya "tiga" kecocokan maka larik yang Anda uji dalam dokumen harus memiliki setidaknya "tiga" elemen untuk memenuhi itu, jadi tidak ada gunanya mengambil dokumen dengan "dua" atau kurang elemen larik karena mereka tidak akan pernah bisa menandingi "tiga".

Karena semua kueri MongoDB pada dasarnya hanyalah representasi dari struktur data, ini membuatnya sangat mudah untuk dibuat. yaitu, untuk JavaScript:

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

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

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

Logikanya ada bahwa "notasi titik" terbentuk dengan $exists menguji keberadaan elemen pada indeks yang ditentukan ( n-1 ), dan elemen tersebut harus ada di sana agar array setidaknya memiliki panjang tersebut.

Sisa penyempitan idealnya menggunakan $setIntersection metode untuk mengembalikan elemen yang cocok antara array aktual dan array yang diuji. Karena larik dalam dokumen tidak cocok dengan struktur untuk "larik uji", larik itu perlu diubah melalui $map operasi yang diatur untuk hanya mengembalikan bidang "artis" dari setiap elemen array.

Saat "persimpangan" dari dua larik itu dibuat, akhirnya diuji untuk $size dari daftar elemen umum yang dihasilkan tempat pengujian diterapkan untuk melihat bahwa "setidaknya tiga" dari elemen tersebut ditemukan memiliki kesamaan.

Akhirnya Anda hanya "memfilter" apa pun yang tidak benar menggunakan $match kondisi.

Idealnya Anda menggunakan MongoDB 2.6 atau lebih tinggi agar operator tersebut tersedia. Untuk versi 2.2.x dan 2.4.x sebelumnya, masih memungkinkan, tetapi hanya sedikit lebih banyak pekerjaan dan biaya pemrosesan:

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. Bagaimana cara menggunakan beberapa database mongodb di aplikasi boot musim semi?

  2. Mongodb NoRM dan POCO

  3. Apa cara terbaik untuk menangani kunci compsite saat menggunakan Salat dengan MongoDB?

  4. Simpan Subset Koleksi MongoDB ke Koleksi Lain

  5. Bagaimana cara mengubah jenis bidang?