Reputation: 23483
I wanted to have elasticsearch implemented for all my laravel search queries. I have the latest Laravel and latest elasticsearch installed using brew.
curl http://localhost:9200/
gives,
{
"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"
}
Here I am using the driver babenkoivan/scout-elasticsearch-driver
.
Model,
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
]
]
]
];
}
}
Configurator,
namespace App;
use ScoutElastic\IndexConfigurator;
use ScoutElastic\Migratable;
class CustomerIndexConfigurator extends IndexConfigurator
{
use Migratable;
/**
* @var array
*/
protected $settings = [
//
];
}
I have a record with ref_num
as I50263
. So I should get this record when I search I50
same like like query
. I tried all the below search but I am getting the result only with the complete word 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
Tried field type as text
also.
Upvotes: 0
Views: 2676
Reputation: 4535
As I see, your ref_num
field is of keyword
type. Performing full-text queries (like match
or match_phrase
) does not give you any results. For keyword
-s you should use term level queries. Perhaps prefix query would be helpful for you here.
PUT /so54176561
{
"mappings": {
"_doc": {
"properties": {
"ref_num": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
}
POST /so54176561/_doc/1
{
"ref_num": "I50263"
}
match
search on text
type fieldPOST /so54176561/_search
{
"query": {
"match": {
"ref_num": "I50263"
}
}
}
Result: document found
POST /so54176561/_search
{
"query": {
"match": {
"ref_num": "I50"
}
}
}
Result: document not found
prefix
search on keyword
type fieldPOST /so54176561/_search
{
"query": {
"prefix": {
"ref_num.raw": "I50"
}
}
}
Result: document found
As you can see, in the example I used so kind of subfields (raw
is a subfield of ref_num
but with a different type). In Elasticsearch it is called fields
and more about that you can read in the documentation.
You can simply use the query with any other query on any other field using bool query properly.
If you would like to achieve the same result on the text
type field you have to prepare your index properly. For example, you could use your custom analyzer with an NGram tokenizer which splits the words into n-gram tokens.
By default, any of the analyzers do not split words so in your case you had only one token in the index:
POST /_analyze
{
"analyzer": "standard",
"text": "I50263"
}
Result:
{
"tokens": [
{
"token": "i50263",
"start_offset": 0,
"end_offset": 6,
"type": "<ALPHANUM>",
"position": 0
}
]
}
For full-text search, Elasticsearch is basing on the tokens it has in the index. If tokens do not match the tokens from the search term, then there is no match.
Upvotes: 1