Поиск с boost на определенные типы данных


(Vladimir Yegudin) #1

Добрый день, можно еще пару вопросов?

Собственно делаю поиск по городам, регионам, странам. Сами данные о этих локациях лежат в мускуле, каждый в своей таблице. В Elasticsearch гружу лишь наименование и делаю по нему поиск, там же вижу id из мускуля и дальше работаю с данными.

Создал такой маппинг:

 'properties' => ['mysql__id' => [
 						'type' => 'integer',
 						'index' => 'not_analyzed',
 					],
 					'type__place' => [
 						'type' => 'text',
 						'index' => 'not_analyzed',
 					],
 					'name' => [
 						'type' => 'text',
 						'analyzer' => 'ru_index_om',
 						'search_analyzer' => 'ru_search',
 						'fields' => [
 							'with_synonyms' => [
 								'type' => 'text',
 								'analyzer' => 'ru_index_wm'
 							]
 						]
 					],
 					'synonims' => [
 						'type' => 'text',
 						'analyzer' => 'ru_index_wm',
 						'search_analyzer' => 'ru_search',
 						'fields' => [
 							'with_synonyms' => [
 								'type' => 'text',
 								'analyzer' => 'ru_index_wm'
 							]
 						]
 					]]

ru_index_om - анализатор без морфологии
ru_index_wm - анализатор с морфологией

Поиск делаю таким запросом:

 'query' => ['bool' => [
 				'must' => [
 					'bool' => [
 						'minimum_should_match' => 1,
 						'should' => [
 							'query_string' => [
 								'default_operator' => 'AND',
 								'query' => 'сев',
 								'fields' => [
 									'name^4', 'name.with_synonyms^3',
 									'synonyms^2', 'synonyms.with_synonyms^1',
 								]
 							]
 						]
 					]
 				],
 				'should' => [
 					'bool' => [
 						'minimum_should_match' => 0,
 						'should' => [
 						..........................
 						]
 					]
 				],
 			]
 		]

Там где многоточие - если не пишу ничего то находит варианты, если же указываю вот такое:

 'term' => [
 	'type__place' => 'city',
 	'boost' => 4
 ],
 'term' => [
 	'type__place' => 'country',
 	'boost' => 1
 ],
 'term' => [
 	'type__place' => 'region',
 	'boost' => 2
 ]

