ElasticSearch ile bir kelimenin bir bölümünü nasıl arayabilirim?


128

Kısa süre önce ElasticSearch kullanmaya başladım ve bir kelimenin bir kısmını aramasını sağlayamıyorum.

Örnek: couchdb'den ElasticSearch'te indekslenmiş üç dokümanım var:

{
  "_id" : "1",
  "name" : "John Doeman",
  "function" : "Janitor"
}
{
  "_id" : "2",
  "name" : "Jane Doewoman",
  "function" : "Teacher"
}
{
  "_id" : "3",
  "name" : "Jimmy Jackal",
  "function" : "Student"
} 

Şimdi, "Doe" içeren tüm belgeleri aramak istiyorum

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

Bu herhangi bir isabet döndürmez. Ama ararsam

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

Bir belge (John Doeman) döndürür.

İndeksimin özellikleri olarak farklı çözümleyiciler ve farklı filtreler ayarlamayı denedim. Ayrıca tam gelişmiş bir sorgu kullanmayı denedim (örneğin:

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

) Ama hiçbir şey işe yaramıyor gibi görünüyor.

ElasticSearch'ün "Doe" kelimesini aradığımda hem John Doeman hem de Jane Doewoman'ı bulmasını nasıl sağlayabilirim?

GÜNCELLEME

Igor'un önerdiği gibi nGram jetonlaştırıcıyı ve filtreyi şu şekilde kullanmaya çalıştım:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "bulk_size": "100",
    "bulk_timeout": "10ms",
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "my_ngram_filter"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      }
    }
  }
}

Şu anda yaşadığım sorun, her sorgunun TÜM belgeleri döndürmesidir. Herhangi bir işaret var mı? NGram kullanımıyla ilgili ElasticSearch belgeleri harika değil ...


9
Merak etmeyin, min / maks ngram değeri 1 olarak ayarlanmış, yani 1 harf :)
Martin B.

Yanıtlar:


85

Ben de nGram kullanıyorum. Standart belirteç ve nGram'ı filtre olarak kullanıyorum. İşte kurulumum:

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "analysis": {
      "index_analyzer": {
        "my_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "mynGram"
          ]
        }
      },
      "search_analyzer": {
        "my_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "mynGram"
          ]
        }
      },
      "filter": {
        "mynGram": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 50
        }
      }
    }
  }
}

50 harfe kadar kelime parçalarını bulalım. Max_gram'ı ihtiyaç duyduğunuz şekilde ayarlayın. Almanca kelimelerde çok büyük olabilir, bu yüzden yüksek bir değere ayarlıyorum.



Dizinin ayarlarından elde ettiğiniz bu mu yoksa onu yapılandırmak için elasticsearch'e gönderdiğiniz şey bu mu?
Tomas Jansson

Elasticsearch'ü yapılandırmak için bir POST.
roka

Elasticsearch'ün güncel sürümlerine sıkı sıkıya bağlı değilim, ancak belgelerde bundan bahsetmeliyim: elastik.co/guide/en/elasticsearch/reference/current/index.html
roka

1
@JimC ElasticSearch'ü en az 7 yıldır kullanmıyorum, bu yüzden projenin mevcut değişikliklerini bilmiyorum.
roka

63

Baştaki ve sondaki joker karakterlerle arama, büyük bir dizinde son derece yavaş olacaktır. Kelime önekine göre arama yapabilmek istiyorsanız, baştaki joker karakteri kaldırın. Eğer gerçekten bir kelimenin ortasında bir alt dize bulmanız gerekiyorsa, ngram belirteç kullanmanız daha iyi olur.


14
Igor haklı. En azından baştaki * işaretini kaldırın. NGram ElasticSearch örneği için, şu ana bakın: gist.github.com/988923
karmi

3
@karmi: Tam örneğiniz için teşekkürler! Belki de yorumunuzu gerçek bir cevap olarak eklemek istersiniz, benim için işe yarayan şey ve oy vermek isteyeceğim şey budur.
Fabian Steeg

54

Herhangi bir eşlemeyi değiştirmeye gerek olmadığını düşünüyorum. Query_string kullanmayı deneyin , bu mükemmel. Tüm senaryolar varsayılan standart analizör ile çalışacaktır:

Verilerimiz var:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Senaryo 1:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }

Tepki:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Senaryo 2:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }

Tepki:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}

Senaryo 3:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }

Tepki:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

DÜZENLEME - Yay verileri elastik arama ile aynı uygulama https://stackoverflow.com/a/43579948/2357869

Query_string'in diğerlerinden nasıl daha iyi olduğu bir açıklama daha https://stackoverflow.com/a/43321606/2357869


