Skip to content

Commit

Permalink
Merge pull request #23 from MacFJA/issue-22-json-support
Browse files Browse the repository at this point in the history
Add support for JSON document
  • Loading branch information
MacFJA authored Dec 18, 2021
2 parents fd3bff9 + bb056d8 commit 8f80c08
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 6 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased v2.x]

### Added

- Support from JSON document (require RediSearch 2.2)

### Fixed

- (dev) Code coverage with XDebug 3

## [2.0.2]

### Added
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ test-with-integration: | vendor

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

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

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

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

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

Expand Down
12 changes: 12 additions & 0 deletions src/IndexBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use MacFJA\RediSearch\Redis\Command\AddFieldOptionTrait;
use MacFJA\RediSearch\Redis\Command\Create;
use MacFJA\RediSearch\Redis\Command\CreateCommand\CreateCommandFieldOption;
use MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption;
use RuntimeException;
use function strlen;

Expand Down Expand Up @@ -84,6 +85,10 @@
* @method IndexBuilder withAddedNumericField(string $name, bool $sortable = false, bool $noIndex = false)
* @method IndexBuilder withAddedGeoField(string $name, bool $noIndex = false)
* @method IndexBuilder withAddedTagField(string $name, ?string $separator = null, bool $sortable = false, bool $noIndex = false)
* @method IndexBuilder withAddedJSONTextField(string $path, string $attribute, bool $noStem = false, ?float $weight = null, ?string $phonetic = null, bool $sortable = false, bool $noIndex = false)
* @method IndexBuilder withAddedJSONNumericField(string $path, string $attribute, bool $sortable = false, bool $noIndex = false)
* @method IndexBuilder withAddedJSONGeoField(string $path, string $attribute, bool $noIndex = false)
* @method IndexBuilder withAddedJSONTagField(string $path, string $attribute, ?string $separator = null, bool $sortable = false, bool $noIndex = false)
*
* @SuppressWarnings(PHPMD.TooManyFields)
*/
Expand Down Expand Up @@ -185,6 +190,13 @@ public function addField(CreateCommandFieldOption $option): self
return $this;
}

public function addJSONField(string $path, CreateCommandFieldOption $option): self
{
$this->fields[$option->getFieldName()] = new JSONFieldOption($path, $option);

return $this;
}

/**
* @return mixed|string
*/
Expand Down
60 changes: 60 additions & 0 deletions src/Redis/Command/AddFieldOptionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use function get_class;
use MacFJA\RediSearch\Redis\Command\CreateCommand\CreateCommandFieldOption;
use MacFJA\RediSearch\Redis\Command\CreateCommand\GeoFieldOption;
use MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption;
use MacFJA\RediSearch\Redis\Command\CreateCommand\NumericFieldOption;
use MacFJA\RediSearch\Redis\Command\CreateCommand\TagFieldOption;
use MacFJA\RediSearch\Redis\Command\CreateCommand\TextFieldOption;
Expand Down Expand Up @@ -75,6 +76,54 @@ public function addTagField(string $name, ?string $separator = null, bool $sorta
);
}

public function addJSONTextField(string $path, string $attribute, bool $noStem = false, ?float $weight = null, ?string $phonetic = null, bool $sortable = false, bool $noIndex = false): self
{
return $this->addJSONField(
$path,
(new TextFieldOption())
->setField($attribute)
->setNoStem($noStem)
->setWeight($weight)
->setPhonetic($phonetic)
->setSortable($sortable)
->setNoIndex($noIndex)
);
}

public function addJSONNumericField(string $path, string $attribute, bool $sortable = false, bool $noIndex = false): self
{
return $this->addJSONField(
$path,
(new NumericFieldOption())
->setField($attribute)
->setSortable($sortable)
->setNoIndex($noIndex)
);
}

public function addJSONGeoField(string $path, string $attribute, bool $noIndex = false): self
{
return $this->addJSONField(
$path,
(new GeoFieldOption())
->setField($attribute)
->setNoIndex($noIndex)
);
}

public function addJSONTagField(string $path, string $attribute, ?string $separator = null, bool $sortable = false, bool $noIndex = false, bool $caseSensitive = false): self
{
return $this->addJSONField(
$path,
(new TagFieldOption())
->setField($attribute)
->setSeparator($separator)
->setCaseSensitive($caseSensitive)
->setSortable($sortable)
->setNoIndex($noIndex)
);
}

