Skip to content

Commit 8f80c08

Browse files
authored
Merge pull request #23 from MacFJA/issue-22-json-support
Add support for JSON document
2 parents fd3bff9 + bb056d8 commit 8f80c08

12 files changed

+418
-6
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased v2.x]
88

9+
### Added
10+
11+
- Support from JSON document (require RediSearch 2.2)
12+
13+
### Fixed
14+
15+
- (dev) Code coverage with XDebug 3
16+
917
## [2.0.2]
1018

1119
### Added

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@ test-with-integration: | vendor
2323

2424
coverage: | vendor
2525
@if [ -z "`php -v | grep -i 'xdebug'`" ]; then echo "You need to install Xdebug in order to do this action"; exit 1; fi
26-
$(COMPOSER) exec -v phpunit -- --coverage-text --color
26+
XDEBUG_MODE=coverage $(COMPOSER) exec -v phpunit -- --coverage-text --color
2727

2828
coverage-with-integration: | vendor
2929
@if [ -z "`php -v | grep -i 'xdebug'`" ]; then echo "You need to install Xdebug in order to do this action"; exit 1; fi
30-
$(COMPOSER) exec -v phpunit -- --group default,integration --coverage-text --color
30+
XDEBUG_MODE=coverage $(COMPOSER) exec -v phpunit -- --group default,integration --coverage-text --color
3131

3232
integration-test: | vendor
3333
$(COMPOSER) exec -v phpunit -- --group integration
3434

3535
integration-coverage: | vendor
3636
@if [ -z "`php -v | grep -i 'xdebug'`" ]; then echo "You need to install Xdebug in order to do this action"; exit 1; fi
37-
$(COMPOSER) exec -v phpunit -- --group integration --coverage-text --color
37+
XDEBUG_MODE=coverage $(COMPOSER) exec -v phpunit -- --group integration --coverage-text --color
3838

3939
validation: fix-code analyze test-with-integration coverage-with-integration
4040

src/IndexBuilder.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use MacFJA\RediSearch\Redis\Command\AddFieldOptionTrait;
3535
use MacFJA\RediSearch\Redis\Command\Create;
3636
use MacFJA\RediSearch\Redis\Command\CreateCommand\CreateCommandFieldOption;
37+
use MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption;
3738
use RuntimeException;
3839
use function strlen;
3940

@@ -84,6 +85,10 @@
8485
* @method IndexBuilder withAddedNumericField(string $name, bool $sortable = false, bool $noIndex = false)
8586
* @method IndexBuilder withAddedGeoField(string $name, bool $noIndex = false)
8687
* @method IndexBuilder withAddedTagField(string $name, ?string $separator = null, bool $sortable = false, bool $noIndex = false)
88+
* @method IndexBuilder withAddedJSONTextField(string $path, string $attribute, bool $noStem = false, ?float $weight = null, ?string $phonetic = null, bool $sortable = false, bool $noIndex = false)
89+
* @method IndexBuilder withAddedJSONNumericField(string $path, string $attribute, bool $sortable = false, bool $noIndex = false)
90+
* @method IndexBuilder withAddedJSONGeoField(string $path, string $attribute, bool $noIndex = false)
91+
* @method IndexBuilder withAddedJSONTagField(string $path, string $attribute, ?string $separator = null, bool $sortable = false, bool $noIndex = false)
8792
*
8893
* @SuppressWarnings(PHPMD.TooManyFields)
8994
*/
@@ -185,6 +190,13 @@ public function addField(CreateCommandFieldOption $option): self
185190
return $this;
186191
}
187192

193+
public function addJSONField(string $path, CreateCommandFieldOption $option): self
194+
{
195+
$this->fields[$option->getFieldName()] = new JSONFieldOption($path, $option);
196+
197+
return $this;
198+
}
199+
188200
/**
189201
* @return mixed|string
190202
*/

src/Redis/Command/AddFieldOptionTrait.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use function get_class;
2626
use MacFJA\RediSearch\Redis\Command\CreateCommand\CreateCommandFieldOption;
2727
use MacFJA\RediSearch\Redis\Command\CreateCommand\GeoFieldOption;
28+
use MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption;
2829
use MacFJA\RediSearch\Redis\Command\CreateCommand\NumericFieldOption;
2930
use MacFJA\RediSearch\Redis\Command\CreateCommand\TagFieldOption;
3031
use MacFJA\RediSearch\Redis\Command\CreateCommand\TextFieldOption;
@@ -75,6 +76,54 @@ public function addTagField(string $name, ?string $separator = null, bool $sorta
7576
);
7677
}
7778

