Skip to content

Commit 8b42e84

Browse files
Spamerczclaude
andcommitted
feat(query): add rank feature query
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6e24431 commit 8b42e84

3 files changed

Lines changed: 128 additions & 0 deletions

File tree

doc/02-query-objects.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,19 @@ new \Spameri\ElasticQuery\Query\MoreLikeThis(
519519
);
520520
```
521521

522+
##### RankFeature Query
523+
Boost documents by a `rank_feature` field (saturation/log/sigmoid/linear functions).
524+
- Class: `\Spameri\ElasticQuery\Query\RankFeature`
525+
- [Documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-rank-feature-query.html)
526+
- [Implementation](https://github.com/Spameri/ElasticQuery/blob/master/src/Query/RankFeature.php)
527+
528+
```php
529+
new \Spameri\ElasticQuery\Query\RankFeature(
530+
field: 'pagerank',
531+
function: ['saturation' => ['pivot' => 8]],
532+
);
533+
```
534+
522535
##### Script Query
523536
Filter documents with a Painless boolean script.
524537
- Class: `\Spameri\ElasticQuery\Query\Script`

src/Query/RankFeature.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Spameri\ElasticQuery\Query;
6+
7+
/**
8+
* @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-rank-feature-query.html
9+
*/
10+
class RankFeature implements \Spameri\ElasticQuery\Query\LeafQueryInterface
11+
{
12+
13+
/**
14+
* @param array<string, mixed>|null $function Optional scoring function:
15+
* ['saturation' => ['pivot' => 8]] | ['log' => ['scaling_factor' => 4]]
16+
* | ['sigmoid' => ['pivot' => 7, 'exponent' => 0.6]] | ['linear' => new \stdClass()].
17+
*/
18+
public function __construct(
19+
private string $field,
20+
private array|null $function = null,
21+
private float $boost = 1.0,
22+
)
23+
{
24+
}
25+
26+
27+
public function key(): string
28+
{
29+
return 'rank_feature_' . $this->field;
30+
}
31+
32+
33+
/**
34+
* @return array<string, array<string, mixed>>
35+
*/
36+
public function toArray(): array
37+
{
38+
$body = [
39+
'field' => $this->field,
40+
'boost' => $this->boost,
41+
];
42+
43+
if ($this->function !== null) {
44+
foreach ($this->function as $name => $config) {
45+
$body[$name] = $config;
46+
}
47+
}
48+
49+
return [
50+
'rank_feature' => $body,
51+
];
52+
}
53+
54+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace SpameriTests\ElasticQuery\Query;
4+
5+
require_once __DIR__ . '/../../bootstrap.php';
6+
7+
8+
class RankFeature extends \Tester\TestCase
9+
{
10+
11+
private const INDEX = 'spameri_test_query_rank_feature';
12+
13+
14+
public function setUp(): void
15+
{
16+
$ch = \curl_init();
17+
\curl_setopt($ch, \CURLOPT_URL, \ELASTICSEARCH_HOST . '/' . self::INDEX);
18+
\curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1);
19+
\curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, 'PUT');
20+
\curl_setopt($ch, \CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
21+
22+
\curl_exec($ch);
23+
}
24+
25+
26+
public function testToArray(): void
27+
{
28+
$rf = new \Spameri\ElasticQuery\Query\RankFeature(
29+
field: 'pagerank',
30+
function: ['saturation' => ['pivot' => 8]],
31+
);
32+
33+
$array = $rf->toArray();
34+
35+
\Tester\Assert::same('pagerank', $array['rank_feature']['field']);
36+
\Tester\Assert::same(8, $array['rank_feature']['saturation']['pivot']);
37+
}
38+
39+
40+
public function testKey(): void
41+
{
42+
$rf = new \Spameri\ElasticQuery\Query\RankFeature('pagerank');
43+
44+
\Tester\Assert::same('rank_feature_pagerank', $rf->key());
45+
}
46+
47+
48+
public function tearDown(): void
49+
{
50+
$ch = \curl_init();
51+
\curl_setopt($ch, \CURLOPT_URL, \ELASTICSEARCH_HOST . '/' . self::INDEX);
52+
\curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1);
53+
\curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, 'DELETE');
54+
\curl_setopt($ch, \CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
55+
56+
\curl_exec($ch);
57+
}
58+
59+
}
60+
61+
(new RankFeature())->run();

0 commit comments

Comments
 (0)