public function addField(CreateCommandFieldOption $option): self
{
if (!($this instanceof AbstractCommand)) {
Expand All @@ -85,4 +134,15 @@ public function addField(CreateCommandFieldOption $option): self

return $this;
}

public function addJSONField(string $path, CreateCommandFieldOption $option): self
{
if (!($this instanceof AbstractCommand)) {
throw new BadMethodCallException('This method is not callable in '.get_class($this));
}

$this->options['fields'][$option->getFieldName()] = new JSONFieldOption($path, $option);

return $this;
}
}
9 changes: 7 additions & 2 deletions src/Redis/Command/Create.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ public function __construct(string $rediSearchVersion = self::MIN_IMPLEMENTED_VE
{
parent::__construct([
'index' => new NamelessOption(null, '>=2.0.0'),
'structure' => CV::allowedValues(new NamedOption('ON', null, '>=2.0.0'), ['HASH']),
'structure' => [
CV::allowedValues(new NamedOption('ON', null, '>=2.0.0 <2.2.0'), ['HASH']),
CV::allowedValues(new NamedOption('ON', null, '>=2.2.0'), ['HASH', 'JSON']),
],
'prefixes' => new NotEmptyOption(new NumberedOption('PREFIX', null, '>=2.0.0')),
'filter' => new NamedOption('FILTER', null, '>=2.0.0'),
'default_lang' => $this->getLanguageOptions(),
Expand Down Expand Up @@ -67,7 +70,9 @@ public function setIndex(string $index): self

public function setStructure(string $structureType = 'HASH'): self
{
$this->options['structure']->setValue($structureType);
array_walk($this->options['structure'], static function (CV $option) use ($structureType): void {
$option->setValue($structureType);
});

return $this;
}
Expand Down
27 changes: 27 additions & 0 deletions src/Redis/Command/CreateCommand/CreateCommandJSONFieldOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

/*
* Copyright MacFJA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

namespace MacFJA\RediSearch\Redis\Command\CreateCommand;

interface CreateCommandJSONFieldOption extends CreateCommandFieldOption
{
public function getJSONPath(): string;
}
69 changes: 69 additions & 0 deletions src/Redis/Command/CreateCommand/JSONFieldOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

/*
* Copyright MacFJA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

namespace MacFJA\RediSearch\Redis\Command\CreateCommand;

use MacFJA\RediSearch\Redis\Command\Option\AbstractCommandOption;

class JSONFieldOption extends AbstractCommandOption implements CreateCommandJSONFieldOption
{
/** @var CreateCommandFieldOption */
private $decorated;
/** @var string */
private $path;

public function __construct(string $path, CreateCommandFieldOption $decorated)
{
parent::__construct('>=2.2.0');
$this->path = $path;
$this->decorated = $decorated;
}

public function isValid(): bool
{
return !empty($this->path) && $this->decorated->isValid();
}

public function getOptionData()
{
return array_merge(['path' => $this->path], $this->decorated->getOptionData());
}

public function getVersionConstraint(): string
{
return '>=2.2.0';
}

public function getFieldName(): string
{
return $this->decorated->getFieldName();
}

public function getJSONPath(): string
{
return $this->path;
}

protected function doRender(?string $version): array
{
return array_merge([$this->path, 'AS'], $this->decorated->render($version));
}
}
40 changes: 40 additions & 0 deletions tests/IndexBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\GeoFieldOption
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\NumericFieldOption
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\TagFieldOption
* @uses \MacFJA\RediSearch\Redis\Command\CreateCommand\JSONFieldOption
* @uses \MacFJA\RediSearch\Redis\Command\Option\DecoratedOptionAwareTrait
* @uses \MacFJA\RediSearch\Redis\Command\Option\GroupedOption
* @uses \MacFJA\RediSearch\Redis\Command\Option\WithPublicGroupedSetterTrait
Expand Down Expand Up @@ -252,6 +253,45 @@ public function testAllAdd(): void
static::assertEquals($createCommand, $builder->getCommand());
}

public function testAllJsonAdd(): void
{
$createCommand = new Create();

$builder = new IndexBuilder();

$builder->setIndex('city');
$createCommand->setIndex('city');

$oldBuilder = $builder;
$builder = $builder->addPrefixes('city-', 'c-');
static::assertNotEquals($createCommand, $builder->getCommand());
$createCommand->setPrefixes('city-', 'c-');
static::assertEquals($createCommand, $builder->getCommand());
static::assertSame($oldBuilder, $builder);

$builder->addStopWords('hello', 'world');
$createCommand->setStopWords('hello', 'world');

$builder->addJSONField('$.city.name', (new TextFieldOption())->setField('name'));
$createCommand->addJSONTextField('$.city.name', 'name');

$builder->addJSONField('$.city.country', (new TextFieldOption())->setField('country'));
$createCommand->addJSONTextField('$.city.country', 'country');

$builder->addJSONTextField('$.city.continent', 'continent');
$createCommand->addJSONTextField('$.city.continent', 'continent');

$builder->addJSONNumericField('$.city.population', 'population');
$createCommand->addJSONNumericField('$.city.population', 'population');

$builder->addJSONGeoField('$.city.gps', 'gps');
$createCommand->addJSONGeoField('$.city.gps', 'gps');

$builder->addJSONTagField('$.city.languages', 'languages');
$createCommand->addJSONTagField('$.city.languages', 'languages');
static::assertEquals($createCommand, $builder->getCommand());
}

/**
* @medium
*/
Expand Down
10 changes: 10 additions & 0 deletions tests/Redis/Command/AddFieldOptionTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ public function testWrongParent(): void
$command->addNumericField('foo');
}

public function testWrongParent2(): void
{
$command = new FakeAddFieldOptionTraitClass1();

$this->expectException(BadMethodCallException::class);
$this->expectExceptionMessage('This method is not callable in '.FakeAddFieldOptionTraitClass1::class);

$command->addJSONNumericField('$.foo', 'foo');
}

public function testValidParent(): void
{
$command = new FakeAddFieldOptionTraitClass2([]);
Expand Down
7 changes: 7 additions & 0 deletions tests/Redis/Command/AggregateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ public function testGetId(): void
static::assertSame('FT.AGGREGATE', $command->getId());
}

public function testGetIndex(): void
{
$command = new Aggregate();
$command->setIndex('idx');
static::assertSame('idx', $command->getIndex());
}

public function testMultiReduce(): void
{
$group = new GroupByOption(['@text1']);
Expand Down
Loading

0 comments on commit 8f80c08

Please sign in to comment.