Kasus utama di sini adalah bahwa hasil pencarian "teks" umumnya lebih diutamakan daripada kondisi filter lain dalam kueri, dan karena itu menjadi perlu untuk "pertama" memperoleh hasil dari komponen "teks", dan kemudian pada dasarnya "memindai" untuk ketentuan lain dalam dokumen.
Jenis pencarian ini mungkin sulit untuk dioptimalkan bersama dengan "rentang" atau jenis kondisi kecocokan "ketidaksetaraan" apa pun sehubungan dengan hasil pencarian teks, dan sebagian besar disebabkan oleh cara MongoDB menangani jenis indeks "khusus" ini.
Untuk demonstrasi singkat, pertimbangkan penyiapan dasar berikut:
db.texty.drop();
db.texty.insert([
{ "a": "a", "text": "something" },
{ "a": "b", "text": "something" },
{ "a": "b", "text": "nothing much" },
{ "a": "c", "text": "something" }
])
db.texty.createIndex({ "text": "text" })
db.texty.createIndex({ "a": 1 })
Jadi jika Anda ingin melihat ini dengan kondisi pencarian teks serta pertimbangan rentang di bidang lain ( { "$lt": "c" }
), maka Anda dapat menangani sebagai berikut:
db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } }).explain()
Dengan penjelasan output seperti ( bagian penting ):
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"a" : {
"$lt" : "c"
}
},
"inputStage" : {
"stage" : "TEXT",
"indexPrefix" : {
},
"indexName" : "text_text",
"parsedTextQuery" : {
"terms" : [
"someth"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "TEXT_OR",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_fts" : "text",
"_ftsx" : 1
},
"indexName" : "text_text",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
}
}
}
}
}
},
Yang pada dasarnya mengatakan "pertama ambilkan saya hasil teksnya, lalu filter hasil yang diambil dengan kondisi lain" . Jadi jelas hanya indeks "teks" yang digunakan di sini dan kemudian semua hasil yang dikembalikannya selanjutnya disaring dengan memeriksa kontennya.
Ini tidak optimal karena dua alasan, karena kemungkinan besar data dibatasi oleh kondisi "rentang" daripada kecocokan dari pencarian teks. Kedua, meskipun ada indeks pada data lain, itu tidak digunakan di sini untuk perbandingan. Jadi, seluruh dokumen dimuat untuk setiap hasil dan filter diuji.
Anda kemudian dapat mempertimbangkan format indeks "gabungan" di sini, dan pada awalnya akan tampak logis bahwa jika "rentang" lebih spesifik untuk pemilihan, maka sertakan itu sebagai urutan awalan dari kunci yang diindeks:
db.texty.dropIndexes();
db.texty.createIndex({ "a": 1, "text": "text" })
Namun ada kendala di sini, karena saat Anda mencoba menjalankan kueri lagi:
db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } })
Ini akan menghasilkan kesalahan:
Kesalahan:kesalahan:{"waitedMS" :NumberLong(0),"ok" :0,"errmsg" :"permintaan pemrosesan kesalahan:ns=test.textyTree:$dan\n a $lt \"c\"\n TEKS :query=something, language=english, caseSensitive=0, diacriticSensitive=0, tag=NULL\nSort:{}\nProj:{}\n perencana mengembalikan kesalahan:gagal menggunakan indeks teks untuk memenuhi kueri $text (jika indeks teks adalah gabungan, apakah predikat kesetaraan diberikan untuk semua bidang awalan?)","code" :2}
Jadi meskipun itu mungkin tampak "optimal", cara MongoDB memproses kueri ( dan benar-benar pemilihan indeks ) untuk indeks "teks" khusus, "pengecualian" di luar rentang ini tidak mungkin dilakukan.
Namun Anda dapat melakukan pencocokan "kesetaraan" dengan cara yang sangat efisien:
db.texty.find({ "a": "b", "$text": { "$search": "something" } }).explain()
Dengan output jelaskan:
"winningPlan" : {
"stage" : "TEXT",
"indexPrefix" : {
"a" : "b"
},
"indexName" : "a_1_text_text",
"parsedTextQuery" : {
"terms" : [
"someth"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "TEXT_OR",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"a" : 1,
"_fts" : "text",
"_ftsx" : 1
},
"indexName" : "a_1_text_text",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
}
}
}
}
},
Jadi indeks digunakan dan dapat ditampilkan untuk "memfilter" konten yang diberikan ke teks yang cocok dengan output dari kondisi lain.
Jika memang Anda menyimpan "awalan" ke indeks sebagai bidang "teks" untuk mencari, namun:
db.texty.dropIndexes();
db.texty.createIndex({ "text": "text", "a": 1 })
Kemudian lakukan pencarian:
db.texty.find({ "a": { "$lt": "c" }, "$text": { "$search": "something" } }).explain()
Kemudian Anda melihat hasil yang mirip dengan kecocokan "kesetaraan" di atas:
"winningPlan" : {
"stage" : "TEXT",
"indexPrefix" : {
},
"indexName" : "text_text_a_1",
"parsedTextQuery" : {
"terms" : [
"someth"
],
"negatedTerms" : [ ],
"phrases" : [ ],
"negatedPhrases" : [ ]
},
"inputStage" : {
"stage" : "TEXT_MATCH",
"inputStage" : {
"stage" : "TEXT_OR",
"filter" : {
"a" : {
"$lt" : "c"
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_fts" : "text",
"_ftsx" : 1,
"a" : 1
},
"indexName" : "text_text_a_1",
"isMultiKey" : true,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
}
}
}
}
},
Perbedaan besar di sini dari upaya pertama adalah di mana filter
ditempatkan dalam rantai pemrosesan, menunjukkan bahwa meskipun bukan "awalan" yang cocok ( yang paling optimal ), konten memang sedang dipindai dari indeks "sebelum" dikirim ke tahap "teks".
Jadi ini "difilter sebelumnya" tetapi tentu saja tidak dengan cara yang paling optimal, dan ini karena sifat dari bagaimana indeks "teks" digunakan. Jadi, jika Anda hanya mempertimbangkan rentang biasa pada indeks dengan sendirinya:
db.texty.createIndex({ "a": 1 })
db.texty.find({ "a": { "$lt": "c" } }).explain()
Kemudian output jelaskan:
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"a" : 1
},
"indexName" : "a_1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"a" : [
"[\"\", \"c\")"
]
}
}
},
Maka setidaknya mendapatkan indexBounds
untuk mempertimbangkan dan hanya melihat bagian indeks yang berada dalam batas tersebut.
Jadi itulah perbedaan di sini. Menggunakan struktur "gabungan" akan menghemat beberapa siklus iterasi di sini dengan dapat mempersempit pilihan, tetapi tetap harus memindai semua entri indeks untuk memfilter, dan tentu saja harus tidak menjadi elemen "awalan" dalam indeks kecuali Anda dapat menggunakan kecocokan kesetaraan di dalamnya.
Tanpa struktur gabungan dalam indeks, Anda selalu mengembalikan hasil teks "pertama", dan kemudian menerapkan kondisi lain ke hasil tersebut. Juga tidak mungkin untuk "menggabungkan/memotong" hasil dari melihat indeks "teks" dan indeks "normal" karena penanganan mesin kueri. Itu umumnya tidak akan menjadi pendekatan yang optimal, jadi perencanaan untuk pertimbangan itu penting.
Singkatnya, idealnya majemukkan dengan "persamaan" yang cocok dengan "awalan", dan jika tidak, sertakan dalam indeks "setelah" definisi teks.