Laravel упругого поиска не дает содержит или как матч

Я хотел, чтобы эластичный поиск был реализован для всех моих поисковых запросов laravel. У меня установлен новейший Laravel и новейший эластичный поиск с использованием brew.

curl http://localhost:9200/ дает,

{
  "name" : "_SFvSGk",
  "cluster_name" : "elasticsearch_an398690",
  "cluster_uuid" : "xBi3aTDaTkmA6dtzhpOrwg",
  "version" : {
    "number" : "6.5.4",
    "build_flavor" : "oss",
    "build_type" : "tar",
    "build_hash" : "d2ef93d",
    "build_date" : "2018-12-17T21:17:40.758843Z",
    "build_snapshot" : false,
    "lucene_version" : "7.5.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

Здесь я использую драйвер babenkoivan/scout-elasticsearch-driver,

Модель,

namespace App;

use ScoutElastic\Searchable;
use Illuminate\Database\Eloquent\Model;

class Customer extends Model
{
    use Searchable;

    /**
     * @var string
     */
    protected $indexConfigurator = CustomerIndexConfigurator::class;

    /**
     * @var array
     */
    protected $searchRules = [
        CustomerSearchRule::class
    ];

    /**
     * @var array
     */
    protected $mapping = [
        'properties' => [
            'text' => [
                'type' => 'text',
                'fields' => [
                    'ref_num' => [
                        'type' => 'keyword',
                    ]
                ]
            ],
        ]
    ];
}

SearchRule,

namespace App;

use ScoutElastic\SearchRule;

class CustomerSearchRule extends SearchRule
{
    /**
     * @inheritdoc
     */
    public function buildHighlightPayload()
    {
        return [
            'fields' => [
                'ref_num' => [
                    'type' => 'plain'
                ]
            ]
        ];
    }

    /**
     * @inheritdoc
     */
    public function buildQueryPayload()
    {
        $query = $this->builder->query;

        return [
                [
                    'match' => [
                        'ref_num' => [
                            'query' => $query,
                            'boost' => 2
                        ]
                    ]
                ]
        ];
    }
}

Конфигуратор,

namespace App;

use ScoutElastic\IndexConfigurator;
use ScoutElastic\Migratable;

class CustomerIndexConfigurator extends IndexConfigurator
{
    use Migratable;

    /**
     * @var array
     */
    protected $settings = [
        //
    ];
}

У меня есть запись с ref_num как I50263, Так что я должен получить эту запись, когда я ищу I50 такой же как like query, Я перепробовал весь приведенный ниже поиск, но получаю результат только с полным словом I50263,

return Customer::search('I50')->get();
// no record
return Customer::search('I50263')->get();
// got record
return Customer::searchRaw([
  'query' => [
     'bool' => [
        'must' => [
            'match' => [
                'ref_num' => 'I502'
            ]
        ]
     ]
  ]
]);
// no record
return Customer::searchRaw([
  'query' => [
      'bool' => [
          'must' => [
             "match_phrase" => [
                "ref_num" => [
                   "query" => "I50",
                   "boost" => 1
                 ]
              ]
           ]
        ]
     ]
 ]);
 // no record

Тип пробного поля как text также.

1 ответ

Как я вижу, твой ref_num поле имеет keyword тип. Выполнение полнотекстовых запросов (например, match или же match_phrase) не дает никаких результатов. За keyword-S вы должны использовать запросы уровня термина. Возможно, префиксный запрос будет вам полезен здесь.

пример

картографирование

PUT /so54176561
{
  "mappings": {
    "_doc": {
      "properties": {
        "ref_num": {
          "type": "text",
          "fields": {
            "raw": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

Добавление образца документа

POST /so54176561/_doc/1
{
  "ref_num": "I50263"
}

Полный текст match Поиск по text поле типа

по всей стоимости

POST /so54176561/_search
{
  "query": {
    "match": {
      "ref_num": "I50263"
    }
  }
}

Результат: документ найден

по префиксу значения

POST /so54176561/_search
{
  "query": {
    "match": {
      "ref_num": "I50"
    }
  }
}

Результат: документ не найден

Срок уровня prefix Поиск по keyword поле типа

POST /so54176561/_search
{
  "query": {
    "prefix": {
      "ref_num.raw": "I50"
    }
  }
}

Результат: документ найден

Как видите, в примере я использовал такие подполя (raw это подполе ref_num но с другим типом). В Elasticsearch это называется fields и больше об этом вы можете прочитать в документации.

Вы можете просто использовать запрос с любым другим запросом в любом другом поле, используя правильный запрос bool.

Если вы хотите достичь того же результата на text введите поле вы должны правильно подготовить свой индекс. Например, вы можете использовать свой собственный анализатор с токенайзером NGram, который разбивает слова на токены n-граммы.

По умолчанию любой из анализаторов не разделяет слова, поэтому в вашем случае в индексе был только один токен:

POST /_analyze
{
  "analyzer": "standard",
  "text": "I50263"
}

Результат:

{
  "tokens": [
    {
      "token": "i50263",
      "start_offset": 0,
      "end_offset": 6,
      "type": "<ALPHANUM>",
      "position": 0
    }
  ]
}

Для полнотекстового поиска Elasticsearch базируется на токенах, которые он имеет в индексе. Если токены не совпадают с токенами из условия поиска, совпадение отсутствует.

Другие вопросы по тегам