Як шукати частину слова за допомогою ElasticSearch


128

Нещодавно я почав використовувати ElasticSearch і, здається, не можу змусити його шукати частину слова.

Приклад: у мене є три документи з мого couchdb, індексованого в ElasticSearch:

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

Тому зараз я хочу шукати всі документи, що містять "Doe"

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

Це не повертає жодного звернення. Але якщо я шукаю

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

Він повертає один документ (Джон Доман).

Я спробував встановити різні аналізатори та різні фільтри як властивості мого індексу. Я також спробував використати повноцінний запит (наприклад:

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

) Але, здається, нічого не працює.

Як я можу змусити ElasticSearch знайти Джона Домана і Джейн Доевоман, коли я шукаю "Doe"?

ОНОВЛЕННЯ

Я намагався використовувати токенізатор і фільтр nGram, як запропонував Ігор:

{
  "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
        }
      }
    }
  }
}

Зараз у мене проблема полягає в тому, що кожен запит повертає ВСІ документи. Якісь покажчики? Документація ElasticSearch щодо використання nGram не велика ...


9
недарма, ви маєте min / max ngram на 1, тому 1 лист :)
Мартін Б.

Відповіді:


85

Я також використовую nGram. Я використовую стандартний токенізатор і nGram просто як фільтр. Ось моя настройка:

{
  "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 літер. Налаштуйте max_gram, як вам потрібно. Німецькою мовою слова можуть стати справді великими, тому я ставлю це на високе значення.



Це те, що ви отримуєте з налаштувань індексу або це те, що ви публікуєте в еластичному пошуку, щоб налаштувати його?
Томаш Янссон

Це POST для налаштування Elasticsearch.
рока

Я не впевнений у поточних версіях Elasticsearch, але мушу згадати про це в документах: elastic.co/guide/en/elasticsearch/reference/current/index.html
roka

1
@JimC Я не використовував ElasticSearch принаймні 7 років, тому я не знаю поточних змін проекту.
рока

63

Пошук за допомогою провідних та останніх макіяжів буде вельми повільним за великим показником. Якщо ви хочете мати можливість пошуку за префіксом слова, видаліть провідну підстановку. Якщо вам дійсно потрібно знайти підрядку в середині слова, вам краще використовувати ngram токенізатор.


14
Ігор має рацію. Принаймні видаліть провідний *. Для прикладу NGram ElasticSearch див. Цю історію
karmi

3
@karmi: Дякую за ваш повний приклад! Можливо, ви хочете додати свій коментар як фактичну відповідь, це те, що на мене працює, і що я хотів би підтримати.
Фабіан Стіг

54

Я думаю, не потрібно змінювати жодне відображення. Спробуйте використовувати query_string , це ідеально. Усі сценарії працюватимуть із стандартним аналізатором за замовчуванням:

У нас є дані:

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

Сценарій 1:

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

Відповідь:

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

Сценарій 2:

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

Відповідь:

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

Сценарій 3:

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

Відповідь:

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

EDIT - однакова реалізація з пружинними даними еластичного пошуку https://stackoverflow.com/a/43579948/2357869

Ще одне пояснення того, як query_string кращий за інші https://stackoverflow.com/a/43321606/2357869


3
Я думаю, що це найпростіше
Есгі Денд’янрі

Так . Я реалізував у своєму проекті.
Opster Elasticsearch Pro-Vijay

Як включити кілька полів для пошуку?
Шубхам А.

спробуйте це: - {"query": {"query_string": {"polja": ["зміст", "ім'я"], "запит": "це ТА
ТО


14

не змінюючи відображення індексів, ви можете виконати простий запит префікса, який виконає частковий пошук, як ви сподіваєтесь

тобто.

{
  "query": { 
    "prefix" : { "name" : "Doe" }
  }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html


чи можна здійснити пошук у кількох полях за допомогою префікса?
batmaci

Дякую, саме те, що я шукав! Будь-які думки щодо продуктивності?
Vingtoft

6

Спробуйте рішення, описане тут: Точні пошукові підрядки в ElasticSearch

{
    "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"
                }
            }
        }
    }
}

Для того, щоб вирішити проблему використання дискового простору і занадто довгий термін пошуку проблему короткий довгі 8 символів ngrams використовуються (конфігурація «max_gram»: 8 ). Щоб шукати терміни з більш ніж 8 символами, перетворіть свій пошук у логічний І запит, шукаючи кожну окрему 8-символьну підрядку в цьому рядку. Наприклад, якщо користувач здійснив пошук у великому дворі (10-символьна рядок), пошук буде таким:

"arge ya І arge yar AND rge двору .


2
мертве посилання, pls fix
DarkMukke

Я шукав щось подібне деякий час. Дякую! Чи знаєте ви, як масштабується пам'ять із значенням min_gramта, max_gramсхоже, це буде лінійно залежно від розміру значень поля та діапазону minта max. Як нахмурився використовувати щось подібне?
Глен Томпсон,

Чи є якась причина, що ngramце фільтр над токенізатором? ви могли б не просто мати його як токенізатор, а потім застосувати фільтр малих літер ... index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }Я спробував це, і, здається, дав ті самі результати, використовуючи тест-аналізатор api
Глен Томпсон,

2

Якщо ви хочете реалізувати функцію автозаповнення, то програму завершення пропозицій є найбільш акуратним рішенням. Наступна публікація в блозі містить дуже чіткий опис того, як це працює.

У двох словах, це структура даних в пам'яті, яка називається FST, яка містить дійсні пропозиції та оптимізована для швидкого пошуку та використання пам'яті. По суті, це просто графік. Наприклад, і FST , що містить слово hotel, marriot, mercure, munchenі munichбуде виглядати наступним чином :

введіть тут опис зображення


2

ви можете використовувати regexp.

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

якщо ви використовуєте цей запит:

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

Ви отримаєте всі дані, що їх ім’я починається з "J". Подумайте, що ви хочете отримати лише перші два записи, що їх ім'я закінчується на "man", щоб ви могли використовувати цей запит:

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

і якщо ви хочете отримати весь запис про те, що в їх імені існує "m", ви можете використовувати цей запит:

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

Це працює для мене. І я сподіваюся, що моя відповідь буде придатною для вирішення вашої проблеми.


1

Використання wilcards (*) запобігає вирахуванню рахунку


1
Чи можете ви додати більше деталей у відповідь? Надайте зразок коду або посилання на документацію про те, що це робить.
Cray

0

Я використовую це, і я працював

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

-6

Не звертай уваги.

Довелося подивитися документацію на люцені. Здається, я можу використовувати символи! :-)

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

робить трюк!


11
Дивіться відповідь @imotov. Використання подвійних карт взагалі не має масштабів.
Майк Манро

5
@Idx - Подивіться, як ваша власна відповідь викликає заперечення. Downvotes представляє якість і відповідність відповіді. Ви можете витратити хвилину, щоб прийняти правильну відповідь? Принаймні нові користувачі будуть вам вдячні.
asyncwait

3
Досить низових подій. ОП уточнила, яка найкраща відповідь зараз. +1, щоб поділитися тим, що здавалося найкращою відповіддю, перш ніж хтось опублікував кращу.
с.Даніель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.