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

Tampilkan hanya bidang yang cocok untuk pencarian teks MongoDB

Setelah memikirkan ini lama, saya pikir mungkin untuk mengimplementasikan apa yang Anda inginkan. Namun, ini tidak cocok untuk database yang sangat besar dan saya belum menemukan pendekatan tambahan. Tidak memiliki stemming dan stop word harus didefinisikan secara manual.

Idenya adalah menggunakan mapReduce untuk membuat kumpulan kata pencarian dengan referensi ke dokumen asal dan bidang tempat kata pencarian berasal. Kemudian, untuk kueri aktual untuk pelengkapan otomatis dilakukan menggunakan agregasi sederhana yang menggunakan indeks dan karenanya harus agak cepat.

Jadi kami akan bekerja dengan tiga dokumen berikut

{
  "name" : "John F. Kennedy",
  "address" : "Kenson Street 1, 12345 Footown, TX, USA",
  "note" : "loves Kendo and Sushi"
}

dan

{
  "name" : "Robert F. Kennedy",
  "address" : "High Street 1, 54321 Bartown, FL, USA",
  "note" : "loves Ethel and cigars"
}

dan

{
  "name" : "Robert F. Sushi",
  "address" : "Sushi Street 1, 54321 Bartown, FL, USA",
  "note" : "loves Sushi and more Sushi"
}

dalam koleksi yang disebut textsearch .

Tahap peta/perkecil

Apa yang pada dasarnya kami lakukan adalah bahwa kami akan memproses setiap kata di salah satu dari tiga bidang, menghapus kata dan angka berhenti dan menyimpan setiap kata dengan _id dokumen dan bidang kejadian di tabel perantara.

Kode beranotasi:

db.textsearch.mapReduce(
  function() {

    // We need to save this in a local var as per scoping problems
    var document = this;

    // You need to expand this according to your needs
    var stopwords = ["the","this","and","or"];

    // This denotes the fields which should be processed
    var fields = ["name","address","note"];

    // For each field...
    fields.forEach(

      function(field){

        // ... we split the field into single words...
        var words = (document[field]).split(" ");

        words.forEach(

          function(word){
            // ...and remove unwanted characters.
            // Please note that this regex may well need to be enhanced
            var cleaned = word.replace(/[;,.]/g,"")

            // Next we check...
            if(
              // ...wether the current word is in the stopwords list,...
              (stopwords.indexOf(word)>-1) ||

              // ...is either a float or an integer... 
              !(isNaN(parseInt(cleaned))) ||
              !(isNaN(parseFloat(cleaned))) ||

              // or is only one character.
              cleaned.length < 2
            )
            {
              // In any of those cases, we do not want to have the current word in our list.
              return
            }
              // Otherwise, we want to have the current word processed.
              // Note that we have to use a multikey id and a static field in order
              // to overcome one of MongoDB's mapReduce limitations:
              // it can not have multiple values assigned to a key.
              emit({'word':cleaned,'doc':document._id,'field':field},1)

          }
        )
      }
    )
  },
  function(key,values) {

    // We sum up each occurence of each word
    // in each field in every document...
    return Array.sum(values);
  },
    // ..and write the result to a collection
  {out: "searchtst" }
)

Menjalankan ini akan menghasilkan pembuatan koleksi searchtst . Jika sudah ada, semua isinya akan diganti.

Ini akan terlihat seperti ini:

