Как правильно сортировать выдачу?


(Stanleer) #1

Использую прослойку elastica

Есть индекс с уличами и номерами домов.

Без указания параметров все ищется нормально.

$resultSet = $search->addIndex($this->_index)
    ->addType('street')
    ->search($text."*")
;

Как пытаюсь указать параметры ничего не находит.

     $query = new \Elastica\Query\Builder('{
        "query" : {
            "term" : { "street_name" : "'.$text.'" }
        },
        "sort": { "street_name": { "order": "desc" }}
    }');

    $query = new \Elastica\Query($query->toArray());

    $resultSet = $search->addIndex($this->_index)
        ->addType('street')
        ->search($query)
    ;

Подскажите как правильно сортировать выдачу?


(Igor Motov) #2

Проблема не в сортировке - проблема в запросе. Попробуйте использовать запрос match вместо term. Запрос term не выполняет анализ запросов и поэтому может не вернуть ожидаемые результаты.


(Stanleer) #3

Спасибо за ответ, буду пробовать.


(Stanleer) #4

Ничего не получилось.

Давай начем тогда с начала.

Есть два поля в документе и надо найти документ, который совпадает по этим двум полям.

пробовал такой запрос, ни к чему не привел

{
    "query": {
        "bool": {
            "should": [
                { "match": { "id": "id" }},
                { "match": { "name": "name" }}
            ]
        }
    }
}

Так же не понятно ка искать если одно поле заполняется частично, реализуется механизм подсказок.


(Igor Motov) #5

Как выглядит документ и схема индекса?


(Stanleer) #6

не вставляется с нормальной разметкой (

{
"geo" : {
"mappings" : {
"street" : {
"properties" : {
"parentguid" : {
"type" : "string"
},
"postalcode" : {
"type" : "string"
},
"street_name" : {
"type" : "string"
},
"street_shortname" : {
"type" : "string"
}
}
},
"city" : {
"properties" : {
"city_id" : {
"type" : "string"
},
"name" : {
"type" : "string"
},
"region_id" : {
"type" : "string"
}
}
}
}
}
}

в одном типе ищу по точному совпадению с parentguid и не полному street_name

документ создается так

$document = new \Elastica\Document($street->code, [ 'parentguid' => $street->parentguid, 'street_shortname' => $street->shortname, 'street_name' => $street->formalname, 'postalcode' => $street->postalcode ]);


(Igor Motov) #7

Как то так?

curl -XDELETE "localhost:9200/test?pretty"
curl -XPUT "localhost:9200/test?pretty" -d '{
  "settings": {
    "number_of_replicas": 0,
    "number_of_shards": 1
  },
  "mappings" : {
    "street" : {
      "properties" : {
        "parentguid" : {
          "type" : "string",
          "index": "not_analyzed"
        },
        "postalcode" : {
          "type" : "string",
          "index": "not_analyzed"
        },
        "street_name" : {
          "type" : "string"
        },
        "street_shortname" : {
          "type" : "string"
        }
      }
    },
    "city" : {
      "properties" : {
        "city_id" : {
          "type" : "string",
          "index": "not_analyzed"
        },
        "name" : {
          "type" : "string"
        },
        "region_id" : {
          "type" : "string",
          "index": "not_analyzed"
        }
      }
    }
  }
}'
curl -XPUT "localhost:9200/test/street/1?pretty" -d '{
  "parentguid": "12345-67890",
  "street_name": "Main St."
}'
curl -XPOST "localhost:9200/test/_refresh?pretty"
curl -XGET "localhost:9200/test/street/_search?pretty" -d '{
    "query": {
        "bool": {
            "must": [
                { "match": { "parentguid": "12345-67890" }},
                { "match": { "street_name": "main" }}
            ]
        }
    }
}'

(Stanleer) #8

попробовал ранее такой вариант
отличалось только параметрами в mapping
почему то ищет толко при полном совпадении названии улицы

попробовал твой вариант, работает, может все из-за русской морфологии?

еще вопрос - можно ли как то журналировать get post запросы для отладки?


(Stanleer) #9

хотя нет, твой вариант тоже не рабочий

если ввести "street_name": "mai", то не найдет


(Igor Motov) #10

Вы нигде не упоминали, что Вам необходим поиск по подстроке. Поиск по подстроке - это уже совсем другая история.

Посмотрите соседний топик. Я там привел пример поиска по подстрокам.


(Stanleer) #11

Возможно я не правилно описал ситуацию

в одном типе ищу по точному совпадению с parentguid и не полному street_name

по неполному совпадению в street_name и имелос ввиду.


(Igor Motov) #12

Тогда пример с edgeNGram в соседнем топике должен для вас работать.


(Stanleer) #13

