Запрос с повышением поля Nest не возвращает результатов от Elasticsearch
У меня есть реальные проблемы с получением запроса с использованием повышения полей для работы с Elasticsearch. Я просматривал документы Nest по этой теме, но они не были особенно полезны, поэтому мой код действительно основан на решении этого вопроса: Elastic Search с использованием NEST Field Boosting.
Если я выполню следующий запрос, я получу один результат, как и ожидалось:
var matches =
_client.Search<SearchableMerchant>(
s => s.From((page - 1) * pageSize)
.Size(pageSize)
.QueryString("*test*")
.MinScore(1)
);
Однако, если я пытаюсь использовать усиление поля, используя следующее, я не получаю совпадений:
var matches =
_client.Search<SearchableMerchant>(
s => s.From((page - 1) * pageSize)
.Size(pageSize)
.Query(q => q
.Boosting(bq => bq
.Positive(pq => pq
.CustomScore(cbf => cbf
.Query(cbfq => cbfq
.QueryString(
qs => qs
.OnFieldsWithBoost(d => d
.Add("opportunities.acquirerLocationMID", Math.Pow(2, 17))
.Add("opportunities.amexMID", Math.Pow(2, 16))
.Add("opportunities.epayMID", Math.Pow(2, 16))
.Add("v1MerchantId", Math.Pow(2, 16))
.Add("locatorId", Math.Pow(2, 15))
.Add("opportunities.opportunityLocatorId", Math.Pow(2, 14))
.Add("businessName", Math.Pow(2, 13))
.Add("searchablePhone", Math.Pow(2, 12))
.Add("address.postCodeDetails.postCode.postCode", Math.Pow(2, 11))
.Add("contacts.contact.searchableEmailAddress", Math.Pow(2, 11))
.Add("contacts.contact.searchableMainPhone", Math.Pow(2, 10))
.Add("contacts.contact.searchableMobilePhone", Math.Pow(2, 10))
.Add("contacts.contact.fullName", Math.Pow(2, 9))
.Add("contacts.contact.surname", Math.Pow(2, 8))
.Add("contacts.contact.firstName", Math.Pow(2, 7))
.Add("searchableAddress", Math.Pow(2, 6))
.Add("ownershipUser.username", Math.Pow(2, 5))
.Add("ownershipUser.searchableFullName", Math.Pow(2, 4))
.Add("ownershipUser.lastName", Math.Pow(2, 3))
.Add("ownershipUser.firstName", Math.Pow(2, 2))
.Add("opportunities.depositAccount", Math.Pow(2, 1))
.Add("opportunities.depositIban", Math.Pow(2, 1))
.Add("opportunities.feesAccount", Math.Pow(2, 1))
.Add("opportunities.feesIban", Math.Pow(2, 1))
// TODO: Company registration number - somewhere in legal methinks
)
.Query(
"*test*"
)
)
)
)
)
.Negative(nq => nq
.Filtered(nfq => nfq
.Query(qq => qq.MatchAll())
.Filter(f =>
f.Missing("opportunities.acquirerLocationMID")
&& f.Missing("opportunities.amexMID")
&& f.Missing("opportunities.epayMID")
&& f.Missing("v1MerchantId")
&& f.Missing("locatorId")
&& f.Missing("opportunities.opportunityLocatorId")
&& f.Missing("businessName")
&& f.Missing("searchablePhone")
&& f.Missing("address.postCodeDetails.postCode.postCode")
&& f.Missing("contacts.contact.searchableEmailAddress")
&& f.Missing("contacts.contact.searchableMainPhone")
&& f.Missing("contacts.contact.searchableMobilePhone")
&& f.Missing("contacts.contact.fullName")
&& f.Missing("contacts.contact.surname")
&& f.Missing("contacts.contact.firstName")
&& f.Missing("searchableAddress")
&& f.Missing("ownershipUser.username")
&& f.Missing("ownershipUser.searchableFullName")
&& f.Missing("ownershipUser.lastName")
&& f.Missing("ownershipUser.firstName")
&& f.Missing("opportunities.depositAccount")
&& f.Missing("opportunities.depositIban")
&& f.Missing("opportunities.feesAccount")
&& f.Missing("opportunities.feesIban")
)
)
)
.NegativeBoost(0.01)
)
)
.MinScore(1)
);
Я понимаю, что этот код может быть лучше структурирован, но сейчас я просто хочу, чтобы запрос на повышение поля работал - я приведу его позже.
Вот несколько вещей, которые я пробовал:
В документации Nest ничего не говорится о том, можете ли вы использовать OnFieldsWithBoost с именами свойств. Т.е. это нормально?
.OnFieldsWithBoost (d => d.Add ("businessName", Math.Pow (2, 13))
В отличие от этого?
.OnFieldsWithBoost(d => d
.Add(m => m.businessName, Math.Pow(2, 13))
Причина, по которой я спрашиваю, состоит в том, что у меня есть дополнительные свойства, которые я хочу увеличить, которые находятся внутри коллекций Например, opportunities.opportunityLocatorId
, Возможности, очевидно, являются коллекцией, и я хочу сопоставить, где любой объект в этой коллекции имеет соответствующее значение для его opportunityLocatorId
поле.
Это работает с полями - вы можете использовать лямбду или строку - но работает ли это с повышением?
Понятия не имею, но я пробовал это в обоих направлениях, уменьшая количество запросов, чтобы просто включить повышение businessName
, поскольку это поле должно соответствовать строке 'test', но результаты все равно не возвращаются.
Я также пытался избавиться от .Negative
пункт, на тот случай, если это совпадает с тем, что не должно. Он может отменить любой запрос, в котором не найдено совпадений ни в одном из полей, перечисленных в .Positive
пункт. По-прежнему нет результатов.
Я также поднял .NegativeBoost
значение до 1 (то есть без эффекта, поэтому любые результаты не следует фильтровать до значения ниже 1, которое не начиналось с такого низкого показателя), но, опять же, без кубиков.
Вот содержание моего индекса, просто чтобы вы могли видеть, что businessName
поле должно соответствовать 'test' со вторым запросом, как это происходит с первым:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [ {
"_index" : "merchantv2",
"_type" : "searchablemerchant",
"_id" : "00000000-0000-0000-0000-000000000000",
"_score" : 1.0,
"_source":{"merchantGuid":"00000000-0000-0000-0000-000000000000","v1MerchantId":0,"locatorId":"0","address":{"addressGuid":"00000000-0000-0000-0000-000000000000","postCodeDetails":{"postCodeKey":0,"postalDistrict":{"postalDistrictKey":0,"postalDistrict":""},"postalLocation":"0","latitude":0.0,"longitude":0.0,"townName":"None","countyKey":0,"countryKey":0,"postCode":{"postCodeKey":0,"postCode":" 0"}},"county":{"countyKey":0,"countyName":"","countryKey":0,"recStatus":3,"countryKeyValue":0},"countryKey":0,"addressTypeKey":0,"updateDate":"0001-01-01T00:00:00+00:00","createdDate":"2016-01-07T19:46:28.4463+00:00"},"searchableAddress":" 0","searchablePhone":"","searchableFax":"","businessName":"","contacts":[],"opportunities":[{"opportunityGuid":"00000000-0000-0000-0000-000000000000","merchantGuid":"00000000-0000-0000-0000-000000000000","location":{"locationGuid":"00000000-0000-0000-0000-000000000000","tradingAddress":{"verified":false,"addressGuid":"00000000-0000-0000-0000-000000000000","postCodeDetails":{"postCodeKey":0,"postalDistrict":{"postalDistrictKey":0,"postalDistrict":""},"postalLocation":"0","latitude":0.0,"longitude":0.0,"townName":"None","countyKey":0,"countryKey":0,"postCode":{"postCodeKey":0,"postCode":" 0"}},"county":{"countyKey":0,"countyName":"","countryKey":0,"recStatus":3,"countryKeyValue":0},"countryKey":0,"addressTypeKey":0,"updateDate":"0001-01-01T00:00:00+00:00","createdDate":"2016-01-07T19:46:28.4463+00:00"}},"opportunityLocatorId":"000000"}]}
}, {
"_index" : "merchantv2",
"_type" : "searchablemerchant",
"_id" : "5f55fe61-ca65-e411-93f3-0cc47a07ef4a",
"_score" : 1.0,
"_source":{"merchantGuid":"5f55fe61-ca65-e411-93f3-0cc47a07ef4a","locatorId":"PM227Z02","address":{"addressGuid":"5c55fe61-ca65-e411-93f3-0cc47a07ef4a","houseNumber":"242","streetName":"Acklam Road","houseName":"","flatAptSuite":"","townName":"London","postCodeDetails":{"postCodeKey":1,"postalDistrict":{"postalDistrictKey":2782,"postalDistrict":"W10"},"postalLocation":"5JJ","latitude":51.52094651,"longitude":-0.20149990,"townName":"London","countyKey":0,"countryKey":224,"postCode":{"postCodeKey":1,"postCode":"W10 5JJ"}},"county":{"countyKey":626,"countyName":"Kensington And Chelsea","countryKey":224,"recStatus":1,"countryKeyValue":224},"countryKey":224,"addressTypeKey":0,"updateDate":"0001-01-01T00:00:00+00:00","createdDate":"2016-01-07T19:46:28.4653+00:00"},"searchableAddress":"242 Acklam Road, London, Kensington And Chelsea, W10 5JJ","searchablePhone":"+44 2031954484","searchableFax":"","businessName":"Test Merchant","contacts":[],"opportunities":[]}
} ]
}
}
Я использую Elasticsearch 1.7.1 и Nest 1.7.1 на Windows 7 (да, я знаю, но это то, что использует клиент) с.NET 4.5.1.
Я также попытался захватить трафик, проходящий между моим Web API и asticsearch, но безрезультатно. Возможно, проблема с конфигурацией, но ни Fiddler, ни Wireshark/npcap не могут перехватить трафик между этими двумя устройствами, работающими на локальном компьютере, поэтому я не вижу фактического запроса, отправляемого в asticsearch, что, как я подозреваю, было бы полезно. По сути, мне было интересно, возвращалась ли какая-нибудь ошибка от Elasticsearch, которую Гнездо глотало.
Ну... интуиция оказалась верной. Вот пример того, что появляется в файле журнала asticsearch:
[2016-01-08 10:14:01,534][DEBUG][action.search.type ] [Rocket Racer] All shards failed for phase: [query]
org.elasticsearch.search.SearchParseException: [user][4]: from[0],size[20]: Parse Failure [Failed to parse source [{
"from": 0,
"size": 20,
"min_score": 1.0,
"query": {
"boosting": {
"positive": {
"custom_score": {
"query": {
"query_string": {
"query": "*test*",
"fields": [
"opportunities.acquirerLocationMID^131072",
"opportunities.amexMID^65536",
"opportunities.epayMID^65536",
"v1MerchantId^65536",
"locatorId^32768",
"opportunities.opportunityLocatorId^16384",
"businessName^8192",
"searchablePhone^4096",
"address.postCodeDetails.postCode.postCode^2048",
"contacts.contact.searchableEmailAddress^2048",
"contacts.contact.searchableMainPhone^1024",
"contacts.contact.searchableMobilePhone^1024",
"contacts.contact.fullName^512",
"contacts.contact.surname^256",
"contacts.contact.firstName^128",
"searchableAddress^64",
"ownershipUser.username^32",
"ownershipUser.searchableFullName^16",
"ownershipUser.lastName^8",
"ownershipUser.firstName^4",
"opportunities.depositAccount^2",
"opportunities.depositIban^2",
"opportunities.feesAccount^2",
"opportunities.feesIban^2"
]
}
}
}
},
"negative": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"bool": {
"must": [
{
"missing": {
"field": "opportunities.acquirerLocationMID"
}
},
{
"missing": {
"field": "opportunities.amexMID"
}
},
{
"missing": {
"field": "opportunities.epayMID"
}
},
{
"missing": {
"field": "v1MerchantId"
}
},
{
"missing": {
"field": "locatorId"
}
},
{
"missing": {
"field": "opportunities.opportunityLocatorId"
}
},
{
"missing": {
"field": "businessName"
}
},
{
"missing": {
"field": "searchablePhone"
}
},
{
"missing": {
"field": "address.postCodeDetails.postCode.postCode"
}
},
{
"missing": {
"field": "contacts.contact.searchableEmailAddress"
}
},
{
"missing": {
"field": "contacts.contact.searchableMainPhone"
}
},
{
"missing": {
"field": "contacts.contact.searchableMobilePhone"
}
},
{
"missing": {
"field": "contacts.contact.fullName"
}
},
{
"missing": {
"field": "contacts.contact.surname"
}
},
{
"missing": {
"field": "contacts.contact.firstName"
}
},
{
"missing": {
"field": "searchableAddress"
}
},
{
"missing": {
"field": "ownershipUser.username"
}
},
{
"missing": {
"field": "ownershipUser.searchableFullName"
}
},
{
"missing": {
"field": "ownershipUser.lastName"
}
},
{
"missing": {
"field": "ownershipUser.firstName"
}
},
{
"missing": {
"field": "opportunities.depositAccount"
}
},
{
"missing": {
"field": "opportunities.depositIban"
}
},
{
"missing": {
"field": "opportunities.feesAccount"
}
},
{
"missing": {
"field": "opportunities.feesIban"
}
}
]
}
}
}
},
"negative_boost": 0.01
}
}
}]]
at org.elasticsearch.search.SearchService.parseSource(SearchService.java:747)
at org.elasticsearch.search.SearchService.createContext(SearchService.java:572)
at org.elasticsearch.search.SearchService.createAndPutContext(SearchService.java:544)
at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:306)
at org.elasticsearch.search.action.SearchServiceTransportAction$5.call(SearchServiceTransportAction.java:231)
at org.elasticsearch.search.action.SearchServiceTransportAction$5.call(SearchServiceTransportAction.java:228)
at org.elasticsearch.search.action.SearchServiceTransportAction$23.run(SearchServiceTransportAction.java:559)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: org.elasticsearch.index.query.QueryParsingException: [user] No query registered for [custom_score]
at org.elasticsearch.index.query.QueryParseContext.parseInnerQuery(QueryParseContext.java:303)
at org.elasticsearch.index.query.BoostingQueryParser.parse(BoostingQueryParser.java:63)
at org.elasticsearch.index.query.QueryParseContext.parseInnerQuery(QueryParseContext.java:305)
at org.elasticsearch.index.query.IndexQueryParserService.innerParse(IndexQueryParserService.java:382)
at org.elasticsearch.index.query.IndexQueryParserService.parse(IndexQueryParserService.java:281)
at org.elasticsearch.index.query.IndexQueryParserService.parse(IndexQueryParserService.java:276)
at org.elasticsearch.search.query.QueryParseElement.parse(QueryParseElement.java:33)
at org.elasticsearch.search.SearchService.parseSource(SearchService.java:731)
... 9 more
Так что я делаю не так? Кто-нибудь знает, как исправить второй запрос, который по-видимому не любит эластичный поиск? И есть ли какой-нибудь способ вытащить из Гнезда каких-либо ошибок? Я ожидал исключение, но этого не происходит - он просто возвращается молча с пустой коллекцией совпадений, и в коллекции нет свойства, которое указывало бы, что что-то пошло не так.
Любая помощь с благодарностью получена.
Спасибо!
Барт
2 ответа
Оказывается, то, что я пытаюсь сделать, довольно просто, и я просто исчез на некоторое время не в той кроличьей норе. Например, вот multi_match
запрос, к которому я применил повышение полей:
curl -XGET http://localhost:9200/merchantv2/_search -d '
{
"query": {
"multi_match": {
"query": "test",
"type": "phrase_prefix",
"fields" : ["businessName^3", "address.streetName"]
}
}
}'
В этом случае я повысил businessName
поле, в котором найденные совпадения в три раза важнее, чем найденные в address.streetName
, Кажется, работает просто отлично.
Вот ссылка на соответствующую документацию: https://www.elastic.co/guide/en/elasticsearch/reference/1.7/query-dsl-multi-match-query.html (поддерживает Val для этого, который он предложил для другой вопрос).
Спасибо за указатели!
Пользовательский запрос оценки не рекомендуется в Elasticsearch 0.90.4 и удален в Elasticsearch 1.x. Он хранится в NEST для обратной совместимости. Вместо этого вы должны использовать запрос оценки функции.
NEST, однако, должен был указать, что ошибка произошла через IsValid
свойство, которое должно быть false
в этом случае. По умолчанию NEST 1.x не создает исключений Elasticsearch. Вы можете включить это поведение, установив ThrowOnElasticsearchServerExceptions()
на ваше ConnectionSettings
,
Примечание: использование подстановочного знака в начале термина (например, *test
), как правило, является плохой практикой, поскольку это приведет к проверке каждого члена в индексе. Возможно, вы захотите изменить ваши отображения и использовать что-то вроде токенайзера nGram.