Поиск по двум связанным типам в одном индексе


(Дмитрий) #1

Коллеги, добрый день!
Есть 2 типа в одном индексе. Пусть это будет Компании и Товары. У компании есть поле company_name. Товары связаны с компаниями один ко многим, 1 компания - несколько товаров. Допустим, у товаров есть поле company_id, в который заносим идентификатор компании.
Нужно составить запрос, поиск по товарам, но чтобы искалось поисковое слово и с учетом поля company_name в типе компании, аналог JOIN в mysql, примерно так:

SELECT good_id FROM goods g INNER JOIN company c USING(company_id) WHERE c.company_name LIKE '%поисковое_слово%';

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

Поиск приводит неизменно на nested запросы и has_child/has_parent запросы. Но никак не могу понять, чем они могут мне помочь..

Установленная версия ES 5.6.8
Заранее спасибо всем откликнувшимся!)


(Igor Motov) #2

В случае с запросом parent/child ваш запрос будет транслироваться во что-то типа:

GET /test_index/goods/_search
{
    "query": {
        "has_parent" : {
            "parent_type" : "company",
            "query" : {
                "match" : {
                    "company_name" : "поисковое_слово"
                }
            }
        }
    }
}

Оба типа должны быть проиндексированы соответствующим образом, чтобы у них было отношение parent/child. Надо также отметить, что в связи с переходом на 1 тип в одном индексе, начиная с версии 6.x лучше использовать join для этих целей.


(Дмитрий) #3

Всё-таки не до конца я добил parent/child )) Игорь, спасибо, буду разбираться )


(Дмитрий) #4

Получилось, но наполовину... Запрос отрабатывает, но только по полю company_name родительского типа. В реальной ситуации нужно еще искать и фильтровать и по полям ребенка.

Mapping (упрощенно):

$params = [
		'index' => $this->_index,
		'type' => parent::TYPE_GOOD_OFFERS,
		'body' => [
			parent::TYPE_GOOD_OFFERS => [
                                     '_parent' => [
                                         'type' => parent::TYPE_USERS
                                    ],
				'properties' => [
					'countryId' => [
						'type' => 'short'
					],
                                            'caption' => [
						'type' => 'text',
						'analyzer' => 'my_analyzer'
					],
					'description' => [
						'type' => 'text',
						'analyzer' => 'my_analyzer'
					]
				]
			]
		]
	];

	$this->_ElasticSearchClient->indices()->putMapping($params);

Запрос:

$this->_paramsForSearch = [
		'index' => $this->_index,
		'type' => $this->_type,
		'body' => [
			'size' => 10,
			'from' => 0,
			'sort' => $this->_getSortCommon(),
			'query' => [
			    'has_parent' => [
			        'parent_type' => parent::TYPE_USERS,
                    'query' => [
                        'bool' => [
                           'must' => [
                                'multi_match' => [
                                    'query' => $this->_searchText,
                                    'fields' => ['caption', 'description', 'companyName']
                                ]
                            ]
                        ]
                    ]
                ]
			]
		]
	];

Соответственно, при таком запросе он ищет только по полю companyName, игнорируя caption и description. Не могу понять, где тогда их указывать. Плюс мне еще нужно фильтровать по полю countryId (см. mapping), тоже не понятно куда вставлять этот кусок кода:

'term' => [
				'countryId' => 1
			]

Буду признателен за помощь


(Igor Motov) #5

Если поля caption и description относятся к goods то их не надо засовывать в has_parent:

"bool": {
  "must": [{
    "multi_match": {..... запрос по goods .... }
  }, {
    "has_parent": { .... запрос  по company .... }
  }]
}