79+
public function addJSONTextField(string $path, string $attribute, bool $noStem = false, ?float $weight = null, ?string $phonetic = null, bool $sortable = false, bool $noIndex = false): self
80+
{
81+
return $this->addJSONField(
82+
$path,
83+
(new TextFieldOption())
84+
->setField($attribute)
85+
->setNoStem($noStem)
86+
->setWeight($weight)
87+
->setPhonetic($phonetic)
88+
->setSortable($sortable)
89+
->setNoIndex($noIndex)
90+
);
91+
}
92+
93+
public function addJSONNumericField(string $path, string $attribute, bool $sortable = false, bool $noIndex = false): self
94+
{
95+
return $this->addJSONField(
96+
$path,
97+
(new NumericFieldOption())
98+
->setField($attribute)
99+
->setSortable($sortable)
100+
->setNoIndex($noIndex)
101+
);
102+
}
103+
104+
public function addJSONGeoField(string $path, string $attribute, bool $noIndex = false): self
105+
{
106+
return $this->addJSONField(
107+
$path,
108+
(new GeoFieldOption())
109+
->setField($attribute)
110+
->setNoIndex($noIndex)
111+
);
112+
}
113+
114+
public function addJSONTagField(string $path, string $attribute, ?string $separator = null, bool $sortable = false, bool $noIndex = false, bool $caseSensitive = false): self
115+
{
116+
return $this->addJSONField(
117+
$path,
118+
(new TagFieldOption())
119+
->setField($attribute)
120+
->setSeparator($separator)
121+
->setCaseSensitive($caseSensitive)
122+
->setSortable($sortable)
123+
->setNoIndex($noIndex)
124+
);
125+
}
126+
78127
public function addField(CreateCommandFieldOption $option): self
79128
{
80129
if (!($this instanceof AbstractCommand)) {
@@ -85,4 +134,15 @@ public function addField(CreateCommandFieldOption $option): self
85134

86135
return $this;
87136
}
137+
138+
public function addJSONField(string $path, CreateCommandFieldOption $option): self
139+
{
140+
if (!($this instanceof AbstractCommand)) {
141+
throw new BadMethodCallException('This method is not callable in '.get_class($this));
142+
}
143+
144+
$this->options['fields'][$option->getFieldName()] = new JSONFieldOption($path, $option);
145+
146+
return $this;
147+
}
88148
}

