Skip to content

Commit e673de6

Browse files
authored
Merge pull request #55 from ensi-platform/v7-scripts
V7 script added
2 parents be81f20 + 69f30aa commit e673de6

File tree

10 files changed

+165
-1
lines changed

10 files changed

+165
-1
lines changed

src/Concerns/ExtendsSort.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Ensi\LaravelElasticQuery\Contracts\SortMode;
66
use Ensi\LaravelElasticQuery\Contracts\SortOrder;
7+
use Ensi\LaravelElasticQuery\Scripts\Script;
78

89
/**
910
* @psalm-require-implements \Ensi\LaravelElasticQuery\Contracts\SortableQuery
@@ -36,4 +37,22 @@ public function medianSortBy(string $field, string $order = SortOrder::ASC): sta
3637
{
3738
return $this->sortBy($field, $order, SortMode::MEDIAN);
3839
}
40+
41+
public function sortByCustomArray(string $field, array $items): static
42+
{
43+
$script = new Script(
44+
params: ['items' => $items],
45+
source: "
46+
for (int i = 0; i < params['items'].length; i++) {
47+
if (params['items'][i] == doc['{$field}'].value) {
48+
return i;
49+
}
50+
}
51+
52+
return params['items'].length;
53+
",
54+
);
55+
56+
return $this->sortByScript($script);
57+
}
3958
}

src/Contracts/ScriptLang.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Ensi\LaravelElasticQuery\Contracts;
4+
5+
final class ScriptLang
6+
{
7+
public const PAINLESS = 'painless';
8+
public const MUSTACHE = 'mustache';
9+
10+
public static function cases(): array
11+
{
12+
return [
13+
self::PAINLESS,
14+
self::MUSTACHE,
15+
];
16+
}
17+
}

src/Contracts/ScriptSortType.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Ensi\LaravelElasticQuery\Contracts;
4+
5+
final class ScriptSortType
6+
{
7+
public const NUMBER = 'number';
8+
9+
public static function cases(): array
10+
{
11+
return [
12+
self::NUMBER,
13+
];
14+
}
15+
}

src/Contracts/SortableQuery.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Ensi\LaravelElasticQuery\Contracts;
44

55
use Closure;
6+
use Ensi\LaravelElasticQuery\Scripts\Script;
67

78
interface SortableQuery extends BoolQuery
89
{
@@ -19,4 +20,8 @@ public function sumSortBy(string $field, string $order = SortOrder::ASC): static
1920
public function medianSortBy(string $field, string $order = SortOrder::ASC): static;
2021

2122
public function sortByNested(string $field, Closure $callback): static;
23+
24+
public function sortByScript(Script $script, string $type = ScriptSortType::NUMBER, string $order = SortOrder::ASC): static;
25+
26+
public function sortByCustomArray(string $field, array $items): static;
2227
}

src/Scripts/Script.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Ensi\LaravelElasticQuery\Scripts;
4+
5+
use Ensi\LaravelElasticQuery\Contracts\DSLAware;
6+
use Ensi\LaravelElasticQuery\Contracts\ScriptLang;
7+
use Webmozart\Assert\Assert;
8+
9+
class Script implements DSLAware
10+
{
11+
public function __construct(
12+
private string $lang = ScriptLang::PAINLESS,
13+
private array $params = [],
14+
private ?string $source = null,
15+
private ?string $id = null,
16+
) {
17+
Assert::oneOf($lang, ScriptLang::cases());
18+
Assert::notNull($source ?? $id);
19+
}
20+
21+
public function addParam(string $name, mixed $value): self
22+
{
23+
$this->params[$name] = $value;
24+
25+
return $this;
26+
}
27+
28+
public function toDSL(): array
29+
{
30+
$dsl = [
31+
'lang' => $this->lang,
32+
'params' => $this->params,
33+
];
34+
35+
if ($this->source) {
36+
$dsl['source'] = $this->source;
37+
} elseif ($this->id) {
38+
$dsl['id'] = $this->id;
39+
}
40+
41+
return $dsl;
42+
}
43+
}

src/Search/SearchQuery.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
use Ensi\LaravelElasticQuery\Concerns\ExtendsSort;
99
use Ensi\LaravelElasticQuery\Contracts\Aggregation;
1010
use Ensi\LaravelElasticQuery\Contracts\CollapsibleQuery;
11+
use Ensi\LaravelElasticQuery\Contracts\ScriptSortType;
1112
use Ensi\LaravelElasticQuery\Contracts\SearchIndex;
1213
use Ensi\LaravelElasticQuery\Contracts\SortableQuery;
1314
use Ensi\LaravelElasticQuery\Contracts\SortOrder;
1415
use Ensi\LaravelElasticQuery\Filtering\BoolQueryBuilder;
16+
use Ensi\LaravelElasticQuery\Scripts\Script;
1517
use Ensi\LaravelElasticQuery\Search\Collapsing\Collapse;
1618
use Ensi\LaravelElasticQuery\Search\Sorting\SortBuilder;
1719
use Ensi\LaravelElasticQuery\Search\Sorting\SortCollection;
@@ -177,6 +179,14 @@ public function sortBy(string $field, string $order = SortOrder::ASC, ?string $m
177179
return $this;
178180
}
179181

182+
public function sortByScript(Script $script, string $type = ScriptSortType::NUMBER, string $order = SortOrder::ASC): static
183+
{
184+
(new SortBuilder($this->sorts))
185+
->sortByScript($script, $type, $order);
186+
187+
return $this;
188+
}
189+
180190
public function sortByNested(string $field, Closure $callback): static
181191
{
182192
(new SortBuilder($this->sorts))->sortByNested($field, $callback);

src/Search/Sorting/Sort.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Ensi\LaravelElasticQuery\Contracts\MissingValuesMode;
77
use Ensi\LaravelElasticQuery\Contracts\SortMode;
88
use Ensi\LaravelElasticQuery\Contracts\SortOrder;
9+
use Ensi\LaravelElasticQuery\Scripts\Script;
910
use Webmozart\Assert\Assert;
1011

1112
class Sort implements DSLAware
@@ -15,7 +16,9 @@ public function __construct(
1516
private string $order = SortOrder::ASC,
1617
private ?string $mode = null,
1718
private ?NestedSort $nested = null,
18-
private ?string $missingValues = null
19+
private ?string $missingValues = null,
20+
private ?string $type = null,
21+
private ?Script $script = null,
1922
) {
2023
Assert::stringNotEmpty(trim($field));
2124
Assert::oneOf($order, SortOrder::cases());
@@ -43,6 +46,14 @@ public function toDSL(): array
4346
$details['missing'] = $this->missingValues;
4447
}
4548

49+
if ($this->type !== null) {
50+
$details['type'] = $this->type;
51+
}
52+
53+
if ($this->script !== null) {
54+
$details['script'] = $this->script->toDSL();
55+
}
56+
4657
if (!$details) {
4758
return [$this->field => $this->order];
4859
}

src/Search/Sorting/SortBuilder.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
use Ensi\LaravelElasticQuery\Concerns\DecoratesBoolQuery;
77
use Ensi\LaravelElasticQuery\Concerns\ExtendsSort;
88
use Ensi\LaravelElasticQuery\Concerns\SupportsPath;
9+
use Ensi\LaravelElasticQuery\Contracts\ScriptSortType;
910
use Ensi\LaravelElasticQuery\Contracts\SortableQuery;
1011
use Ensi\LaravelElasticQuery\Contracts\SortOrder;
1112
use Ensi\LaravelElasticQuery\Filtering\BoolQueryBuilder;
13+
use Ensi\LaravelElasticQuery\Scripts\Script;
1214
use Illuminate\Support\Collection;
1315

1416
class SortBuilder implements SortableQuery
@@ -43,6 +45,20 @@ public function sortBy(string $field, string $order = SortOrder::ASC, ?string $m
4345
return $this;
4446
}
4547

48+
public function sortByScript(Script $script, string $type = ScriptSortType::NUMBER, string $order = SortOrder::ASC): static
49+
{
50+
$sort = new Sort(
51+
field: '_script',
52+
order: $order,
53+
type: $type,
54+
script: $script,
55+
);
56+
57+
$this->sorts->add($sort);
58+
59+
return $this;
60+
}
61+
4662
public function sortByNested(string $field, Closure $callback): static
4763
{
4864
$path = $this->absolutePath($field);

tests/Functional/Search/SearchQueryTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,26 @@ public function testSortBy(): void
4949
$this->assertDocumentIds([1, 150, 319]);
5050
}
5151

52+
/**
53+
* @dataProvider providerSortByCustomArray
54+
*/
55+
public function testSortByCustomArray(array $items, array $documents): void
56+
{
57+
$this->testing->sortByCustomArray('product_id', $items)->take(3);
58+
59+
$this->assertDocumentIds($documents);
60+
}
61+
62+
public static function providerSortByCustomArray(): array
63+
{
64+
return [
65+
'all_first' => [[150, 1, 319], [150, 1, 319]],
66+
'all_second' => [[319, 150, 1], [319, 150, 1]],
67+
'extra' => [[319, 150, 1, 328], [319, 150, 1]],
68+
'mixed' => [[123456789, 319, 150], [319, 150, 1]],
69+
];
70+
}
71+
5272
public function testSelect(): void
5373
{
5474
$this->testing->select(['product_id'])->take(1);

tests/Unit/Search/Sorting/SortBuilderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ public function testSortBy(): void
3232
$this->assertArrayStructure([['code'], ['name']], $this->buildDSL());
3333
}
3434

35+
public function testSortByCustomArray(): void
36+
{
37+
$this->testing
38+
->sortByCustomArray('product_id', [2, 3, 1]);
39+
40+
$this->assertArrayStructure([['_script' => ['type', 'script', 'order']]], $this->buildDSL());
41+
}
42+
3543
public function testSortByNested(): void
3644
{
3745
$this->testing

0 commit comments

Comments
 (0)