Elasticsearch should operator on multiple fields - elasticsearch

Currently, I am trying to query the elasticsearch with should clause on multiple fields along with must clause in one field.
with SQL I would write this query:
SELECT * FROM test where ( titleName='Business' OR titleName='Bear') AND (status='Draft' OR status='Void') AND creator='bob'
I tried this:
$params = [
'index' => myindex,
'type' => mytype,
'body' => [
"from" => 0,
"size" => 1000,
'query' => [
'bool' => [
'must' => [
'bool' => [
'should' => [
['match' => ['titleName' => 'Business']],
['match' => ['titleName' => 'Bear']]
]
],
'should' => [
['match' => ['status' => 'Draft']],
['match' => ['status' => 'Void']]
]
'must' => [
['match'] => ['creator' => 'bob']
]
]
]
]
]
];
The above query string working with single status field or single title field. But it's not working with both the fields.
Does anyone have a solution?

You need to AND both of your bool/should pairs. This should work:
$params = [
'index' => myindex,
'type' => mytype,
'body' => [
"from" => 0,
"size" => 1000,
'query' => [
'bool' => [
'must' => [
[
'bool' => [
'should' => [
['match' => ['titleName' => 'Business']],
['match' => ['titleName' => 'Bear']]
]
]
],
[
'bool' => [
'should' => [
['match' => ['status' => 'Draft']],
['match' => ['status' => 'Draft']]
]
]
],
[
'match' => ['creator' => 'bob']
]
]
]
]
]
];

You can write your query something like this. Add a Must inside that you add Should
{
"query": {
"filter": {
"bool": {
"must": [{
"bool": {
"should": [{
"term": {
"titleName": "business"
}
},
{
"term": {
"titleName": "bear"
}
}
]
}
},
{
"bool": {
"should": [{
"term": {
"status": "draft"
}
},
{
"term": {
"status": "void"
}
}
]
}
},
{
"bool": {
"must": [{
"term": {
"creator": "bob"
}
}]
}
}
]
}
}
},
"from": 0,
"size": 25
}

Related

elasticsearch search string with slash

I try to search for a string that contains a slash (in field firstname) but elasticsearch shows me an error "Failed to parse query ...".
Here is my mapping
[
'index' => 'indexA',
'body' => [
'settings' => [
'number_of_shards' => 1,
'number_of_replicas' => 1,
"analysis" => [
"analyzer" => [
"analyzer_asciifolding" => [
"tokenizer" => "standard",
"filter" => [ "lowercase", "my_ascii_folding" ]
]
],
"filter" => [
"my_ascii_folding" => [
"type" => "asciifolding",
"preserve_original" => true
]
]
]
],
'mappings' => [
'dynamic' => false,
'properties' => [
...,
'persons' => [
'type' => 'nested',
'properties' => [
...,
'firstname' => [
'type' => 'text',
'analyzer' => 'analyzer_asciifolding'
]
]
]
]
]
]
];
Can you help me please?
UPDATED 2022-04-01 15:10
here is the php code which query elasticsearch
$response = $this->elasticsearchService->search([
'index' => 'indexA',
'body' => [
'query' => [
'bool' => [
'filter' => [
[
'query_string' => [
'query' => $attributes['person_firstname'],
'fields' => ['persons.firstname']
]
]
],
]
],
'from' => $from,
'size' => $size
]
]);
You should escape them with a leading backslash.
The reserved characters are: + - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
{
"query": {
"query_string": {
"fields": ["persons.firstname"],
"query": "use\\/share"
}
}
}

Elasticsearch: Is it possible to sort collapsed results by a nested field?