То перестает выдавать результат вообще.
Немного поясню - я ожидал что boost будет поднимать данные о городах над регионами, а это условие просто перестает выводить результаты. Не могу понять почему ((


(Igor Motov) #2

Какая версия?


(Vladimir Yegudin) #3

Эластик стоит версии 5.6.5


(Igor Motov) #4

Этот кусок можно упростить:

 					'type__place' => [
 						'type' => 'keyword'
 					]

Я в PHP не силен, но по-моему то, что вы хотите в PHP должно выглядеть примерно так:

 'query' => ['bool' => [
           'must' => [
             'query_string' => [
               'default_operator' => 'AND',
               'query' => 'сев',
               'fields' => [
                 'name^4', 'name.with_synonyms^3',
                 'synonyms^2', 'synonyms.with_synonyms^1',
               ]
             ]
           ],
           'should' => [
             [
               'term' => [
                 'type__place' => [
                   'value' => 'city',
                   'boost' => 4
                 ]
               ]           
             ],
             [
               'term' => [
                 'type__place' => [
                   'value' => 'country',
                   'boost' => 1
                 ]
               ]             
             ],
             [
               'term' => [
                 'type__place' => [
                   'value' => 'region',
                   'boost' => 2
                 ]
               ]             
             ]
           ]
         ]
       ]

(Vladimir Yegudin) #5

Спасибо, но поведение совсем не то, которое я ожидаю (( Пишу по вашему примеру вот так (изменил на json, в моем случае различие лишь => изменяется на двоеточие и скобки квадратные на круглые):

"query" : {
			"bool" : {
				"must" : {
					"bool" : {
						"minimum_should_match" : 1,
						"should" : {
							"query_string" : {
								"default_operator" : "AND",
								"query" : "сев",
								"fields" : [
									"name^4", "name.with_synonyms^3",
									"synonyms^2", "synonyms.with_synonyms^1",
								]
							}
						}
					}
				},
				"should" : {
					"bool" : {
						"minimum_should_match" : 0,
						"should" : {
							"term" : {
								"type__place" : {
									"value" : "city",
									"boost" : 50
								}
							},
							"term" : {
								"type__place" : {
									"value" : "country",
									"boost" : 1
								}
							},
							"term" : {
								"type__place" : {
									"value" : "region",
									"boost" : 2
								}
							}
						}
					}
				},
			}
		}

Ожидаю, что все записи у которых в поле type__place указан city - будут выше региона и страны, но вверху оказывается именно регион... Почему так, ведь буст указал аж 50... Я думал так, что если поле равно значению - то будет вес у таких записей увеличиваться...


(Vladimir Yegudin) #6

Поставил Kiban-у очень удобный инструмент, но по прежнему не могу понять как правильно написать синтаксис. :frowning:

Собственно хочу сделать так - обязательное условие - выбирает записи которые попадают в индекс по запросу, вес по полям определяется коэффициентами в полях + вес выбранной записи корректируется в зависимости от значения поля (города выше, регионы ниже, страны еще ниже) ?

Сейчас запрос синтаксически не верный так как term как свойство объекта, не может повторяться,


(Igor Motov) #7

Перевод в json был не совсем правильный -bool должен быть один на всех, а should должен быть массивом запросов. Но то, что вы описали, лучше сделать с помощью запроса function_score вместо bool.


(Vladimir Yegudin) #8

Я пробовал should сделать массивом вот так:

"should" : [
    {
      "term": {
        "boost": 3,
        "type__place": "city"
      }
    },
    {
      "term": {
        "boost": 2,
        "type__place": "region"
      }
    }
  ]

однако мне пишет: "type": "parsing_exception", "reason": "[term] query doesn't support multiple fields, found [boost] and [type__place]"

и не очень понимаю как сделать один bool я тут же сделал одну секцию в которой как бы обозначаю что должно быть хотя бы одно соответствие, а во второй я допускаю что соответствий может не быть (тип региона к примеру другой), но если соответствие есть то пытаюсь добавить коэффициент


(Igor Motov) #9

Я пробовал should сделать массивом вот так:

Должно быть

    {
      "term": {
        "type__place": {
           "value": "city",
           "boost": 3
        }
      }
    }

а во второй я допускаю что соответствий может не быть

Посмотрите последний параграф в документации для minimum_should_match. В вашем случае "minimum_should_match" : 0 просто игнорируется. И поскольку json в запросе неправильный (у вас одно и тоже поле term повторяется 3 раза), только один term остается, и ваш запрос по сути превращается в

"should": {
  "minimum_should_match" : 1,
  "term": {... какой-то один из 3-x...}
}

(Vladimir Yegudin) #10

Спасибо, синтаксис поправил, убрал все лишнее из запроса и перечитал статью
Получилось вот так:

{
  "query": {
    "bool": {
      "must": {
        "query_string": {
          "default_operator": "AND",
          "query": "сев",
          "fields": [
            "name^2",
            "name.with_synonyms^1"
          ]
        }
      },
      "should": [
        {
          "term": {
            "type__place": {
              "value": "city",
              "boost": 2
            }
          }
        },
        {
          "term": {
            "type__place": "region"
          }
        }
      ]
    }
  }
}

Если boost стоит как здесь 2, 5, 10, 30 - вначале идут регионы. а только потом города. Если коэффициент boost увеличить до 100 и более - города выпрыгивают наверх. Не очень понятно как правильно считать индексы подбор ведь не всегда может быть верным


(Igor Motov) #11

А вам действительно нужно строгое разделение? То есть чтобы города всегда были впереди регионов. То есть даже если есть супер релевантный результат в регионе, он все-равно должен идти после самого плохого результата в городе?


(system) #12

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.