Spec-Zone.ru › Elasticsearch 8
›Руководство по Elasticsearch [8.17] ›Язык запросов DSL ›Полные текстовые запросы

Запрос multi-match

Запрос multi_match основан на match запросе для выполнения запросов по нескольким полям:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'this is a test',
                'fields' => [
                    'subject',
                    'message',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "this is a test",
            "fields": [
                "subject",
                "message"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'this is a test',
        fields: [
          'subject',
          'message'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "this is a test",
	      "fields": [
	        "subject",
	        "message"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "this is a test",
      fields: ["subject", "message"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "this is a test", 
      "fields": [ "subject", "message" ] 
    }
  }
}

Строка запроса.

Поля для поиска.

fields и усиление по полям

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

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'Will Smith',
                'fields' => [
                    'title',
                    '*_name',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "Will Smith",
            "fields": [
                "title",
                "*_name"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'Will Smith',
        fields: [
          'title',
          '*_name'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "Will Smith",
	      "fields": [
	        "title",
	        "*_name"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "Will Smith",
      fields: ["title", "*_name"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":    "Will Smith",
      "fields": [ "title", "*_name" ] 
    }
  }
}

Поиск по полям title, first_name и last_name.

Отдельные поля могут быть усилены с помощью обозначения с возведением в степень (^):

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'this is a test',
                'fields' => [
                    'subject^3',
                    'message',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "this is a test",
            "fields": [
                "subject^3",
                "message"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'this is a test',
        fields: [
          'subject^3',
          'message'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "this is a test",
	      "fields": [
	        "subject^3",
	        "message"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "this is a test",
      fields: ["subject^3", "message"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query" : "this is a test",
      "fields" : [ "subject^3", "message" ] 
    }
  }
}

Запрос умножает рейтинг поля subject на три, но оставляет рейтинг поля message неизменным.

Если fields не указаны, запрос multi_match по умолчанию использует параметры индекса index.query.default_field, которые, в свою очередь, по умолчанию установлены в *. * извлекает все поля в схеме, подходящие для запросов по терминам и фильтрует метаданные. Затем все извлечённые поля объединяются для построения запроса.

Предел числа полей

По умолчанию существует ограничение на количество условий в запросе. Это ограничение определяется параметром indices.query.bool.max_clause_count, который по умолчанию равен 4096. Для запросов multi-match количество условий вычисляется как произведение количества полей и количества терминов.

Типы запроса multi_match:

Способ выполнения запроса multi_match зависит от параметра type, который может быть:

best_fields

(По умолчанию) Находит документы, которые соответствуют любому полю, но использует _score из лучшего поля. См. best_fields.

most_fields

Находит документы, которые соответствуют любому полю, и объединяет _score из каждого поля. См. most_fields.

cross_fields

Обращается к полям с одинаковым analyzer как к одному большому полю. Ищет каждое слово в любом поле. См. cross_fields.

phrase

Выполняет запрос match_phrase для каждого поля и использует _score из лучшего поля. См. phrase и phrase_prefix.

phrase_prefix

Выполняет запрос match_phrase_prefix для каждого поля и использует _score из лучшего поля. См. phrase и phrase_prefix.

bool_prefix

Создает запрос match_bool_prefix для каждого поля и объединяет _score из каждого поля. См. bool_prefix.

best_fields

Тип best_fields наиболее полезен при поиске нескольких слов, которые лучше всего находятся в одном поле. Например, «бурый лис» в одном поле имеет более ясный смысл, чем «бурый» в одном поле и «лис» в другом.

Тип best_fields генерирует запрос match для каждого поля и оборачивает их в запрос dis_max, чтобы найти лучшее соответствующее поле. Например, этот запрос:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'brown fox',
                'type' => 'best_fields',
                'fields' => [
                    'subject',
                    'message',
                ],
                'tie_breaker' => 0.3,
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "brown fox",
            "type": "best_fields",
            "fields": [
                "subject",
                "message"
            ],
            "tie_breaker": 0.3
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'brown fox',
        type: 'best_fields',
        fields: [
          'subject',
          'message'
        ],
        tie_breaker: 0.3
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "brown fox",
	      "type": "best_fields",
	      "fields": [
	        "subject",
	        "message"
	      ],
	      "tie_breaker": 0.3
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "brown fox",
      type: "best_fields",
      fields: ["subject", "message"],
      tie_breaker: 0.3,
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "brown fox",
      "type":       "best_fields",
      "fields":     [ "subject", "message" ],
      "tie_breaker": 0.3
    }
  }
}

будет выполнен как:

$params = [
    'body' => [
        'query' => [
            'dis_max' => [
                'queries' => [
                    [
                        'match' => [
                            'subject' => 'brown fox',
                        ],
                    ],
                    [
                        'match' => [
                            'message' => 'brown fox',
                        ],
                    ],
                ],
                'tie_breaker' => 0.3,
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "dis_max": {
            "queries": [
                {
                    "match": {
                        "subject": "brown fox"
                    }
                },
                {
                    "match": {
                        "message": "brown fox"
                    }
                }
            ],
            "tie_breaker": 0.3
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      dis_max: {
        queries: [
          {
            match: {
              subject: 'brown fox'
            }
          },
          {
            match: {
              message: 'brown fox'
            }
          }
        ],
        tie_breaker: 0.3
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "dis_max": {
	      "queries": [
	        {
	          "match": {
	            "subject": "brown fox"
	          }
	        },
	        {
	          "match": {
	            "message": "brown fox"
	          }
	        }
	      ],
	      "tie_breaker": 0.3
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    dis_max: {
      queries: [
        {
          match: {
            subject: "brown fox",
          },
        },
        {
          match: {
            message: "brown fox",
          },
        },
      ],
      tie_breaker: 0.3,
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match": { "subject": "brown fox" }},
        { "match": { "message": "brown fox" }}
      ],
      "tie_breaker": 0.3
    }
  }
}

Обычно тип best_fields использует рейтинг одного лучшего соответствующего поля, но если tie_breaker указан, то рейтинг рассчитывается следующим образом:

  • рейтинг лучшего соответствующего поля
  • плюс tie_breaker * _score для всех остальных соответствующих полей

Также принимает analyzer, boost, operator, minimum_should_match, fuzziness, lenient, prefix_length, max_expansions, fuzzy_rewrite, zero_terms_query, auto_generate_synonyms_phrase_query и fuzzy_transpositions, как описано в запросе match.

operator и minimum_should_match

Типы best_fields и most_fields являются ориентированными на поле — они генерируют запрос match для каждого поля. Это означает, что параметры operator и minimum_should_match применяются к каждому полю индивидуально, что, вероятно, не является желаемым результатом.

Рассмотрим этот пример запроса:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'Will Smith',
                'type' => 'best_fields',
                'fields' => [
                    'first_name',
                    'last_name',
                ],
                'operator' => 'and',
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "Will Smith",
            "type": "best_fields",
            "fields": [
                "first_name",
                "last_name"
            ],
            "operator": "and"
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'Will Smith',
        type: 'best_fields',
        fields: [
          'first_name',
          'last_name'
        ],
        operator: 'and'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "Will Smith",
	      "type": "best_fields",
	      "fields": [
	        "first_name",
	        "last_name"
	      ],
	      "operator": "and"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "Will Smith",
      type: "best_fields",
      fields: ["first_name", "last_name"],
      operator: "and",
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "best_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and" 
    }
  }
}

Все термины должны присутствовать.

Этот запрос выполняется как:

  (+first_name:will +first_name:smith)
| (+last_name:will  +last_name:smith)

Другими словами, все термины должны присутствовать в одном поле для соответствия документу.

Запрос combined_fields предлагает подход, ориентированный на термин, который обрабатывает operator и minimum_should_match на основе каждого термина. Другой режим multi-match cross_fields также решает эту проблему.

most_fields

Тип most_fields наиболее полезен при запросе к нескольким полям, содержащим один и тот же текст, проанализированный различными способами. Например, основное поле может содержать синонимы, стемминг и термины без диакритики. Второе поле может содержать исходные термины, а третье поле может содержать фрагменты. Объединив оценки из всех трёх полей, мы можем сопоставить как можно больше документов с основным полем, но использовать второе и третье поля для вывода наиболее похожих результатов в верхней части списка.

Этот запрос:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'quick brown fox',
                'type' => 'most_fields',
                'fields' => [
                    'title',
                    'title.original',
                    'title.shingles',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "quick brown fox",
            "type": "most_fields",
            "fields": [
                "title",
                "title.original",
                "title.shingles"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'quick brown fox',
        type: 'most_fields',
        fields: [
          'title',
          'title.original',
          'title.shingles'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "quick brown fox",
	      "type": "most_fields",
	      "fields": [
	        "title",
	        "title.original",
	        "title.shingles"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "quick brown fox",
      type: "most_fields",
      fields: ["title", "title.original", "title.shingles"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown fox",
      "type":       "most_fields",
      "fields":     [ "title", "title.original", "title.shingles" ]
    }
  }
}

будет выполнен как:

$params = [
    'body' => [
        'query' => [
            'bool' => [
                'should' => [
                    [
                        'match' => [
                            'title' => 'quick brown fox',
                        ],
                    ],
                    [
                        'match' => [
                            'title.original' => 'quick brown fox',
                        ],
                    ],
                    [
                        'match' => [
                            'title.shingles' => 'quick brown fox',
                        ],
                    ],
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "bool": {
            "should": [
                {
                    "match": {
                        "title": "quick brown fox"
                    }
                },
                {
                    "match": {
                        "title.original": "quick brown fox"
                    }
                },
                {
                    "match": {
                        "title.shingles": "quick brown fox"
                    }
                }
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      bool: {
        should: [
          {
            match: {
              title: 'quick brown fox'
            }
          },
          {
            match: {
              'title.original' => 'quick brown fox'
            }
          },
          {
            match: {
              'title.shingles' => 'quick brown fox'
            }
          }
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "bool": {
	      "should": [
	        {
	          "match": {
	            "title": "quick brown fox"
	          }
	        },
	        {
	          "match": {
	            "title.original": "quick brown fox"
	          }
	        },
	        {
	          "match": {
	            "title.shingles": "quick brown fox"
	          }
	        }
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    bool: {
      should: [
        {
          match: {
            title: "quick brown fox",
          },
        },
        {
          match: {
            "title.original": "quick brown fox",
          },
        },
        {
          match: {
            "title.shingles": "quick brown fox",
          },
        },
      ],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title":          "quick brown fox" }},
        { "match": { "title.original": "quick brown fox" }},
        { "match": { "title.shingles": "quick brown fox" }}
      ]
    }
  }
}

Сумма оценки от каждой из match клаузы складывается, как и в запросе bool.

Также поддерживает analyzer, boost, operator, minimum_should_match, fuzziness, lenient, prefix_length, max_expansions, fuzzy_rewrite и zero_terms_query.

phrase и phrase_prefix

Типы phrase и phrase_prefix ведут себя точно так же, как best_fields, но используют запрос типа match_phrase или match_phrase_prefix вместо запроса match.

Этот запрос:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'quick brown f',
                'type' => 'phrase_prefix',
                'fields' => [
                    'subject',
                    'message',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "quick brown f",
            "type": "phrase_prefix",
            "fields": [
                "subject",
                "message"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'quick brown f',
        type: 'phrase_prefix',
        fields: [
          'subject',
          'message'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "quick brown f",
	      "type": "phrase_prefix",
	      "fields": [
	        "subject",
	        "message"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "quick brown f",
      type: "phrase_prefix",
      fields: ["subject", "message"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown f",
      "type":       "phrase_prefix",
      "fields":     [ "subject", "message" ]
    }
  }
}

будет выполнен как:

$params = [
    'body' => [
        'query' => [
            'dis_max' => [
                'queries' => [
                    [
                        'match_phrase_prefix' => [
                            'subject' => 'quick brown f',
                        ],
                    ],
                    [
                        'match_phrase_prefix' => [
                            'message' => 'quick brown f',
                        ],
                    ],
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "dis_max": {
            "queries": [
                {
                    "match_phrase_prefix": {
                        "subject": "quick brown f"
                    }
                },
                {
                    "match_phrase_prefix": {
                        "message": "quick brown f"
                    }
                }
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      dis_max: {
        queries: [
          {
            match_phrase_prefix: {
              subject: 'quick brown f'
            }
          },
          {
            match_phrase_prefix: {
              message: 'quick brown f'
            }
          }
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "dis_max": {
	      "queries": [
	        {
	          "match_phrase_prefix": {
	            "subject": "quick brown f"
	          }
	        },
	        {
	          "match_phrase_prefix": {
	            "message": "quick brown f"
	          }
	        }
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    dis_max: {
      queries: [
        {
          match_phrase_prefix: {
            subject: "quick brown f",
          },
        },
        {
          match_phrase_prefix: {
            message: "quick brown f",
          },
        },
      ],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        { "match_phrase_prefix": { "subject": "quick brown f" }},
        { "match_phrase_prefix": { "message": "quick brown f" }}
      ]
    }
  }
}

Также поддерживает analyzer, boost, lenient и zero_terms_query, как описано в Match, а также slop, описанное в Match phrase. Тип phrase_prefix дополнительно поддерживает max_expansions.

phrase, phrase_prefix и fuzziness

Параметр fuzziness не может использоваться с типами phrase или phrase_prefix.

cross_fields

Тип cross_fields особенно полезен с структурированными документами, где несколько полей должны совпадать. Например, при запросе полей first_name и last_name для «Уилла Смита», наиболее подходящий результат, скорее всего, будет содержать «Уилл» в одном поле и «Смит» в другом.

Это похоже на задачу для most_fields, но у этого подхода есть две проблемы. Первая проблема в том, что operator и minimum_should_match применяются к каждому полю, а не к каждому термину (см. объяснение выше).

Вторая проблема связана с релевантностью: разные частоты встречаемости терминов в полях first_name и last_name могут приводить к неожиданным результатам.

Например, представьте двух человек: «Уилл Смит» и «Смит Джонс». «Смит» в качестве фамилии встречается очень часто (и поэтому имеет низкую важность), но «Смит» в качестве имени встречается очень редко (и поэтому имеет высокую важность).

Если мы выполним поиск по запросу «Уилл Смит», документ «Смит Джонс» может отобразиться выше, чем более подходящий документ «Уилл Смит», так как оценка first_name:smith превзошла комбинированные оценки first_name:will плюс last_name:smith.

Один из способов решения таких запросов — просто индексировать поля first_name и last_name в одно поле full_name. Конечно, это можно сделать только во время индексирования.

Тип cross_field пытается решить эти проблемы во время запроса, используя термин-ориентированный подход. Сначала он анализирует строку запроса на отдельные термины, затем ищет каждый термин в любом из полей, как если бы это было одно большое поле.

Запрос, подобный:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'Will Smith',
                'type' => 'cross_fields',
                'fields' => [
                    'first_name',
                    'last_name',
                ],
                'operator' => 'and',
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "Will Smith",
            "type": "cross_fields",
            "fields": [
                "first_name",
                "last_name"
            ],
            "operator": "and"
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'Will Smith',
        type: 'cross_fields',
        fields: [
          'first_name',
          'last_name'
        ],
        operator: 'and'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "Will Smith",
	      "type": "cross_fields",
	      "fields": [
	        "first_name",
	        "last_name"
	      ],
	      "operator": "and"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "Will Smith",
      type: "cross_fields",
      fields: ["first_name", "last_name"],
      operator: "and",
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Will Smith",
      "type":       "cross_fields",
      "fields":     [ "first_name", "last_name" ],
      "operator":   "and"
    }
  }
}

выполняется как:

+(first_name:will last_name:will)
+(first_name:smith last_name:smith)

Другими словами, все термины должны присутствовать по крайней мере в одном поле для соответствия документа. (Сравните это с логикой, используемой для best_fields и most_fields.)

Это решает одну из двух проблем. Проблема с различными частотами встречаемости терминов решается путем смешивания частот встречаемости терминов для всех полей для выравнивания различий.

На практике first_name:smith будет обрабатываться так, как будто у него такие же частоты, как у last_name:smith, плюс еще одна. Это сделает соответствия по полям first_name и last_name сопоставимыми по оценкам, с незначительным преимуществом для last_name, поскольку это наиболее вероятное поле, содержащее smith.

Обратите внимание, что cross_fields обычно полезен только для коротких строковых полей, у которых все имеют boost, равное 1. В противном случае бонусы, частоты встречаемости терминов и нормализация длины вносят вклад в оценку таким образом, что слияние статистических данных терминов больше не имеет смысла.

Если вы запустите вышеуказанный запрос через Проверка, он вернет такое объяснение:

+blended("will",  fields: [first_name, last_name])
+blended("smith", fields: [first_name, last_name])

Также поддерживаются analyzer, boost, operator, minimum_should_match, lenient и zero_terms_query.

Тип cross_fields смешивает статистику полей сложным способом, который может быть трудно интерпретировать. Комбинация оценок может быть даже некорректной, особенно когда некоторые документы содержат некоторые из поисковых полей, но не все из них. В качестве альтернативы следует рассмотреть запрос combined_fields, который также ориентирован на термины, но сочетает статистику полей более надежным способом.

cross_field и анализ

Тип cross_field может работать только в режиме ориентированном на термины, с полями, имеющими одинаковый анализатор. Поля с одинаковым анализатором группируются, как в примере выше. Если есть несколько групп, запрос будет использовать лучшую оценку из любой группы.

Например, если у нас есть поля first и last с одинаковым анализатором, а также first.edge и last.edge, которые оба используют анализатор edge_ngram, этот запрос:

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'Jon',
                'type' => 'cross_fields',
                'fields' => [
                    'first',
                    'first.edge',
                    'last',
                    'last.edge',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "Jon",
            "type": "cross_fields",
            "fields": [
                "first",
                "first.edge",
                "last",
                "last.edge"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'Jon',
        type: 'cross_fields',
        fields: [
          'first',
          'first.edge',
          'last',
          'last.edge'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "Jon",
	      "type": "cross_fields",
	      "fields": [
	        "first",
	        "first.edge",
	        "last",
	        "last.edge"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "Jon",
      type: "cross_fields",
      fields: ["first", "first.edge", "last", "last.edge"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "Jon",
      "type":       "cross_fields",
      "fields":     [
        "first", "first.edge",
        "last",  "last.edge"
      ]
    }
  }
}

будет выполнен как:

    blended("jon", fields: [first, last])
| (
    blended("j",   fields: [first.edge, last.edge])
    blended("jo",  fields: [first.edge, last.edge])
    blended("jon", fields: [first.edge, last.edge])
)

Другими словами, first и last будут сгруппированы и обработаны как одно поле, а first.edge и last.edge будут сгруппированы и обработаны как одно поле.

Наличие нескольких групп нормально, но в сочетании с operator или minimum_should_match, это может страдать от той же проблемы, что и most_fields или best_fields.

Вы можете легко переписать этот запрос как два отдельных запроса cross_fields в сочетании с запросом dis_max и применить параметр minimum_should_match только к одному из них:

resp = client.search(
    query={
        "dis_max": {
            "queries": [
                {
                    "multi_match": {
                        "query": "Will Smith",
                        "type": "cross_fields",
                        "fields": [
                            "first",
                            "last"
                        ],
                        "minimum_should_match": "50%"
                    }
                },
                {
                    "multi_match": {
                        "query": "Will Smith",
                        "type": "cross_fields",
                        "fields": [
                            "*.edge"
                        ]
                    }
                }
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      dis_max: {
        queries: [
          {
            multi_match: {
              query: 'Will Smith',
              type: 'cross_fields',
              fields: [
                'first',
                'last'
              ],
              minimum_should_match: '50%'
            }
          },
          {
            multi_match: {
              query: 'Will Smith',
              type: 'cross_fields',
              fields: [
                '*.edge'
              ]
            }
          }
        ]
      }
    }
  }
)
puts response
const response = await client.search({
  query: {
    dis_max: {
      queries: [
        {
          multi_match: {
            query: "Will Smith",
            type: "cross_fields",
            fields: ["first", "last"],
            minimum_should_match: "50%",
          },
        },
        {
          multi_match: {
            query: "Will Smith",
            type: "cross_fields",
            fields: ["*.edge"],
          },
        },
      ],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "multi_match" : {
            "query":      "Will Smith",
            "type":       "cross_fields",
            "fields":     [ "first", "last" ],
            "minimum_should_match": "50%" 
          }
        },
        {
          "multi_match" : {
            "query":      "Will Smith",
            "type":       "cross_fields",
            "fields":     [ "*.edge" ]
          }
        }
      ]
    }
  }
}

Либо will, либо smith должны присутствовать в любом из полей first или last

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

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'Jon',
                'type' => 'cross_fields',
                'analyzer' => 'standard',
                'fields' => [
                    'first',
                    'last',
                    '*.edge',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "Jon",
            "type": "cross_fields",
            "analyzer": "standard",
            "fields": [
                "first",
                "last",
                "*.edge"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'Jon',
        type: 'cross_fields',
        analyzer: 'standard',
        fields: [
          'first',
          'last',
          '*.edge'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "Jon",
	      "type": "cross_fields",
	      "analyzer": "standard",
	      "fields": [
	        "first",
	        "last",
	        "*.edge"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "Jon",
      type: "cross_fields",
      analyzer: "standard",
      fields: ["first", "last", "*.edge"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
   "multi_match" : {
      "query":      "Jon",
      "type":       "cross_fields",
      "analyzer":   "standard", 
      "fields":     [ "first", "last", "*.edge" ]
    }
  }
}

Используйте анализатор standard для всех полей.

который будет выполнен как:

blended("will",  fields: [first, first.edge, last.edge, last])
blended("smith", fields: [first, first.edge, last.edge, last])

tie_breaker

По умолчанию каждый запрос blended для каждого термина будет использовать лучшую оценку, возвращенную любым полем в группе. Затем, при объединении оценок по группам, запрос использует лучшую оценку из любой группы. Параметр tie_breaker может изменить поведение для обеих этих операций:

0.0

Возьмите единственную лучшую оценку из (например) first_name:will и last_name:will (по умолчанию)

1.0

Сложите оценки (например) first_name:will и last_name:will

0.0 < n < 1.0

Возьмите единственную лучшую оценку плюс tie_breaker, умноженную на каждую из оценок из других совпадающих полей/групп

cross_fields и fuzziness

Параметр fuzziness не может использоваться с типом cross_fields.

bool_prefix

Тип bool_prefix использует механизм подсчёта, аналогичный most_fields, но вместо запроса match использует match_bool_prefix.

$params = [
    'body' => [
        'query' => [
            'multi_match' => [
                'query' => 'quick brown f',
                'type' => 'bool_prefix',
                'fields' => [
                    'subject',
                    'message',
                ],
            ],
        ],
    ],
];
$response = $client->search($params);
resp = client.search(
    query={
        "multi_match": {
            "query": "quick brown f",
            "type": "bool_prefix",
            "fields": [
                "subject",
                "message"
            ]
        }
    },
)
print(resp)
response = client.search(
  body: {
    query: {
      multi_match: {
        query: 'quick brown f',
        type: 'bool_prefix',
        fields: [
          'subject',
          'message'
        ]
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "multi_match": {
	      "query": "quick brown f",
	      "type": "bool_prefix",
	      "fields": [
	        "subject",
	        "message"
	      ]
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  query: {
    multi_match: {
      query: "quick brown f",
      type: "bool_prefix",
      fields: ["subject", "message"],
    },
  },
});
console.log(response);
GET /_search
{
  "query": {
    "multi_match" : {
      "query":      "quick brown f",
      "type":       "bool_prefix",
      "fields":     [ "subject", "message" ]
    }
  }
}

Поддерживаются параметры analyzer, boost, operator, minimum_should_match, lenient, zero_terms_query и auto_generate_synonyms_phrase_query, описанные в запросе match. Для терминов, используемых для построения запросов term, поддерживаются параметры fuzziness, prefix_length, max_expansions, fuzzy_rewrite и fuzzy_transpositions, но они не влияют на запрос prefix, построенный из конечного термина.

Параметр slop не поддерживается этим типом запроса.

© 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/query-dsl-multi-match-query.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API