Skip to content

Commit 8f3b8c5

Browse files
authored
Merge pull request #74 from ethanhann/v2
v2 -> master
2 parents 51574ca + 1b7c583 commit 8f3b8c5

19 files changed

+278
-123
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
.idea/
33
.php_cs.cache
44
composer.lock
5+
composer.phar
56
tests.log
7+
/.phpunit.result.cache

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ before_install:
55
- docker run -d -p 6379:6379 redislabs/redisearch:edge --protected-mode no --loadmodule /usr/lib/redis/modules/redisearch.so
66
language: php
77
php:
8-
- 7.0
9-
- 7.1
10-
- 7.2
118
- 7.3
9+
- 7.4
10+
- 8.0
1211
before_script:
1312
- printf "yes\n" | pecl install igbinary
1413
- composer install

composer.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@
2121
"minimum-stability": "dev",
2222
"prefer-stable": true,
2323
"require": {
24-
"php": ">=7",
24+
"php": ">=7.3||>=8.0",
2525
"psr/log": "^1.0",
26-
"ethanhann/redis-raw": "^0.3"
26+
"ethanhann/redis-raw": "v1.x-dev"
2727
},
2828
"require-dev": {
29-
"phpunit/phpunit": "^6.0",
29+
"phpunit/phpunit": "^8.0",
3030
"mockery/mockery": "^0.9.9",
3131
"predis/predis": "^1.1",
3232
"friendsofphp/php-cs-fixer": "^2.2",
3333
"consolidation/robo": "^2.0",
3434
"monolog/monolog": "^1.23",
35-
"cheprasov/php-redis-client": "^1.8",
36-
"ukko/phpredis-phpdoc": "^5.0@beta"
35+
"ukko/phpredis-phpdoc": "^5.0@beta",
36+
"cheprasov/php-redis-client": "^1.9"
3737
}
3838
}

docs-src/indexing.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,17 @@ An alias can be deleted like this:
227227
```php-inline
228228
$index->deleteAlias('foo');
229229
```
230+
231+
## Managing an Index
232+
233+
Whether or not an index exists can be checked:
234+
235+
```php-inline
236+
$indexExists = $index->exists();
237+
```
238+
239+
An index can be removed:
240+
241+
```php-inline
242+
$index->drop();
243+
```

