Skip to content

Commit a42e2dd

Browse files
Merge pull request #1462 from elastic/esql-query-builder
ES|QL query builder
2 parents 999a05c + 9e24bec commit a42e2dd

31 files changed

+2836
-0
lines changed

docs/reference/esql.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ There are two ways to use ES|QL in the PHP client:
1414
* Use the Elasticsearch [ES|QL API](https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-esql) directly: This is the most flexible approach, but it’s also the most complex because you must handle results in their raw form. You can choose the precise format of results, such as JSON, CSV, or text.
1515
* Use ES|QL `mapTo($class)` helper. This mapper takes care of parsing the raw response and converting into an array of objects. If you don’t specify the class using the `$class` parameter, the mapper uses [stdClass](https://www.php.net/manual/en/class.stdclass.php).
1616

17+
ES|QL queries can be given directly as regular strings or heredoc strings, or for a more convenient option you can use the [ES|QL query builder](#esql-query-builder) helper, which can define ES|QL queries using PHP code.
1718

1819
## How to use the ES|QL API [esql-how-to]
1920

@@ -206,3 +207,71 @@ $result = $client->esql()->query([
206207
$books = $result->mapTo(Book::class); // Array of Book
207208
```
208209

210+
## Using the ES|QL query builder [esql-query-builder]
211+
212+
::::{warning}
213+
This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
214+
::::
215+
216+
The ES|QL query builder allows you to construct ES|QL queries using PHP syntax. Consider the following example:
217+
218+
```php
219+
use Elastic\Elasticsearch\Helper\Esql\Query;
220+
221+
$query = Query::from("employees")
222+
->sort("emp_no")
223+
->keep("first_name", "last_name", "height")
224+
->eval(height_feet: "height * 3.281", height_cm: "height * 100")
225+
->limit(3);
226+
```
227+
228+
Casting this query object to a string returns the raw ES|QL query, which you can send to the Elasticsearch ES|QL API:
229+
230+
```php
231+
$result = $client->esql()->query([
232+
'body' => ['query' => (string)$query]
233+
]);
234+
```
235+
236+
### Creating an ES|QL query
237+
238+
To construct an ES|QL query object you typically use `Query::from()`, although there are more [source commands](https://www.elastic.co/docs/reference/query-languages/esql/commands/source-commands) available. Here are some examples:
239+
240+
```php
241+
// FROM employees
242+
$query = Query::from("employees");
243+
244+
// FROM <logs-{now/d}>
245+
$query = Query::from("<logs-{now/d}>");
246+
247+
// FROM employees-00001, other-employees-*
248+
$query = Query::from("employees-00001", "other-employees-*");
249+
250+
// FROM cluster_one:employees-00001, cluster_two:other-employees-*
251+
$query = Query::from("cluster_one:employees-00001", "cluster_two:other-employees-*");
252+
253+
// FROM employees METADATA _id
254+
$query = Query::from("employees")->metadata("_id");
255+
```
256+
257+
Note how in the last example the optional `METADATA` clause of the `FROM` command is added as a chained method.
258+
259+
### Adding processing commands
260+
261+
Once you have a query object, you can add one or more processing commands to it by chaining them. The following example shows how to create a query that uses the `WHERE` and `LIMIT` processing commands to filter the results:
262+
263+
```php
264+
$query = Query::from("employees")
265+
->where("still_hired == true")
266+
->limit(10);
267+
```
268+
269+
The ES|QL documentation includes the complete list of available [processing commands](https://www.elastic.co/docs/reference/query-languages/esql/commands/processing-commands).
270+
271+
### Using Code completion
272+
273+
You can rely on autocompletion as an aid in constructing ES|QL queries with the query builder. Note that this requires an IDE that is configured with a PHP language server.
274+
275+
![Autocompleting Query::](images/autocompleting-query.png)
276+
277+
![Autocompleting from()](images/autocompleting-from.png)
213 KB
Loading
108 KB
Loading

src/Helper/Esql/Branch.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Esql;
16+
17+
/**
18+
* Implementation of a branch inside a `FORK` processing command.
19+
*
20+
* This class inherits from EsqlBase to make it possible to chain all the commands
21+
* that belong to an ES|QL query in a single expression.
22+
*/
23+
class Branch extends EsqlBase {
24+
public function __construct()
25+
{
26+
parent::__construct(null);
27+
}
28+
29+
protected function renderInternal(): string
30+
{
31+
return "";
32+
}
33+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Esql;
16+
17+
/**
18+
* Implementation of the `CHANGE_POINT` processing command.
19+
*
20+
* This class inherits from EsqlBase to make it possible to chain all the commands
21+
* that belong to an ES|QL query in a single expression.
22+
*/
23+
class ChangePointCommand extends EsqlBase {
24+
private string $value;
25+
private string $key = "";
26+
private string $type_name = "";
27+
private string $pvalue_name = "";
28+
29+
public function __construct(EsqlBase $previous_command, string $value)
30+
{
31+
parent::__construct($previous_command);
32+
$this->value = $value;
33+
}
34+
35+
/**
36+
* Continuation of the `CHANGE_POINT` command.
37+
*
38+
* @param string $key The column with the key to order the values by. If not
39+
* specified, `@timestamp` is used.
40+
*/
41+
public function on(string $key): ChangePointCommand
42+
{
43+
$this->key = $key;
44+
return $this;
45+
}
46+
47+
/**
48+
* Continuation of the `CHANGE_POINT` command.
49+
*
50+
* @param string $type_name The name of the output column with the change
51+
* point type. If not specified, `type` is used.
52+
* @param string $pvalue_name The name of the output column with the p-value
53+
* that indicates how extreme the change point is.
54+
* If not specified, `pvalue` is used.
55+
*/
56+
public function as(string $type_name, string $pvalue_name): ChangePointCommand
57+
{
58+
$this->type_name = $type_name;
59+
$this->pvalue_name = $pvalue_name;
60+
return $this;
61+
}
62+
63+
protected function renderInternal(): string
64+
{
65+
$key = $this->key ? " ON " . $this->formatId($this->key) : "";
66+
$names = ($this->type_name && $this->pvalue_name) ?
67+
" AS " . $this->formatId($this->type_name) . ", " .
68+
$this->formatId($this->pvalue_name)
69+
: "";
70+
return "CHANGE_POINT " . $this->value . $key . $names;
71+
}
72+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Esql;
16+
17+
use RuntimeException;
18+
19+
/**
20+
* Implementation of the `COMPLETION` processing command.
21+
*
22+
* This class inherits from EsqlBase to make it possible to chain all the commands
23+
* that belong to an ES|QL query in a single expression.
24+
*/
25+
class CompletionCommand extends EsqlBase {
26+
private string $prompt;
27+
private array $named_prompt = [];
28+
private string $inference_id = "";
29+
30+
public function __construct(EsqlBase $previous_command, array $prompt)
31+
{
32+
if (sizeof($prompt) != 1) {
33+
throw new RuntimeException("Only one prompt is supported");
34+
}
35+
parent::__construct($previous_command);
36+
if ($this->isNamedArgumentList($prompt)) {
37+
$this->named_prompt = $prompt;
38+
}
39+
else {
40+
$this->prompt = $prompt[0];
41+
}
42+
}
43+
44+
/**
45+
* Continuation of the `COMPLETION` command.
46+
*
47+
* @param string $inference_id The ID of the inference endpoint to use for
48+
* the task. The inference endpoint must be
49+
* configured with the `completion` task type.
50+
*/
51+
public function with(string $inference_id): CompletionCommand
52+
{
53+
$this->inference_id = $inference_id;
54+
return $this;
55+
}
56+
57+
protected function renderInternal(): string
58+
{
59+
if (!$this->inference_id) {
60+
throw new RuntimeException("The completion command requires an inference ID");
61+
}
62+
$with = ["inference_id" => $this->inference_id];
63+
if ($this->named_prompt) {
64+
return "COMPLETION " .
65+
$this->formatId(array_keys($this->named_prompt)[0]) . " = " .
66+
$this->formatId(array_values($this->named_prompt)[0]) .
67+
" WITH " . json_encode($with);
68+
}
69+
else {
70+
return "COMPLETION " . $this->formatId($this->prompt) .
71+
" WITH " . json_encode($with);
72+
}
73+
}
74+
}

src/Helper/Esql/DissectCommand.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Esql;
16+
17+
/**
18+
* Implementation of the `DISSECT` processing command.
19+
*
20+
* This class inherits from EsqlBase to make it possible to chain all the commands
21+
* that belong to an ES|QL query in a single expression.
22+
*/
23+
class DissectCommand extends EsqlBase {
24+
private string $input;
25+
private string $pattern;
26+
private string $separator = "";
27+
28+
public function __construct(EsqlBase $previous_command, string $input, string $pattern)
29+
{
30+
parent::__construct($previous_command);
31+
$this->input = $input;
32+
$this->pattern = $pattern;
33+
}
34+
35+
/**
36+
* Continuation of the `DISSECT` command.
37+
*
38+
* @param string $separator A string used as the separator between appended
39+
* values, when using the append modifier.
40+
*/
41+
public function append_separator(string $separator): DissectCommand
42+
{
43+
$this->separator = $separator;
44+
return $this;
45+
}
46+
47+
protected function renderInternal(): string
48+
{
49+
$sep = $this->separator ? " APPEND_SEPARATOR=" . json_encode($this->separator) : "";
50+
return "DISSECT " . $this->formatId($this->input) . " " . json_encode($this->pattern) . $sep;
51+
}
52+
}

src/Helper/Esql/DropCommand.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Helper\Esql;
16+
17+
/**
18+
* Implementation of the `DROP` processing command.
19+
*
20+
* This class inherits from EsqlBase to make it possible to chain all the commands
21+
* that belong to an ES|QL query in a single expression.
22+
*/
23+
class DropCommand extends EsqlBase {
24+
private array $columns;
25+
26+
public function __construct(EsqlBase $previous_command, array $columns)
27+
{
28+
parent::__construct($previous_command);
29+
$this->columns = $columns;
30+
}
31+
32+
protected function renderInternal(): string
33+
{
34+
return "DROP " . implode(
35+
", ", array_map(array($this, "formatId"), $this->columns)
36+
);
37+
}
38+
}

0 commit comments

Comments
 (0)