src/Redis/Command/Create.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ public function __construct(string $rediSearchVersion = self::MIN_IMPLEMENTED_VE
3737
{
3838
parent::__construct([
3939
'index' => new NamelessOption(null, '>=2.0.0'),
40-
'structure' => CV::allowedValues(new NamedOption('ON', null, '>=2.0.0'), ['HASH']),
40+
'structure' => [
41+
CV::allowedValues(new NamedOption('ON', null, '>=2.0.0 <2.2.0'), ['HASH']),
42+
CV::allowedValues(new NamedOption('ON', null, '>=2.2.0'), ['HASH', 'JSON']),
43+
],
4144
'prefixes' => new NotEmptyOption(new NumberedOption('PREFIX', null, '>=2.0.0')),
4245
'filter' => new NamedOption('FILTER', null, '>=2.0.0'),
4346
'default_lang' => $this->getLanguageOptions(),
@@ -67,7 +70,9 @@ public function setIndex(string $index): self
6770

6871
public function setStructure(string $structureType = 'HASH'): self
6972
{
70-
$this->options['structure']->setValue($structureType);
73+
array_walk($this->options['structure'], static function (CV $option) use ($structureType): void {
74+
$option->setValue($structureType);
75+
});
7176

7277
return $this;
7378
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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\Redis\Command\CreateCommand;
23+
24+
interface CreateCommandJSONFieldOption extends CreateCommandFieldOption
25+
{
26+
public function getJSONPath(): string;
27+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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\Redis\Command\CreateCommand;
23+
24+
use MacFJA\RediSearch\Redis\Command\Option\AbstractCommandOption;
25+
26+
class JSONFieldOption extends AbstractCommandOption implements CreateCommandJSONFieldOption
27+
{
28+
/** @var CreateCommandFieldOption */
29+
private $decorated;
30+
/** @var string */
31+
private $path;
32+
33+
public function __construct(string $path, CreateCommandFieldOption $decorated)
34+
{
35+
parent::__construct('>=2.2.0');
36+
$this->path = $path;
37+
$this->decorated = $decorated;
38+
}
39+
40+
public function isValid(): bool
41+
{
42+
return !empty($this->path) && $this->decorated->isValid();
43+
}
44+
45+
public function getOptionData()
46+
{
47+
return array_merge(['path' => $this->path], $this->decorated->getOptionData());
48+
}
49+
50+
public function getVersionConstraint(): string
51+
{
52+
return '>=2.2.0';
53+
}
54+
55+
public function getFieldName(): string
56+
{
57+
return $this->decorated->getFieldName();
58+
}
59+
60+
public function getJSONPath(): string
61+
{
62+
return $this->path;
63+
}
64+
65+
protected function doRender(?string $version): array
66+
{
67+
return array_merge([$this->path, 'AS'], $this->decorated->render($version));
68+
}
69+
}

tests/IndexBuilderTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\GeoFieldOption
4949
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\NumericFieldOption
5050
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\TagFieldOption
51+
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption
5152
* @uses \MacFJA\RediSearch\Redis\Command\Option\DecoratedOptionAwareTrait
5253
* @uses \MacFJA\RediSearch\Redis\Command\Option\GroupedOption
5354
* @uses \MacFJA\RediSearch\Redis\Command\Option\WithPublicGroupedSetterTrait
@@ -252,6 +253,45 @@ public function testAllAdd(): void
252253
static::assertEquals($createCommand, $builder->getCommand());
253254
}
254255

256+
public function testAllJsonAdd(): void
257+
{
258+
$createCommand = new Create();
259+
260+
$builder = new IndexBuilder();
261+
262+
$builder->setIndex('city');
263+
$createCommand->setIndex('city');
264+
265+
$oldBuilder = $builder;
266+
$builder = $builder->addPrefixes('city-', 'c-');
267+
static::assertNotEquals($createCommand, $builder->getCommand());
268+
$createCommand->setPrefixes('city-', 'c-');
269+
static::assertEquals($createCommand, $builder->getCommand());
270+
static::assertSame($oldBuilder, $builder);
271+
272+
$builder->addStopWords('hello', 'world');
273+
$createCommand->setStopWords('hello', 'world');
274+
275+
$builder->addJSONField('$.city.name', (new TextFieldOption())->setField('name'));
276+
$createCommand->addJSONTextField('$.city.name', 'name');
277+
278+
$builder->addJSONField('$.city.country', (new TextFieldOption())->setField('country'));
279+
$createCommand->addJSONTextField('$.city.country', 'country');
280+
281+
$builder->addJSONTextField('$.city.continent', 'continent');
282+
$createCommand->addJSONTextField('$.city.continent', 'continent');
283+
284+
$builder->addJSONNumericField('$.city.population', 'population');
285+
$createCommand->addJSONNumericField('$.city.population', 'population');
286+
287+
$builder->addJSONGeoField('$.city.gps', 'gps');
288+
$createCommand->addJSONGeoField('$.city.gps', 'gps');
289+
290+
$builder->addJSONTagField('$.city.languages', 'languages');
291+
$createCommand->addJSONTagField('$.city.languages', 'languages');
292+
static::assertEquals($createCommand, $builder->getCommand());
293+
}
294+
255295
/**
256296
* @medium
257297
*/

tests/Redis/Command/AddFieldOptionTraitTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ public function testWrongParent(): void
5151
$command->addNumericField('foo');
5252
}
5353

54+
public function testWrongParent2(): void
55+
{
56+
$command = new FakeAddFieldOptionTraitClass1();
57+
58+
$this->expectException(BadMethodCallException::class);
59+
$this->expectExceptionMessage('This method is not callable in '.FakeAddFieldOptionTraitClass1::class);
60+
61+
$command->addJSONNumericField('$.foo', 'foo');
62+
}
63+
5464
public function testValidParent(): void
5565
{
5666
$command = new FakeAddFieldOptionTraitClass2([]);

tests/Redis/Command/AggregateTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ public function testGetId(): void
6060
static::assertSame('FT.AGGREGATE', $command->getId());
6161
}
6262

63+
public function testGetIndex(): void
64+
{
65+
$command = new Aggregate();
66+
$command->setIndex('idx');
67+
static::assertSame('idx', $command->getIndex());
68+
}
69+
6370
public function testMultiReduce(): void
6471
{
6572
$group = new GroupByOption(['@text1']);

0 commit comments

Comments
 (0)