src/Aggregate/Builder.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public function load(array $fieldNames): BuilderInterface
6868
* @param CanBecomeArrayInterface|array $reducer
6969
* @return BuilderInterface
7070
*/
71-
public function groupBy($fieldName, CanBecomeArrayInterface $reducer = null): BuilderInterface
71+
public function groupBy($fieldName = [], CanBecomeArrayInterface $reducer = null): BuilderInterface
7272
{
7373
$this->pipeline[] = new GroupBy(is_array($fieldName) ? $fieldName : [$fieldName]);
7474
if (!is_null($reducer)) {

src/Aggregate/Operations/AbstractFieldNameOperation.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ public function __construct(string $operationName, array $fieldNames)
1717

1818
public function toArray(): array
1919
{
20-
$count = count($this->fieldNames);
21-
return $count > 0 ? array_merge(
22-
[$this->operationName, $count],
20+
return array_merge(
21+
[$this->operationName, count($this->fieldNames)],
2322
array_map(function ($fieldName) {
2423
return "@$fieldName";
2524
}, $this->fieldNames)
26-
) : [];
25+
);
2726
}
2827
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Ehann\RediSearch\Exceptions;
4+
5+
use Exception;
6+
7+
class DocumentAlreadyInIndexException extends Exception
8+
{
9+
public function __construct($indexName, $documentId, $code = 0, Exception $previous = null)
10+
{
11+
parent::__construct("Document ($documentId) already in index ($indexName).", $code, $previous);
12+
}
13+
}

src/Fields/FieldFactory.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66

77
class FieldFactory
88
{
9-
public static function make($name, $value)
9+
public static function make($name, $value, $tagSeparator = ',')
1010
{
11+
if (is_array($value)) {
12+
return (new TagField($name, implode($tagSeparator, $value)))->setSeparator($tagSeparator);
13+
}
1114
if ($value instanceof Tag) {
1215
return new TagField($name, $value);
1316
}

src/Index.php

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Ehann\RediSearch\Document\AbstractDocumentFactory;
88
use Ehann\RediSearch\Document\DocumentInterface;
99
use Ehann\RediSearch\Exceptions\NoFieldsInIndexException;
10+
use Ehann\RediSearch\Exceptions\UnknownIndexNameException;
1011
use Ehann\RediSearch\Fields\FieldInterface;
1112
use Ehann\RediSearch\Fields\GeoField;
1213
use Ehann\RediSearch\Fields\NumericField;
@@ -15,6 +16,8 @@
1516
use Ehann\RediSearch\Query\Builder as QueryBuilder;
1617
use Ehann\RediSearch\Query\BuilderInterface as QueryBuilderInterface;
1718
use Ehann\RediSearch\Query\SearchResult;
19+
use Ehann\RedisRaw\Exceptions\RawCommandErrorException;
20+
use RedisException;
1821

1922
class Index extends AbstractIndex implements IndexInterface
2023
{
@@ -65,10 +68,23 @@ public function create()
6568
return $this->rawCommand('FT.CREATE', array_merge($properties, $fieldDefinitions));
6669
}
6770

71+
/**
72+
* @return bool
73+
*/
74+
public function exists(): bool
75+
{
76+
try {
77+
$this->info();
78+
return true;
79+
} catch (UnknownIndexNameException $exception) {
80+
return false;
81+
}
82+
}
83+
6884
/**
6985
* @return array
7086
*/
71-
protected function getFields(): array
87+
public function getFields(): array
7288
{
7389
$fields = [];
7490
foreach (get_object_vars($this) as $field) {
@@ -127,6 +143,15 @@ public function addTagField(string $name, bool $sortable = false, bool $noindex
127143
return $this;
128144
}
129145

146+
/**
147+
* @param string $name
148+
* @return array
149+
*/
150+
public function tagValues(string $name): array
151+
{
152+
return $this->rawCommand('FT.TAGVALS', [$this->getIndexName(), $name]);
153+
}
154+
130155
/**
131156
* @return mixed
132157
*/
@@ -449,14 +474,30 @@ public function verbatim(): QueryBuilderInterface
449474
/**
450475
* @param array $documents
451476
* @param bool $disableAtomicity
477+
* @param bool $replace
452478
*/
453-
public function addMany(array $documents, $disableAtomicity = false)
479+
public function addMany(array $documents, $disableAtomicity = false, $replace = false)
454480
{
481+
$result = null;
482+
455483
$pipe = $this->redisClient->multi($disableAtomicity);
456484
foreach ($documents as $document) {
457-
$this->_add($document);
485+
if (is_array($document)) {
486+
$document = $this->arrayToDocument($document);
487+
}
488+
$this->_add($document->setReplace($replace));
489+
}
490+
try {
491+
$pipe->exec();
492+
} catch (RedisException $exception) {
493+
$result = $exception->getMessage();
494+
} catch (RawCommandErrorException $exception) {
495+
$result = $exception->getPrevious()->getMessage();
496+
}
497+
498+
if ($result) {
499+
$this->redisClient->validateRawCommandResults($result, 'PIPE', [$this->indexName, '*MANY']);
458500
}
459-
$pipe->exec();
460501
}
461502

462503
/**
@@ -472,7 +513,7 @@ protected function _add(DocumentInterface $document, bool $isFromHash = false)
472513

473514
$properties = $isFromHash ? $document->getHashDefinition() : $document->getDefinition();
474515
array_unshift($properties, $this->getIndexName());
475-
return $this->rawCommand($isFromHash ? 'FT.ADDHASH' : 'FT.ADD', $properties);
516+
return $this->rawCommand($isFromHash ? 'HSET' : 'FT.ADD', $properties);
476517
}
477518

478519
/**
@@ -505,6 +546,15 @@ public function replace($document): bool
505546
return $this->_add($this->arrayToDocument($document)->setReplace(true));
506547
}
507548

549+
/**
550+
* @param array $documents
551+
* @param bool $disableAtomicity
552+
*/
553+
public function replaceMany(array $documents, $disableAtomicity = false)
554+
{
555+
$this->addMany($documents, $disableAtomicity, true);
556+
}
557+
508558
/**
509559
* @param $document
510560
* @return bool

src/IndexInterface.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
interface IndexInterface extends BuilderInterface
1010
{
1111
public function create();
12+
public function exists(): bool;
1213
public function drop();
1314
public function info();
1415
public function delete($id, $deleteDocument = false);
16+
public function getFields(): array;
1517
public function makeDocument($id = null): DocumentInterface;
1618
public function makeAggregateBuilder(): AggregateBuilderInterface;
1719
public function getRedisClient(): RediSearchRedisClient;
@@ -29,9 +31,11 @@ public function addTextField(string $name, float $weight = 1.0, bool $sortable =
2931
public function addNumericField(string $name, bool $sortable = false, bool $noindex = false): IndexInterface;
3032
public function addGeoField(string $name, bool $noindex = false): IndexInterface;
3133
public function addTagField(string $name, bool $sortable = false, bool $noindex = false, string $separator = ','): IndexInterface;
34+
public function tagValues(string $name): array;
3235
public function add($document): bool;
33-
public function addMany(array $documents, $disableAtomicity = false);
36+
public function addMany(array $documents, $disableAtomicity = false, $replace = false);
3437
public function replace($document): bool;
38+
public function replaceMany(array $documents, $disableAtomicity = false);
3539
public function addHash($document): bool;
3640
public function replaceHash($document): bool;
3741
public function addAlias(string $name): bool;

src/RediSearchRedisClient.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
namespace Ehann\RediSearch;
44

55
use Ehann\RediSearch\Exceptions\AliasDoesNotExistException;
6+
use Ehann\RediSearch\Exceptions\DocumentAlreadyInIndexException;
67
use Ehann\RediSearch\Exceptions\RediSearchException;
78
use Ehann\RediSearch\Exceptions\UnknownIndexNameException;
89
use Ehann\RediSearch\Exceptions\UnknownIndexNameOrNameIsAnAliasItselfException;
910
use Ehann\RediSearch\Exceptions\UnknownRediSearchCommandException;
1011
use Ehann\RediSearch\Exceptions\UnsupportedRediSearchLanguageException;
1112
use Ehann\RedisRaw\AbstractRedisRawClient;
13+
use Ehann\RedisRaw\Exceptions\RawCommandErrorException;
1214
use Ehann\RedisRaw\RedisRawClientInterface;
1315
use Exception;
1416
use Psr\Log\LoggerInterface;
@@ -23,7 +25,7 @@ public function __construct(RedisRawClientInterface $redis)
2325
$this->redis = $redis;
2426
}
2527

26-
public function validateRawCommandResults($payload)
28+
public function validateRawCommandResults($payload, string $command, array $arguments)
2729
{
2830
$isPayloadException = $payload instanceof Exception;
2931
$message = $isPayloadException ? $payload->getMessage() : $payload;
@@ -38,7 +40,7 @@ public function validateRawCommandResults($payload)
3840
throw new UnknownIndexNameException();
3941
}
4042

41-
if (in_array($message, ['unsupported language', 'unsupported stemmer language', 'bad argument for `language`'])) {
43+
if (in_array($message, ['no such language', 'unsupported language', 'unsupported stemmer language', 'bad argument for `language`'])) {
4244
throw new UnsupportedRediSearchLanguageException();
4345
}
4446

@@ -54,6 +56,10 @@ public function validateRawCommandResults($payload)
5456
throw new UnknownRediSearchCommandException($message);
5557
}
5658

59+
if (in_array($message, ['document already in index', 'document already exists'])) {
60+
throw new DocumentAlreadyInIndexException($arguments[0], $arguments[1]);
61+
}
62+
5763
throw new RediSearchException($payload);
5864
}
5965

@@ -72,11 +78,22 @@ public function multi(bool $usePipeline = false)
7278
return $this->redis->multi($usePipeline);
7379
}
7480

75-
public function rawCommand(string $command, array $arguments)
81+
public function rawCommand(string $command, array $arguments = [])
7682
{
77-
$result = $this->redis->rawCommand($command, $arguments);
83+
try {
84+
foreach ($arguments as $index => $value) {
85+
/* The various RedisRaw clients have different expectations about arg types, but generally they all
86+
* agree that they can be strings.
87+
*/
88+
$arguments[$index] = strval($value);
89+
}
90+
$result = $this->redis->rawCommand($command, $arguments);
91+
} catch (RawCommandErrorException $exception) {
92+
$result = $exception->getPrevious()->getMessage();
93+
}
94+
7895
if ($command !== 'FT.EXPLAIN') {
79-
$this->validateRawCommandResults($result);
96+
$this->validateRawCommandResults($result, $command, $arguments);
8097
}
8198

8299
return $result;

tests/RediSearch/Aggregate/AggregationResultTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class AggregationResultTest extends RediSearchTestCase
1414
protected $subject;
1515
protected $expectedDocuments;
1616

17-
public function setUp()
17+
public function setUp(): void
1818
{
1919
$this->expectedDocuments = [
2020
['title' => 'part1'],

tests/RediSearch/Aggregate/BuilderTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class BuilderTest extends RediSearchTestCase
1919
private $expectedResult3;
2020
private $expectedResult4;
2121

22-
public function setUp()
22+
public function setUp(): void
2323
{
2424
$this->indexName = 'AggregateBuilderTest';
2525
$index = (new TestIndex($this->redisClient, $this->indexName))
@@ -67,7 +67,7 @@ public function setUp()
6767
$this->subject = (new Builder($this->redisClient, $this->indexName));
6868
}
6969

70-
public function tearDown()
70+
public function tearDown(): void
7171
{
7272
$this->redisClient->flushAll();
7373
}
@@ -244,7 +244,7 @@ public function testGetAbsoluteMin()
244244
$expected = 9.99;
245245

246246
$result = $this->subject
247-
->groupBy('_')
247+
->groupBy()
248248
->min('price')
249249
->search();
250250

@@ -256,7 +256,7 @@ public function testGetAbsoluteMax()
256256
$expected = 38.85;
257257

258258
$result = $this->subject
259-
->groupBy('_')
259+
->groupBy()
260260
->max('price')
261261
->search();
262262

@@ -284,7 +284,7 @@ public function testGetAbsoluteQuantile()
284284
$expected = 38.85;
285285

286286
$result = $this->subject
287-
->groupBy('_')
287+
->groupBy()
288288
->quantile('price', 0.5)
289289
->search();
290290

tests/RediSearch/Fields/TextFieldTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class TextFieldTest extends TestCase
1818
/** @var float */
1919
private $defaultWeight = 1.0;
2020

21-
public function setUp()
21+
public function setUp(): void
2222
{
2323
$this->subject = new TextField($this->fieldName);
2424
}

0 commit comments

Comments
 (0)