I have a fairly complex mapping which is storing products, and within each document it contains a nested array of pre-calculated prices for each customer.
There may be multiple versions of each product in the index (with unique codes). Alternative products are grouped by a common xrefs_hash. The query I'm writing needs to select the best product for each customer (i.e. aggregate/collapse on the xrefs_hash), and then select the top product based on the value of the prices.weight nested field.
The prices.weight field is a float which we've pre-calculated based on the shops' customer settings on how they want to prioritise their own items. A hash is created from these settings (stored in prices.pricing_hash) so that we can store a single set of pricing if multiple customers share the same settings.
The index contains up to 300,000 products and can end up with ~100,000,000 documents once all prices are calculated and inserted.
The mapping looks something like this (shortened for brevity):
'mappings' => [
'_source' => [
'enabled' => true,
],
'dynamic' => false,
'properties' => [
'dealer_item_id' => [
'type' => 'integer',
],
'code' => [
'type' => 'text',
'analyzer' => 'custom_code_analyzer',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
'xrefs' => [
'type' => 'text',
'analyzer' => 'custom_code_analyzer',
'fields' => [
'raw' => [
'type' => 'keyword',
],
],
],
'xrefs_hash' => [
'type' => 'keyword',
],
'title' => [
'type' => 'text',
'analyzer' => 'custom_english_analyzer',
'fields' => [
'ngram_title' => [
'type' => 'text',
'analyzer' => 'custom_title_analyzer',
],
'raw' => [
'type' => 'keyword',
],
],
],
...
'prices' => [
'type' => 'nested',
'dynamic' => false,
'properties' => [
'pricing_hash' => [
'type' => 'keyword',
'index' => true,
],
'unit_price' => [
'type' => 'float',
'index' => true,
],
'pricebreaks' => [
'type' => 'object',
'dynamic' => false,
'properties' => [
'quantity' => [
'type' => 'integer',
'index' => false,
],
'price' => [
'type' => 'integer',
'index' => false,
],
],
],
'weight' => [
'type' => 'float',
'index' => true,
],
],
],
],
],
Example documents:
{
"dealer_item_id": 122023,
"code": "ABC123A",
"xrefs": [
"ABC123A",
"ABC123B",
],
"title": "Product A",
"xrefs_hash": "16d5415674c8365f63329b11ffc88da109590cec",
"prices": [
{
"pricebreaks": [
{
"quantity": 1,
"price": 9.75,
"contract": false
}
],
"weight": 0.20512820512820512,
"pricing_hash": "aabe06b7",
"unit_price": 9.75,
},
{
"pricebreaks": [
{
"quantity": 1,
"price": 9.75,
"contract": false
}
],
"weight": 0.20512820512820512,
"pricing_hash": "73643f3b",
"unit_price": 9.75,
}
]
},
{
"dealer_item_id": 124293,
"code": "ABC1234B",
"xrefs": [
"ABC123A",
"ABC123B",
],
"title": "Product B",
"xrefs_hash": "16d5415674c8365f63329b11ffc88da109590cec",
"prices": [
{
"contract_item": false,
"pricebreaks": [
{
"quantity": 1,
"price": 7.39,
"contract": false
}
],
"weight": 0.33829499323410017,
"pricing_hash": "aabe06b7",
"unit_price": 7.39,
},
{
"pricebreaks": [
{
"quantity": 1,
"price": 9.75,
"contract": false
}
],
"weight": 0.20512820512820512,
"pricing_hash": "73643f3b",
"unit_price": 9.75,
}
]
},
Example query:
{
"track_total_hits": 100000,
"query": {
"bool": {
"filter": {
"bool": {
"must": [
{
"nested": {
"path": "prices",
"score_mode": "none",
"inner_hits": {
"_source": {
"include": [
"prices"
]
}
},
"query": {
"bool": {
"must": [
{
"term": {
"prices.pricing_hash": "aabe06b7"
}
}
]
}
}
}
},
{
"term": {
"code.raw": "RX58022"
}
}
],
"must_not": [
{
"term": {
"disabled": true
}
}
]
}
}
}
},
"_source": {
"includes": [
"code",
"dealer_item_id",
"title",
"xrefs"
]
},
"collapse": {
"field": "xrefs_hash",
"inner_hits": {
"name": "best_xrefs",
"sort": {
"prices.weight": "desc"
},
"size": 1
}
},
"aggregations": {
"xrefs_count": {
"cardinality": {
"field": "xrefs_hash",
"precision_threshold": 40000
}
}
}
}
I have tried using a collapse query to select the best product, but this does not seem to support sorting by the nested prices.weight field.
I've also tried aggretating based on the xrefs_hash, but this seems to make pagination at the category level impossible.
The above example query almost works, but does not return the collapsed results in the correct order. When inspecting the query it seems to be replacing the collapse sort with Infinity, which apparently ES does if a document does not contain a sort field.
So what I'm wondering is; is it possible to:
Return 1 document per unique xref_hash value
Return the specific document whith the highest prices.weight value, matching customer's pricing_hash
Also make this work with pagination

ElasticSearch 7 - combine filters

I use ES 7 and Laravel implementation, I want to combine a range and a term match, according to documentation, I did this :
$items = $client->search([
'index' => $instance->getSearchIndex(),
'type' => $instance->getSearchType(),
'body' => [
'size' => 50,
'query' => [
'bool' => [
'must' => [
'multi_match' => [
'fields' => config('elasticsearch.fields'),
'query' => $query,
],
],
'filter' => [
'bool' => [
'must' => [
'range' => [
'note' => [
'gte' => config('elasticsearch.note_minimum')
]
],
'term' => [
'type_video_id' => 5
],
],
],
],
]
],
],
]);
And got this error :
"parsing_exception","reason":"[range] malformed query, expected
[END_OBJECT] but found [FIELD_NAME]
I only found documentation and examples for ES 2 about combining queries, did something change ?
I want my query to match the fields, and be filtered according to filter.
Here is the right way to do this:
$items = $client->search([
'index' => $instance->getSearchIndex(),
'type' => $instance->getSearchType(),
'body' => [
'size' => 50,
'query' => [
'bool' => [
'must' => [
'multi_match' => [
'fields' => config('elasticsearch.fields'),
'query' => $query,
]
],
'filter' => [
[
'range' => [
'note' => [
'gte' => config('elasticsearch.note_minimum')
]
]
],
[
'term' => [
'type_video_id' => 5
]
]
]
]
]
]
]);
I don't have a way to test this, but I'm seeing a couple brackets where you need curlies. Also, don't you need you need $ before those "config"?
{
"query" => {
"bool" => {
"must" => [
{
"multi_match" => {
"query" => $query,
"fields" => $config('elasticsearch.fields')
}
}
],
"filter" => {
{
"range" => {
"note" => {
"gte" => $config('elasticsearch.note_minimum')
}
}
},
{
"term" => {
"type_video_id" => {
"value" => "5"
}
}
}
}
}
}
}
If this doesn't work, can you paste what the string looks like after your variables get rendered?

Elasticsearch query for simple category search, sorting by price and getting records by range

I want to write query with following condition :
search by category (like category = 'cat1')
and with price range (and price between 100 to 500)
and sort by price (low to high)
I tried:
$params = [
'index' => 'my_index',
'type' => 'product',
'body' => [
//"from" => 0, "size" => 2,
"sort" => [
["default_product_low_price.sale_price" => ["order" => "asc"]]
],
'query'=> $query,
"aggs" => [
"default_product_low_price" => [
"nested" => [
"path" => "default_product_low_price"
],
"aggs" => [
"range" => ["default_product_low_price.sale_price" => [ "gte" => '790',
"lte" => '1000' ]],
//"max_price" => ["max" => [ "field" => "default_product_low_price.sale_price" ]]
],
]
]
]
];
But I am getting an error
Bad Request 400 Exception in GuzzleConnection.php line 277: error.
Please guide me where I am wrong? What is the right query?
I think this should be your query :
$params = [
'index' => 'my_index',
'type' => 'product',
'body' => [
"sort" =>[
["default_product_low_price.sale_price" => ["order" => "asc"]]
],
"query"=> [
"filtered"=> [
"query"=> [
"match_all"=> []
],
"query"=>[
"term"=> [
"category.name"=> "jeans"
]
],
"filter"=> [
"nested"=> [
"path"=> "default_product_low_price",
"filter"=> [
"bool"=> [
"must"=> [
[
"range"=> [
"default_product_low_price.sale_price"=> [
"gte"=> 100,
"lte"=> 200,
]
]
]
]
]
]
]
]
]
]
]
];

elastic search limit results for types

i have the following query
$queryDefinition = [
'query' => [
'bool' => [
'must' => [
[
'query_string' => [
'default_field' => '_all',
'query' => $term
]
]
],
'must_not' => [],
'should' => []
],
],
//'filter' => [
// 'limit' => ['value' => 3],
//],
'from' => 0,
'size' => 50,
'sort' => [],
'facets' => [
'types' => [
'terms' => ['field' => '_type']
]
]
];
in the index we have 5 types, and i would like to show only 3 results from each type, for autocomplete. when i set the filter limit only the results from the first type are filtered, for other types i get all the results.
how can i do this?
thanks
{
"aggregations": {
"types": {
"terms": {
"field": "_type"
},
"hits": {
"top_hits": {
"size": 3
}
}
}
}
}
If you're using ElasticSearch 1.4.0, you can use terms aggregation on "_type" field and use top hits aggregation as sub aggregation and set its size to 3 (in your case).
Hope that helps.

Resources