Смешивание точного поиска со стеммингом
При разработке поискового приложения стемминг часто необходим, так как желательно, чтобы запрос на skiing соответствовал документам, содержащим ski или skis. Но что делать, если пользователь хочет найти skiing конкретно? Обычно для этого используется многопольное поле, чтобы один и тот же контент индексировался двумя разными способами:
resp = client.indices.create(
index="index",
settings={
"analysis": {
"analyzer": {
"english_exact": {
"tokenizer": "standard",
"filter": [
"lowercase"
]
}
}
}
},
mappings={
"properties": {
"body": {
"type": "text",
"analyzer": "english",
"fields": {
"exact": {
"type": "text",
"analyzer": "english_exact"
}
}
}
}
},
)
print(resp)
resp1 = client.index(
index="index",
id="1",
document={
"body": "Ski resort"
},
)
print(resp1)
resp2 = client.index(
index="index",
id="2",
document={
"body": "A pair of skis"
},
)
print(resp2)
resp3 = client.indices.refresh(
index="index",
)
print(resp3) response = client.indices.create(
index: 'index',
body: {
settings: {
analysis: {
analyzer: {
english_exact: {
tokenizer: 'standard',
filter: [
'lowercase'
]
}
}
}
},
mappings: {
properties: {
body: {
type: 'text',
analyzer: 'english',
fields: {
exact: {
type: 'text',
analyzer: 'english_exact'
}
}
}
}
}
}
)
puts response
response = client.index(
index: 'index',
id: 1,
body: {
body: 'Ski resort'
}
)
puts response
response = client.index(
index: 'index',
id: 2,
body: {
body: 'A pair of skis'
}
)
puts response
response = client.indices.refresh(
index: 'index'
)
puts response const response = await client.indices.create({
index: "index",
settings: {
analysis: {
analyzer: {
english_exact: {
tokenizer: "standard",
filter: ["lowercase"],
},
},
},
},
mappings: {
properties: {
body: {
type: "text",
analyzer: "english",
fields: {
exact: {
type: "text",
analyzer: "english_exact",
},
},
},
},
},
});
console.log(response);
const response1 = await client.index({
index: "index",
id: 1,
document: {
body: "Ski resort",
},
});
console.log(response1);
const response2 = await client.index({
index: "index",
id: 2,
document: {
body: "A pair of skis",
},
});
console.log(response2);
const response3 = await client.indices.refresh({
index: "index",
});
console.log(response3); PUT index
{
"settings": {
"analysis": {
"analyzer": {
"english_exact": {
"tokenizer": "standard",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"properties": {
"body": {
"type": "text",
"analyzer": "english",
"fields": {
"exact": {
"type": "text",
"analyzer": "english_exact"
}
}
}
}
}
}
PUT index/_doc/1
{
"body": "Ski resort"
}
PUT index/_doc/2
{
"body": "A pair of skis"
}
POST index/_refresh При такой настройке поиск по ski в body вернёт оба документа:
resp = client.search(
index="index",
query={
"simple_query_string": {
"fields": [
"body"
],
"query": "ski"
}
},
)
print(resp) response = client.search(
index: 'index',
body: {
query: {
simple_query_string: {
fields: [
'body'
],
query: 'ski'
}
}
}
)
puts response const response = await client.search({
index: "index",
query: {
simple_query_string: {
fields: ["body"],
query: "ski",
},
},
});
console.log(response); GET index/_search
{
"query": {
"simple_query_string": {
"fields": [ "body" ],
"query": "ski"
}
}
} {
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 2,
"relation": "eq"
},
"max_score": 0.18232156,
"hits": [
{
"_index": "index",
"_id": "1",
"_score": 0.18232156,
"_source": {
"body": "Ski resort"
}
},
{
"_index": "index",
"_id": "2",
"_score": 0.18232156,
"_source": {
"body": "A pair of skis"
}
}
]
}
} С другой стороны, поиск по ski в body.exact вернёт только документ 1, так как цепочка анализа body.exact не выполняет стемминг.
resp = client.search(
index="index",
query={
"simple_query_string": {
"fields": [
"body.exact"
],
"query": "ski"
}
},
)
print(resp) response = client.search(
index: 'index',
body: {
query: {
simple_query_string: {
fields: [
'body.exact'
],
query: 'ski'
}
}
}
)
puts response const response = await client.search({
index: "index",
query: {
simple_query_string: {
fields: ["body.exact"],
query: "ski",
},
},
});
console.log(response); GET index/_search
{
"query": {
"simple_query_string": {
"fields": [ "body.exact" ],
"query": "ski"
}
}
} {
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.8025915,
"hits": [
{
"_index": "index",
"_id": "1",
"_score": 0.8025915,
"_source": {
"body": "Ski resort"
}
}
]
}
} Это не так просто для реализации для конечных пользователей, так как нам нужно определить, ищут ли они точное совпадение или нет и перенаправить их соответственно на нужное поле. А что делать, если только части запроса должны точно совпадать, а другие части должны учитывать стемминг?
К счастью, запросы query_string и simple_query_string обладают функцией, которая решает эту проблему: quote_field_suffix. Это сообщает Elasticsearch, что слова, заключённые в кавычки, должны быть перенаправлены на другое поле, см. ниже:
resp = client.search(
index="index",
query={
"simple_query_string": {
"fields": [
"body"
],
"quote_field_suffix": ".exact",
"query": "\"ski\""
}
},
)
print(resp) response = client.search(
index: 'index',
body: {
query: {
simple_query_string: {
fields: [
'body'
],
quote_field_suffix: '.exact',
query: '"ski"'
}
}
}
)
puts response const response = await client.search({
index: "index",
query: {
simple_query_string: {
fields: ["body"],
quote_field_suffix: ".exact",
query: '"ski"',
},
},
});
console.log(response); GET index/_search
{
"query": {
"simple_query_string": {
"fields": [ "body" ],
"quote_field_suffix": ".exact",
"query": "\"ski\""
}
}
} {
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped" : 0,
"failed": 0
},
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.8025915,
"hits": [
{
"_index": "index",
"_id": "1",
"_score": 0.8025915,
"_source": {
"body": "Ski resort"
}
}
]
}
} В данном случае, так как ski было заключено в кавычки, оно искалось в поле body.exact благодаря параметру quote_field_suffix, поэтому совпал только документ 1. Это позволяет пользователям смешивать точный поиск со стеммингом по своему усмотрению.
Если указанное в quote_field_suffix поле не существует, поиск вернётся к использованию поля по умолчанию для строкового запроса.
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/mixing-exact-search-with-stemming.html