Skip to content

Commit f5169c9

Browse files
authored
Merge pull request #44 from MacFJA/redisearch-compatibility
Redisearch compatibility
2 parents a56cf5f + bf556aa commit f5169c9

31 files changed

+1594
-11
lines changed

Diff for: CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Documentation on paginated responses
1212
- Throw custom exception is client is missing
1313
- Add compatibility for Predis version 2.x
14+
- `LOAD ALL` option on Aggregate command (RediSearch `2.0.13`)
15+
- Profile command (RediSearch `2.2.0`) ([Issue#4])
16+
- Vector feature (RediSearch `2.4.0` / `2.4.2`)
17+
- Add `DIALECT` option (RediSearch `2.4.3`) ([Issue#35])
1418
- (dev) Add `make clean` to remove all generated files
1519
- (dev) Add code coverage to `\MacFJA\RediSearch\Index`
1620
- (dev) Add integration test for document insertion
@@ -222,6 +226,7 @@ First version
222226
[1.0.0]: https://github.com/MacFJA/php-redisearch/releases/tag/1.0.0
223227

224228
[Issue#2]: https://github.com/MacFJA/php-redisearch/issues/2
229+
[Issue#4]: https://github.com/MacFJA/php-redisearch/issues/4
225230
[Issue#5]: https://github.com/MacFJA/php-redisearch/issues/5
226231
[Issue#6]: https://github.com/MacFJA/php-redisearch/issues/6
227232
[Issue#9]: https://github.com/MacFJA/php-redisearch/issues/9
@@ -230,6 +235,7 @@ First version
230235
[Issue#12]: https://github.com/MacFJA/php-redisearch/issues/12
231236
[Issue#16]: https://github.com/MacFJA/php-redisearch/issues/16
232237
[Issue#26]: https://github.com/MacFJA/php-redisearch/issues/26
238+
[Issue#35]: https://github.com/MacFJA/php-redisearch/issues/35
233239
[Issue#38]: https://github.com/MacFJA/php-redisearch/issues/38
234240
[Issue#39]: https://github.com/MacFJA/php-redisearch/issues/39
235241
[Issue#46]: https://github.com/MacFJA/php-redisearch/issues/46

Diff for: src/Query/Builder/QueryElementVector.php

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* Copyright MacFJA
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
9+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
10+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
11+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
14+
* Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
namespace MacFJA\RediSearch\Query\Builder;
23+
24+
use function array_key_exists;
25+
use function count;
26+
use function gettype;
27+
use function is_int;
28+
use function is_string;
29+
use function strlen;
30+
31+
use InvalidArgumentException;
32+
33+
class QueryElementVector implements QueryElementDecorator
34+
{
35+
private const PARAMETERS = [
36+
'EF_RUNTIME' => 'int',
37+
];
38+
39+
/** @var QueryElement */
40+
private $element;
41+
42+
/** @var int|string */
43+
private $number;
44+
45+
/** @var string */
46+
private $field;
47+
48+
/** @var string */
49+
private $blob;
50+
51+
/** @var null|string */
52+
private $scoreAlias;
53+
54+
/** @var array<string,int|string> */
55+
private $parameters = [];
56+
57+
/**
58+
* @param int|scalar|string $number
59+
*/
60+
public function __construct(QueryElement $element, $number, string $field, string $blob, ?string $scoreAlias = null)
61+
{
62+
if (!is_string($number) && !is_int($number)) {
63+
throw new InvalidArgumentException();
64+
}
65+
$this->element = $element;
66+
$this->number = $number;
67+
$this->field = $field;
68+
$this->blob = $blob;
69+
$this->scoreAlias = $scoreAlias;
70+
}
71+
72+
public function render(?callable $escaper = null): string
73+
{
74+
return sprintf(
75+
'(%s)=>[KNN %s @%s $%s%s%s]',
76+
$this->element->render($escaper),
77+
$this->getRenderedNumber(),
78+
$this->getRenderedField(),
79+
$this->getRenderedBlob(),
80+
$this->getRenderedAttributes(),
81+
$this->getRenderedAlias()
82+
);
83+
}
84+
85+
/**
86+
* @param int|string $value
87+
*
88+
* @return $this
89+
*/
90+
public function addParameter(string $name, $value): self
91+
{
92+
if (!array_key_exists($name, self::PARAMETERS)) {
93+
throw new InvalidArgumentException();
94+
}
95+
if (
96+
!(gettype($value) === self::PARAMETERS[$name])
97+
&& (is_string($value) && (strlen($value) < 2 || !(0 === strpos($value, '$'))))
98+
) {
99+
throw new InvalidArgumentException();
100+
}
101+
$this->parameters[$name] = $value;
102+
103+
return $this;
104+
}
105+
106+
public function priority(): int
107+
{
108+
return self::PRIORITY_BEFORE;
109+
}
110+
111+
public function includeSpace(): bool
112+
{
113+
return false;
114+
}
115+
116+
private function getRenderedNumber(): string
117+
{
118+
return is_string($this->number) ? ('$'.ltrim($this->number, '$')) : ((string) $this->number);
119+
}
120+
121+
private function getRenderedField(): string
122+
{
123+
return ltrim($this->field, '@');
124+
}
125+
126+
private function getRenderedBlob(): string
127+
{
128+
return ltrim($this->blob, '$');
129+
}
130+
131+
private function getRenderedAlias(): string
132+
{
133+
if (!is_string($this->scoreAlias)) {
134+
return '';
135+
}
136+
137+
return ' AS '.$this->scoreAlias;
138+
}
139+
140+
private function getRenderedAttributes(): string
141+
{
142+
if (0 === count($this->parameters)) {
143+
return '';
144+
}
145+
146+
return ' '.implode(' ', array_map(static function ($key, $value) {
147+
return $key.' '.$value;
148+
}, array_keys($this->parameters), array_values($this->parameters)));
149+
}
150+
}

Diff for: src/Redis/Command/AbstractCommand.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
*/
3434
abstract class AbstractCommand implements Command
3535
{
36-
public const MAX_IMPLEMENTED_VERSION = '2.0.12';
36+
public const MAX_IMPLEMENTED_VERSION = '2.4.3';
3737
public const MIN_IMPLEMENTED_VERSION = '2.0.0';
3838

3939
/**

Diff for: src/Redis/Command/AddFieldOptionTrait.php

+24
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use MacFJA\RediSearch\Redis\Command\CreateCommand\NumericFieldOption;
3131
use MacFJA\RediSearch\Redis\Command\CreateCommand\TagFieldOption;
3232
use MacFJA\RediSearch\Redis\Command\CreateCommand\TextFieldOption;
33+
use MacFJA\RediSearch\Redis\Command\CreateCommand\VectorFieldOption;
3334

3435
trait AddFieldOptionTrait
3536
{
@@ -77,6 +78,17 @@ public function addTagField(string $name, ?string $separator = null, bool $sorta
7778
);
7879
}
7980

81+
public function addVectorField(string $name, string $algorithm, string $type, int $dimension, string $distanceMetric, bool $noIndex = false): self
82+
{
83+
return $this->addField(
84+
(new VectorFieldOption())
85+
->setField($name)
86+
->setAlgorithm($algorithm)
87+
->addAttribute($type, $dimension, $distanceMetric)
88+
->setNoIndex($noIndex)
89+
);
90+
}
91+
8092
public function addJSONTextField(string $path, string $attribute, bool $noStem = false, ?float $weight = null, ?string $phonetic = null, bool $sortable = false, bool $noIndex = false): self
8193
{
8294
return $this->addJSONField(
@@ -125,6 +137,18 @@ public function addJSONTagField(string $path, string $attribute, ?string $separa
125137
);
126138
}
127139

140+
public function addJSONVectorField(string $path, string $attribute, string $algorithm, string $type, int $dimension, string $distanceMetric, bool $noIndex = false): self
141+
{
142+
return $this->addJSONField(
143+
$path,
144+
(new VectorFieldOption())
145+
->setField($attribute)
146+
->setAlgorithm($algorithm)
147+
->addAttribute($type, $dimension, $distanceMetric)
148+
->setNoIndex($noIndex)
149+
);
150+
}
151+
128152
public function addField(CreateCommandFieldOption $option): self
129153
{
130154
if (!($this instanceof AbstractCommand)) {

Diff for: src/Redis/Command/Aggregate.php

+19
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use MacFJA\RediSearch\Redis\Command\AggregateCommand\SortByOption;
3333
use MacFJA\RediSearch\Redis\Command\AggregateCommand\WithCursor;
3434
use MacFJA\RediSearch\Redis\Command\Option\CommandOption;
35+
use MacFJA\RediSearch\Redis\Command\Option\CustomValidatorOption as CV;
3536
use MacFJA\RediSearch\Redis\Command\Option\FlagOption;
3637
use MacFJA\RediSearch\Redis\Command\Option\NamedOption;
3738
use MacFJA\RediSearch\Redis\Command\Option\NamelessOption;
@@ -58,12 +59,14 @@ public function __construct(string $rediSearchVersion = self::MIN_IMPLEMENTED_VE
5859
'query' => new NamelessOption(null, '>=2.0.0'),
5960
'verbatim' => new FlagOption('VERBATIM', false, '>= 2.0.0'),
6061
'load' => new NumberedOption('LOAD', null, '>=2.0.0'),
62+
'loadall' => CV::allowedValues(new NamedOption('LOAD', null, '>=2.0.13'), ['ALL']),
6163
'groupby' => [],
6264
'sortby' => new SortByOption(),
6365
'apply' => [],
6466
'limit' => new LimitOption(),
6567
'filter' => [],
6668
'cursor' => new WithCursor(),
69+
'dialect' => CV::isNumeric(new NamedOption('DIALECT', null, '>=2.4.3')),
6770
], $rediSearchVersion);
6871
}
6972

@@ -157,6 +160,22 @@ public function setLoad(string ...$field): self
157160
return $this;
158161
}
159162

163+
public function setLoadAll(): self
164+
{
165+
$this->options['load']->setArguments(null);
166+
$this->options['loadall']->setValue('ALL');
167+
$this->lastAdded = $this->options['loadall'];
168+
169+
return $this;
170+
}
171+
172+
public function setDialect(int $version): self
173+
{
174+
$this->options['dialect']->setValue($version);
175+
176+
return $this;
177+
}
178+
160179
public function setWithCursor(?int $count = null, ?int $maxIdle = null): self
161180
{
162181
$this->options['cursor']

Diff for: src/Redis/Command/ConfigSet.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public function __construct(string $rediSearchVersion = self::MIN_IMPLEMENTED_VE
3333
'action' => new FlagOption('SET', true, '>=2.0.0'),
3434
'options' => [
3535
CustomValidatorOption::allowedValues(new NamelessOption(null, '>=2.0.0 <2.0.6'), ['NOGC', 'MAXEXPANSIONS', 'TIMEOUT', 'ON_TIMEOUT', 'MIN_PHONETIC_TERM_LEN']),
36-
CustomValidatorOption::allowedValues(new NamelessOption(null, '>=2.0.6'), ['NOGC', 'MINPREFIX', 'MAXEXPANSIONS', 'MAXFILTEREXPANSION', 'TIMEOUT', 'ON_TIMEOUT', 'MIN_PHONETIC_TERM_LEN']),
36+
CustomValidatorOption::allowedValues(new NamelessOption(null, '>=2.0.6 <2.4.3'), ['NOGC', 'MINPREFIX', 'MAXEXPANSIONS', 'MAXFILTEREXPANSION', 'TIMEOUT', 'ON_TIMEOUT', 'MIN_PHONETIC_TERM_LEN']),
37+
CustomValidatorOption::allowedValues(new NamelessOption(null, '>=2.4.3'), ['NOGC', 'MINPREFIX', 'MAXEXPANSIONS', 'TIMEOUT', 'ON_TIMEOUT', 'MIN_PHONETIC_TERM_LEN', 'DEFAULT_DIALECT']),
3738
],
3839
'value' => new NamelessOption(null, '>=2.0.0'),
3940
], $rediSearchVersion);

0 commit comments

Comments
 (0)