{ "_id" : { "word" : "Bartown", "doc" : ObjectId("544b9811fd9270c1492f5835"), "field" : "address" }, "value" : 1 }
{ "_id" : { "word" : "Bartown", "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "address" }, "value" : 1 }
{ "_id" : { "word" : "Ethel", "doc" : ObjectId("544b9811fd9270c1492f5835"), "field" : "note" }, "value" : 1 }
{ "_id" : { "word" : "FL", "doc" : ObjectId("544b9811fd9270c1492f5835"), "field" : "address" }, "value" : 1 }
{ "_id" : { "word" : "FL", "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "address" }, "value" : 1 }
{ "_id" : { "word" : "Footown", "doc" : ObjectId("544b7e44fd9270c1492f5834"), "field" : "address" }, "value" : 1 }
[...]
{ "_id" : { "word" : "Sushi", "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "name" }, "value" : 1 }
{ "_id" : { "word" : "Sushi", "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "note" }, "value" : 2 }
[...]

Ada beberapa hal yang perlu diperhatikan di sini. Pertama-tama, sebuah kata dapat memiliki beberapa kemunculan, misalnya dengan "FL". Namun, mungkin dalam dokumen yang berbeda, seperti yang terjadi di sini. Sebuah kata juga dapat memiliki beberapa kemunculan dalam satu bidang dari satu dokumen, di sisi lain. Kami akan menggunakan ini untuk keuntungan kami nanti.

Kedua, kami memiliki semua bidang, terutama word bidang dalam indeks gabungan untuk _id , yang seharusnya membuat kueri yang datang cukup cepat. Namun, ini juga berarti indeks akan cukup besar dan – seperti untuk semua indeks – cenderung memakan RAM.

Tahap agregasi

Jadi kami telah mengurangi daftar kata. Sekarang kita meminta (sub) string. Yang perlu kita lakukan adalah menemukan semua kata yang dimulai dengan string yang diketik pengguna sejauh ini, mengembalikan daftar kata yang cocok dengan string itu. Untuk dapat melakukan ini dan mendapatkan hasil dalam bentuk yang sesuai untuk kami, kami menggunakan agregasi.

Agregasi ini seharusnya cukup cepat, karena semua bidang yang diperlukan untuk kueri adalah bagian dari indeks gabungan.

Berikut adalah agregasi beranotasi untuk kasus ketika pengguna mengetikkan huruf S :

db.searchtst.aggregate(
  // We match case insensitive ("i") as we want to prevent
  // typos to reduce our search results
  { $match:{"_id.word":/^S/i} },
  { $group:{
      // Here is where the magic happens:
      // we create a list of distinct words...
      _id:"$_id.word",
      occurrences:{
        // ...add each occurrence to an array...
        $push:{
          doc:"$_id.doc",
          field:"$_id.field"
        } 
      },
      // ...and add up all occurrences to a score
      // Note that this is optional and might be skipped
      // to speed up things, as we should have a covered query
      // when not accessing $value, though I am not too sure about that
      score:{$sum:"$value"}
    }
  },
  {
    // Optional. See above
    $sort:{_id:-1,score:1}
  }
)

Hasil kueri ini terlihat seperti ini dan seharusnya cukup jelas:

{
  "_id" : "Sushi",
  "occurences" : [
    { "doc" : ObjectId("544b7e44fd9270c1492f5834"), "field" : "note" },
    { "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "address" },
    { "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "name" },
    { "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "note" }
  ],
  "score" : 5
}
{
  "_id" : "Street",
  "occurences" : [
    { "doc" : ObjectId("544b7e44fd9270c1492f5834"), "field" : "address" },
    { "doc" : ObjectId("544b9811fd9270c1492f5835"), "field" : "address" },
    { "doc" : ObjectId("544bb320fd9270c1492f583c"), "field" : "address" }
  ],
  "score" : 3
}

Skor 5 untuk Sushi berasal dari fakta bahwa kata Sushi muncul dua kali di kolom catatan salah satu dokumen. Ini adalah perilaku yang dimaksudkan.

Meskipun ini mungkin solusi orang miskin, perlu dioptimalkan untuk banyak sekali kasus penggunaan yang masuk akal dan akan membutuhkan mapReduce tambahan untuk diimplementasikan agar setengah berguna di lingkungan produksi, ini berfungsi seperti yang diharapkan. h.

Sunting

Tentu saja, seseorang dapat menghapus $match panggung dan tambahkan $out tahap dalam fase agregasi untuk mendapatkan hasil yang diproses sebelumnya:

db.searchtst.aggregate(
  {
    $group:{
      _id:"$_id.word",
      occurences:{ $push:{doc:"$_id.doc",field:"$_id.field"}},
      score:{$sum:"$value"}
     }
   },{
     $out:"search"
   })

Sekarang, kita dapat mengkueri search yang dihasilkan koleksi untuk mempercepat. Pada dasarnya Anda memperdagangkan hasil waktu nyata untuk kecepatan.

Edit 2 :Jika pendekatan pra-pemrosesan diambil, searchtst kumpulan contoh harus dihapus setelah agregasi selesai untuk menghemat ruang disk dan – yang lebih penting – RAM yang berharga.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Spring dan MongoDB:SAXParseException saat membaca Definisi Kacang

  2. bagaimana cara melepaskan caching yang digunakan oleh Mongodb?

  3. MongoDB :Urutan indeks dan urutan kueri harus cocok?

  4. Cara membaca dari MongoDB

  5. Tidak dapat menanyakan mongoDB dengan luwak di node.js