Не ищет в индексе по половине слова

Добрый день!
Вопрос возник. В документе есть записи в поле title с значениями "Балтика №0", "Балтика №1" и тд
Если подать на поиск слово "балтик" - то находит записи, а если, например, "балти" - то ничего не находит. Можете подсказать в чем проблема? Вот параметры создания документа:

    $params = [
        'index' => $this->index,
        'body' => [
            'settings' => [
                'analysis' => [
                    'tokenizer' => 'standard',
                    'filter' => [
                        'ru_stop' => [
                            'type' => 'stop',
                            'stopwords' => '_russian_'
                        ],
                        'ru_stemmer' => [
                            'type' => 'stemmer',
                            'language' => 'russian'
                        ],
                        'possessive_english' => [
                            'type' => 'stemmer',
                            'language' => 'possessive_english'
                        ],
                        'english' => [
                            'type' => 'stemmer',
                            'language' => 'english'
                        ],
                    ],
                    'analyzer' => [
                        'default' => [
                            'tokenizer' => 'standard',
                            'filter' => ['lowercase', 'ru_stop', 'ru_stemmer', 'possessive_english', 'english']
                        ]
                    ]
                ]
            ],
        ]
    ];

А ищу слово так:

$paramSearch['body']['query']['match']['title'] = 'балти';

Заранее спасибо всем, кто сможет помочь!

Давайте посмотрим какие токены получаются в каждом случае. Запускаем анализатор на документе:

GET _analyze
{
  "tokenizer": "standard",
  "filter": [
    "lowercase",
    {
      "type": "stop",
      "stopwords": "_russian_"
    },
    {
      "type": "stemmer",
      "language": "russian"
    },
    {
      "type": "stemmer",
      "language": "possessive_english"
    },
    {
      "type": "stemmer",
      "language": "english"
    }
  ],
  "text": "Балтика №0, Балтика №1"
}

Получаем токены балтик, 0, балтик, 1. Теперь запускаем анализатор на вашем запросе, получаем балт. Токен из вашего запроса не совпадает ни с одним токеном из вашего документа, и как результат вы ничего не находите.

а как то можно сделать что бы находилось? неужели es не может того, что может простой MySQL LIKE ?

Может, только вам надо решить, что вам нужно - простой MySQL LIKE или стемминг. Или вы будете искать и так и так.

и так и так нужно :slight_smile: Можете сказать, как это сделать можно?

Если главное - скорость поиска, то можно проиндексировать с помощью N-gram фильтра:

DELETE test_index
PUT test_index
{
  "settings": {
    "analysis": {
      "filter": {
        "ru_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "ru_stem": {
          "type": "stemmer",
          "language": "russian"
        },
        "en_stem": {
          "type": "stemmer",
          "language": "english"
        },
        "en_pos": {
          "type": "stemmer",
          "language": "possessive_english"
        },
        "ngram": {
          "type": "edgeNGram",
          "min_gram": 1,
          "max_gram": 20
        }
      }, 
      "analyzer": {
        "my_stemmer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "ru_stop", "ru_stem", "en_pos"]
        },
        "my_ngram_index": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "ru_stop", "ngram"]
        },
        "my_ngram_search": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase"]
        }
      }
    }
  },
  "mappings": {
    "doc": {
      "properties": {
        "text": {
          "type": "text",
          "analyzer": "my_stemmer",
          "fields": {
            "ngrams": {
              "type": "text",
              "analyzer": "my_ngram_index",
              "search_analyzer": "my_ngram_search"
            }
          }
        }
      }
    }
  }
}

POST /test_index/doc/_bulk?refresh
{ "index" : { "_id" : "1" } }
{ "text" : "Балтика №0, Балтика №1" }
{ "index" : { "_id" : "2" } }
{ "text" : "Lion's Head или Lions" }

GET test_index/doc/_search
{
  "query": {
    "multi_match": {
      "query": "Балтика",
      "fields": ["text", "text.ngrams"]
    }
  }
}

GET test_index/doc/_search
{
  "query": {
    "multi_match": {
      "query": "Балтикой",
      "fields": ["text", "text.ngrams"]
    }
  }
}

GET test_index/doc/_search
{
  "query": {
    "multi_match": {
      "query": "Бал",
      "fields": ["text", "text.ngrams"]
    }
  }
}

GET test_index/doc/_search
{
  "query": {
    "multi_match": {
      "query": "lion",
      "fields": ["text", "text.ngrams"]
    }
  }
}

Если заботить размер индекса и не волнует скорость поиска или требуемая память - то можно искать с помощью match_phrase_prefix или prefix.

DELETE test_index
PUT test_index
{
  "settings": {
    "analysis": {
      "filter": {
        "ru_stop": {
          "type": "stop",
          "stopwords": "_russian_"
        },
        "ru_stem": {
          "type": "stemmer",
          "language": "russian"
        },
        "en_stem": {
          "type": "stemmer",
          "language": "english"
        },
        "en_pos": {
          "type": "stemmer",
          "language": "possessive_english"
        }
      }, 
      "analyzer": {
        "my_stemmer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "ru_stop", "ru_stem", "en_pos"]
        }
      }
    }
  },
  "mappings": {
    "doc": {
      "properties": {
        "text": {
          "type": "text",
          "analyzer": "my_stemmer",
          "fields": {
            "standard": {
              "type": "text",
              "analyzer": "standard"
            }
          }
        }
      }
    }
  }
}

POST /test_index/doc/_bulk?refresh
{ "index" : { "_id" : "1" } }
{ "text" : "Балтика №0, Балтика №1" }
{ "index" : { "_id" : "2" } }
{ "text" : "Lion's Head или Lions" }

GET test_index/doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "text": "Бал"
          }
        },
        {
          "match_phrase_prefix": {
            "text.standard": "Бал"
          }
        }
      ]
    }
  }
}
1 Like

Спасибо, Вам огромное!

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.