3
bence bu en kolayı
Esgi Dendyanri

Evet . Projemde uyguladım.
Opster Elasticsearch Pro-Vijay

Aramak için birden çok alan nasıl dahil edilir?
Shubham A.

şunu deneyin: - {"sorgu": {"sorgu_dizesi": {"alanlar": ["içerik", "ad"], "sorgu": "bu VE şu"}}}
Opster Elasticsearch Pro-Vijay



6

Çözümü şu adresten deneyin: ElasticSearch'te Tam Alt Dize Aramaları

{
    "mappings": {
        "my_type": {
            "index_analyzer":"index_ngram",
            "search_analyzer":"search_ngram"
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "ngram_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            },
            "analyzer": {
                "index_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [ "ngram_filter", "lowercase" ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": "lowercase"
                }
            }
        }
    }
}

Disk kullanım problemini ve çok uzun arama terimi problemini çözmek için kısa 8 karakter uzunluğunda ngramlar kullanılır (şu şekilde yapılandırılmıştır: "max_gram": 8 ). 8 karakterden fazla terimleri aramak için, aramanızı o dizedeki her farklı 8 karakterli alt dizeyi arayan bir boole VE sorgusuna çevirin. Örneğin, bir kullanıcı geniş bahçe (10 karakterlik bir dizi) aradıysa, arama şöyle olacaktır:

"arge ya VE arge yar AND rge yard .


2
ölü bağlantı, pls düzelt
DarkMukke

Bir süredir böyle bir şey arıyordum. Teşekkür ederim! Birlikte nasıl hafıza ölçekler biliyor musunuz min_gramve max_grambu alan değerlerinin büyüklüğü ve aralığı doğrusal bağlıdır olacak gibi görünüyor minve max. Böyle bir şey kullanmak nasıl kaşlarını çattı?
Glen Thompson

Ayrıca, ngrambir belirteç üzerinde filtre olmasının herhangi bir nedeni var mı? bunu yalnızca bir jetonlaştırıcı olarak alıp daha sonra küçük harfli bir filtre uygulayamaz mıydınız ... index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }Bunu denedim ve aynı sonuçları analizör test api'sini kullanarak veriyor gibi görünüyor
Glen Thompson

2

Otomatik tamamlama işlevini uygulamak istiyorsanız, Completion Suggester en temiz çözümdür. Bir sonraki blog yazısı , bunun nasıl çalıştığını çok net bir açıklama içeriyor.

İki kelimeyle, geçerli öneriler içeren ve hızlı erişim ve bellek kullanımı için optimize edilmiş FST adı verilen bir bellek içi veri yapısıdır. Esasen, bu sadece bir grafiktir. Kelimeleri içeren örnek ve FST için hotel, marriot, mercure, munchenve munichbu şekilde görünecektir:

görüntü açıklamasını buraya girin


2

regexp kullanabilirsiniz.

{ "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"}
{ "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher"  }
{ "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student"  } 

Bu sorguyu kullanırsanız:

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

adlarının "J" ile başladığı tüm verileri size vereceksiniz. Adlarının "man" ile bittiği ilk iki kaydı almak istediğinizi düşünün, böylece bu sorguyu kullanabilirsiniz:

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

ve adında "m" bulunan tüm kayıtları almak istiyorsanız, şu sorguyu kullanabilirsiniz:

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

Bu benim için çalışıyor ve umarım cevabım probleminizi çözmek için uygun olur.


1

Wilcard (*) kullanmak puanın hesaplanmasını önler


1
Cevabınıza daha fazla ayrıntı ekleyebilir misiniz? Bunun ne işe yaradığına ilişkin örnek bir kod veya belgelere başvuru sağlayın.
Cray

0

Bunu kullanıyorum ve çalıştım

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

-6

Boşver.

Lucene belgelerine bakmam gerekiyordu. Joker karakter kullanabiliyorum gibi görünüyor! :-)

curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*

hile yapar!


11
@İmotov cevabına bakın. Joker karakterlerin kullanımı hiç de iyi ölçeklenmeyecek.
Mike Munroe

5
@Idx - Kendi cevabınızın nasıl düşürüldüğünü görün. Olumsuz oylar, bir yanıtın ne kadar kaliteli ve alaka düzeyini gösterir. Doğru cevabı kabul etmek için bir dakikanızı ayırabilir misiniz? En azından yeni kullanıcılar size minnettar olacaktır.
asyncwait

3
Yeterince olumsuz oy. OP şimdi en iyi cevabın ne olduğunu netleştirdi. Birisi daha iyi bir yanıt göndermeden önce en iyi yanıt gibi görünen şeyi paylaşmak için +1.
s.Daniel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.