Laravel弹性搜索未给出包含匹配项或类似匹配项

时间:2019-01-14 06:18:17

标签: php laravel elasticsearch laravel-5 laravel-scout

我想为所有的Laravel搜索查询实现elasticsearch。我使用brew安装了最新的Laravel和最新的elasticsearch。

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

我在这里使用driver 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_numI50263。因此,当我像搜索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 个答案:

答案 0 :(得分:1)

如我所见,您的ref_num字段是keyword类型。执行full-text queries(例如matchmatch_phrase)不会给您任何结果。对于keyword -s,您应该使用term level queries。也许prefix query在这里对您有所帮助。

示例

映射

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

结果:找到文档

如您所见,在示例中,我使用了这种子字段(rawref_num的子字段,但类型不同)。在Elasticsearch中,它称为fields,有关更多内容,您可以在documentation中阅读。

您可以使用bool query正确地将查询与其他任何字段上的其他查询一起使用。

如果您想在text类型字段上获得相同的结果,则必须正确准备索引。例如,您可以将自定义分析器与NGram tokenizer一起使用,该分析器将单词分成n-gram标记。

默认情况下,任何分析器都不会拆分单词,因此在您的情况下,索引中只有一个令牌:

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

结果:

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

对于全文搜索,Elasticsearch基于其在索引中的标记。如果令牌与搜索词中的令牌不匹配,则没有匹配项。