читал в интернете, что для русской морфологии ставятся плагины
особо разницы не заметил, это не актуально?


(Igor Motov) #14

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

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


(Stanleer) #15

Снова возвращаюсь к этому вопросу

$mapping = [
			'code' => [
				'type' => 'string',
				'index' => 'not_analyzed'
			],
			'parentguid' => [
				'type' => 'string',
				'index' => 'not_analyzed'
			],
			'postalcode' => [
				'type' => 'string',
				'index' => 'not_analyzed'
			],
			'street_shortname' => [
				'type' => 'string',
				'index' => 'not_analyzed'
			],
			'street_name' => [
				'type' => 'string',
				'analyzer' => 'ngram_index_analyzer',
				'search_analyzer' => 'ngram_search_analyzer'
			],
			'full_name' => [
				'type' => 'string',
				'index' => 'not_analyzed'
			]
		];

index

			'number_of_shards' => 1,
			'number_of_replicas' => 0,
			'analysis' => [
				'analyzer' => [
					'ngram_index_analyzer' => [
						'tokenizer' => 'standard',
						'filter' => ['lowercase', 'ngram']
					],
					'ngram_search_analyzer' => [
						'tokenizer' => 'standard',
						'filter' => ['lowercase']
					],
				],
				'filter' => [
					'ngram' => [
						'type' => 'edgeNGram',
						'min_gram' => 3,
						'max_gram' => 50
					]
				]
			]

и сам запрос

            "query": {
                "bool": {
                    "must": [
                        { "match": { "parentguid": "'.$parentguid.'" }},
                        { "match": { "street_name": "'.$query.'" }}
                    ]
                }
            }

при поиске улицы: кузнецкий мост
выдает Кузнецкий Мост и Богатырский Мост

при поиске улицы зацепенский вал выдает что только можно где было вхождение

как организовать нормальный поиск без левых выдач?


(Igor Motov) #16

Вы не могли бы задать этот вопрос на более техническом уровне. "Нормальный" -понятие растяжимое, и левая выдача для одного типа задач - правильная выдача для другого. Я понимаю, что точно определить, как оптимальный поиск должен работать достаточно сложно. Поэтому можно было бы начать с примера с данными, запросами, результатом, который этот пример выдает и ожидаемым результатом, который вы хотели бы видеть.


(Stanleer) #17

Я пытаюсь сделать систему подсказок как в яндкес или гугл картах

там же не выводится по искомой фразе еще много чего нерелеватного

если я набираю последовательно фразу Кузнецкий Мост

Набирав кузнецкий выдает Кузнецкий Мост
если дописываю мост уже выдает и Кузнецкий Мост и Богатырский Мост
разве это нормально?


(Igor Motov) #18

Если вам важно совпадение по слову "мост", то это вполне нормально, тем более, что кузнецкий мост появляется в результатах перед богатырским. Если вам необходимо совпадение по все словам, которые появляются в запросе, то вам надо переключится на другой режим в запросе match. Например с использованием оператора and, если вам важно совпадение всех слов в запросе

"match": {
    "street_name": {
        "query": "кузнецкий мост",
        "operator" : "and"
    }
}

Или с использованием match_phrase если важен порядок слов:

"match_phrase": {
    "street_name": {
        "query": "Кузнецкий Мост"
    }
}

Но при этом кузнецкий мо вам больше ничего возвращать не будет, потому, что у вас n-gram начинаются с 3, а нужно, чтобы они начинались с 1.

Вариантов много. Надо только точно определиться, что является "нормальным" для вас. И, главное, не подходить к elasticsearch, как к какому-то магическому существу, которое может залезть к вам в голову и точно определить, что вы хотите найти. Тут никакой магии нет - все дело в простом совпадении генерируемых токенов при запросе и индексировании. И контроль над тем, как эти токены генерируются - в ваших руках. Если вы не хотите этот контроль, то лучше использовать более простые средства - анализатор standard и запрос match_phrase_prefix.


(Stanleer) #19

С operator стало все яснее, еще пробую другие варинаты.
Про минимальный лимит ngram вылетело из головы вообще )

Мне кажется если бы вы реализовали механизм запросов через sql было бы удобнее и понятнее.
Многое не понятно новичку, тк тут совсем другая идеология.
В том же sphinx все через sql и довольно быстро можно освоиться, с sql то все знакомы )


(Igor Motov) #20

Так в этом-то и все дело! Если бы мы реализовали механизм запросов через sql, то пользователи бы стали пользоваться elasticsearch как реляционной базой, и ни к чему хорошему это бы не привело. Elasticsearch это поисковый движок, который использует совсем другие модели данных, которые базируются на совсем других математических моделях и алгоритмах, и поэтому он требует иного подхода к моделированию данных и построению запросов.