Skip to content

Commit 097855a

Browse files
authored
Fix #311: Added support for runtime mappings in Elasticsearch 7.11+
1 parent 1324bc1 commit 097855a

File tree

6 files changed

+133
-3
lines changed

6 files changed

+133
-3
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
- "7.4"
2626

2727
es-version:
28+
- "7.14.0"
2829
- "7.7.0"
2930
- "6.8.9"
3031
- "5.6.16"
@@ -67,7 +68,7 @@ jobs:
6768
- name: Get composer cache directory
6869
id: composer-cache
6970
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
70-
71+
7172
- name: Cache dependencies installed with composer
7273
uses: actions/cache@v1
7374
with:

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Yii Framework 2 Elasticsearch extension Change Log
44
2.1.3 under development
55
-----------------------
66

7-
- no changes in this release.
7+
- Enh #311: Added support for runtime mappings in Elasticsearch 7.11+ (mabentley85)
88

99

1010
2.1.2 August 09, 2021

Query.php

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,38 @@ class Query extends Component implements QueryInterface
103103
* @see source
104104
*/
105105
public $scriptFields;
106+
/**
107+
* @var array An array of runtime fields evaluated at query time
108+
* Example:
109+
* ```php
110+
* $query->$runtimeMappings = [
111+
* 'value_times_two' => [
112+
* 'type' => 'double',
113+
* 'script' => "emit(doc['my_field_name'].value * 2)",
114+
* ],
115+
* 'value_times_factor' => [
116+
* 'type' => 'double',
117+
* 'script' => "emit(doc['my_field_name'].value * factor)",
118+
* 'params' => [
119+
* 'factor' => 2.0
120+
* ],
121+
* ],
122+
* ]
123+
* ```
124+
*
125+
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html
126+
* @see runtimeMappings()
127+
* @see source
128+
*/
129+
public $runtimeMappings;
130+
/**
131+
* @var array Use the fields parameter to retrieve the values of runtime fields. Runtime fields won’t display in
132+
* _source, but the fields API works for all fields, even those that were not sent as part of the original _source.
133+
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-retrieving-fields.html
134+
* @see fields()
135+
* @see fields
136+
*/
137+
public $fields;
106138
/**
107139
* @var array this option controls how the `_source` field is returned from
108140
* the documents. For example, `['id', 'name']` means that only the `id`
@@ -450,7 +482,7 @@ public function count($q = '*', $db = null)
450482
if ($this->emulateExecution) {
451483
return 0;
452484
}
453-
485+
454486
$command = $this->createCommand($db);
455487

456488
// performing a query with return size of 0, is equal to getting result stats such as count
@@ -732,6 +764,38 @@ public function scriptFields($fields)
732764
return $this;
733765
}
734766

767+
/**
768+
* Sets the runtime mappings for this query
769+
* @param $mappings
770+
* @return $this the query object itself
771+
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html
772+
*/
773+
public function runtimeMappings($mappings)
774+
{
775+
if (is_array($mappings) || $mappings === null) {
776+
$this->runtimeMappings = $mappings;
777+
} else {
778+
$this->runtimeMappings = func_get_args();
779+
}
780+
return $this;
781+
}
782+
783+
/**
784+
* Sets the runtime fields to retrieve from the documents.
785+
* @param array $fields the fields to be selected.
786+
* @return $this the query object itself
787+
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-retrieving-fields.html
788+
*/
789+
public function fields($fields)
790+
{
791+
if (is_array($fields) || $fields === null) {
792+
$this->fields = $fields;
793+
} else {
794+
$this->fields = func_get_args();
795+
}
796+
return $this;
797+
}
798+
735799
/**
736800
* Sets the source filtering, specifying how the `_source` field of the
737801
* document should be returned.

QueryBuilder.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ public function build($query)
5353
if ($query->scriptFields !== null) {
5454
$parts['script_fields'] = $query->scriptFields;
5555
}
56+
if ($query->runtimeMappings !== null) {
57+
$parts['runtime_mappings'] = $query->runtimeMappings;
58+
}
59+
if ($query->fields !== null) {
60+
$parts['fields'] = $query->fields;
61+
}
5662

5763
if ($query->source !== null) {
5864
$parts['_source'] = $query->source;

docs/guide/usage-query.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,30 @@ $query->from('customer');
9494
// Note the literal string 'true', not a boolean value!
9595
$query->addOptions(['track_total_hits' => 'true']);
9696
```
97+
98+
## Runtime Fields/Mappings in ES >= 7.11
99+
100+
Runtime Fields are fields that can be dynamically generated at query time by supplying a script similar to `script_fields`.
101+
The major difference being that the value of a Runtime Field can be used in search queries, aggregations, filtering, and
102+
sorting.
103+
104+
Any Runtime Field values that you want to be included in the search results must be added to the `field` array by passing
105+
an array of field names using the `fields()` method.
106+
107+
Example for fetching users' full names by concatenating the `first_name` and `last_name` fields from the index and
108+
sorting them alphabetically.
109+
```php
110+
$results = (new yii\elasticsearch\Query())
111+
->from('users')
112+
->runtimeMappings([
113+
'full_name' => [
114+
'type' => 'keyword',
115+
'script' => "emit(doc['first_name'].value + ' ' + doc['last_name'].value)",
116+
],
117+
])
118+
->fields(['full_name'])
119+
->orderBy(['full_name' => SORT_ASC])
120+
->search($connection);
121+
```
122+
123+
For more information concerning `type` and `script` please see [Elastic's Runtime Field Documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html)

tests/QueryTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,4 +451,36 @@ public function testSuggest()
451451

452452
$this->assertCount(5, $result['customer_name'][0]['options']);
453453
}
454+
455+
public function testRuntimeMappings()
456+
{
457+
// Check that Elasticsearch is version 7.11.0 or later before running this test
458+
$elasticsearchInfo = $this->getConnection()->get('/');
459+
if(!version_compare($elasticsearchInfo['version']['number'], '7.11.0', '>=')) {
460+
return;
461+
}
462+
463+
$query = new Query();
464+
$query->from('query-test', 'user');
465+
466+
$query->runtimeMappings([
467+
'name_email' => [
468+
'type' => 'keyword',
469+
'script' => "emit(doc['name'].value + ':' + doc['email'].value)",
470+
],
471+
]);
472+
$this->assertEquals([
473+
'name_email' => [
474+
'type' => 'keyword',
475+
'script' => "emit(doc['name'].value + ':' + doc['email'].value)",
476+
],
477+
], $query->runtimeMappings);
478+
479+
$query->fields(['name_email']);
480+
$this->assertEquals(['name_email'], $query->fields);
481+
482+
$result = $query->search($this->getConnection());
483+
$this->assertArrayHasKey('name_email', $result['hits']['hits'][0]['fields']);
484+
$this->assertEquals($result['hits']['hits'][0]['fields']['name_email'][0], 'user1:user1@example.com');
485+
}
454486
}

0 commit comments

Comments
 (0)