diff --git a/src/Array/ArrayStorage.php b/src/Array/ArrayStorage.php index 7d619cd..76680d8 100644 --- a/src/Array/ArrayStorage.php +++ b/src/Array/ArrayStorage.php @@ -2,6 +2,15 @@ namespace Shopware\Storage\Array; +use Shopware\Storage\Common\Aggregation\AggregationAware; +use Shopware\Storage\Common\Aggregation\AggregationCaster; +use Shopware\Storage\Common\Aggregation\Type\Aggregation; +use Shopware\Storage\Common\Aggregation\Type\Avg; +use Shopware\Storage\Common\Aggregation\Type\Count; +use Shopware\Storage\Common\Aggregation\Type\Distinct; +use Shopware\Storage\Common\Aggregation\Type\Max; +use Shopware\Storage\Common\Aggregation\Type\Min; +use Shopware\Storage\Common\Aggregation\Type\Sum; use Shopware\Storage\Common\Document\Document; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Filter\FilterAware; @@ -26,22 +35,23 @@ use Shopware\Storage\Common\Filter\Type\Not; use Shopware\Storage\Common\Filter\Type\Prefix; use Shopware\Storage\Common\Filter\Type\Suffix; -use Shopware\Storage\Common\KeyValue\KeyAware; use Shopware\Storage\Common\Schema\FieldType; use Shopware\Storage\Common\Schema\Schema; use Shopware\Storage\Common\Schema\SchemaUtil; -use Shopware\Storage\Common\Storage; use Shopware\Storage\Common\StorageContext; use Shopware\Storage\Common\Total; -class ArrayStorage extends ArrayKeyStorage implements FilterAware +class ArrayStorage extends ArrayKeyStorage implements FilterAware, AggregationAware { /** * @var array */ private array $storage = []; - public function __construct(private readonly Schema $schema) {} + public function __construct( + private readonly AggregationCaster $caster, + private readonly Schema $schema + ) {} public function setup(): void { @@ -82,6 +92,43 @@ public function store(Documents $documents): void } } + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { + $filtered = $this->storage; + + if ($criteria->primaries) { + $filtered = array_filter($filtered, fn($key) => in_array($key, $criteria->primaries, true), ARRAY_FILTER_USE_KEY); + } + + if ($criteria->filters) { + $filtered = array_filter($filtered, fn(Document $document): bool => $this->match($document, $criteria->filters, $context)); + } + + $result = []; + foreach ($aggregations as $aggregation) { + // reset to root filter to not apply aggregations specific filters + $documents = $filtered; + + if ($aggregation->filters) { + $documents = array_filter($documents, fn(Document $document): bool => $this->match($document, $aggregation->filters, $context)); + } + + $value = $this->parseAggregation( + filtered: $documents, + aggregation: $aggregation, + context: $context + ); + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value + ); + } + + return $result; + } + public function filter(Criteria $criteria, StorageContext $context): Result { $filtered = $this->storage; @@ -118,6 +165,66 @@ public function filter(Criteria $criteria, StorageContext $context): Result return new Result(elements: $filtered, total: $total); } + /** + * @param array $filtered + */ + private function parseAggregation(array $filtered, Aggregation $aggregation, StorageContext $context): mixed + { + $values = []; + foreach ($filtered as $document) { + $nested = $this->getDocValue( + document: $document, + accessor: $aggregation->field, + context: $context + ); + if (!is_array($nested)) { + $values[] = $nested; + continue; + } + foreach ($nested as $item) { + $values[] = $item; + } + } + + if ($aggregation instanceof Min) { + return min($values); + } + + if ($aggregation instanceof Max) { + return max($values); + } + + if ($aggregation instanceof Sum) { + return array_sum($values); + } + + if ($aggregation instanceof Avg) { + return array_sum($values) / count($values); + } + + if ($aggregation instanceof Distinct) { + return array_unique($values); + } + + if ($aggregation instanceof Count) { + $mapped = []; + assert(is_array($values), 'Count aggregation must return an array'); + foreach ($values as $value) { + $key = (string) $value; + + if (!isset($mapped[$key])) { + $mapped[$key] = ['key' => $value, 'count' => 0]; + } + + $mapped[$key]['count']++; + } + + return $mapped; + } + + throw new \LogicException(sprintf('Unknown aggregation type %s', get_class($aggregation))); + } + /** * @param array $filters */ @@ -458,5 +565,4 @@ private function lte(mixed $docValue, mixed $value): bool default => $docValue <= $value, }; } - } diff --git a/src/Common/Aggregation/Aggregation.php b/src/Common/Aggregation/Aggregation.php deleted file mode 100644 index 1fdfdf3..0000000 --- a/src/Common/Aggregation/Aggregation.php +++ /dev/null @@ -1,8 +0,0 @@ - */ public function aggregate( array $aggregations, Criteria $criteria, StorageContext $context - ): Aggregations; + ): array; } diff --git a/src/Common/Aggregation/AggregationCaster.php b/src/Common/Aggregation/AggregationCaster.php new file mode 100644 index 0000000..5b19fc7 --- /dev/null +++ b/src/Common/Aggregation/AggregationCaster.php @@ -0,0 +1,72 @@ +field); + + switch ($type) { + case FieldType::INT: + // cast to float, because of avg aggregation + $caster = fn($value) => (float) $value; + break; + case FieldType::FLOAT: + $caster = fn($value) => round((float) $value, 6); + break; + case FieldType::BOOL: + $caster = function ($value) { + return match (true) { + $value === '1', $value === 'true' => true, + $value === '0', $value === 'false' => false, + default => (bool) $value + }; + }; + break; + case FieldType::DATETIME: + + $caster = function ($value) { + return match (true) { + is_string($value) => (new \DateTimeImmutable($value))->format('Y-m-d H:i:s.v'), + is_int($value) => (new \DateTimeImmutable('@' . $value))->format('Y-m-d H:i:s.v'), + default => $value + }; + }; + break; + default: + $caster = fn($value) => $value; + } + + if ($aggregation instanceof Distinct) { + assert(is_array($data), 'Distinct aggregation must return an array'); + + $values = array_map($caster, $data); + sort($values); + return $values; + } + + if ($aggregation instanceof Count) { + assert(is_array($data), 'Count aggregation must return an array'); + + $values = array_map(fn($value) => [ + 'key' => $caster($value['key']), + 'count' => (int) $value['count'] + ], $data); + + usort($values, fn($a, $b) => $a['key'] <=> $b['key']); + + return $values; + } + + return $caster($data); + } +} diff --git a/src/Common/Aggregation/Type/Aggregation.php b/src/Common/Aggregation/Type/Aggregation.php new file mode 100644 index 0000000..2bca02c --- /dev/null +++ b/src/Common/Aggregation/Type/Aggregation.php @@ -0,0 +1,17 @@ + $filters + */ + public function __construct( + public readonly string $name, + public readonly string $field, + public readonly array $filters = [], + ) {} +} diff --git a/src/Common/Aggregation/Type/Avg.php b/src/Common/Aggregation/Type/Avg.php new file mode 100644 index 0000000..01f8bb7 --- /dev/null +++ b/src/Common/Aggregation/Type/Avg.php @@ -0,0 +1,5 @@ +fields[$part] ?? null; if (!$field instanceof Field) { - throw new \RuntimeException(sprintf('Field %s not found in schema', $part)); + throw new \RuntimeException( + sprintf('Unable to get nested field part %s of accessor %s in schema', $part, $accessor) + ); } } diff --git a/src/Meilisearch/MeilisearchStorage.php b/src/Meilisearch/MeilisearchStorage.php index ef940e8..4a40f02 100644 --- a/src/Meilisearch/MeilisearchStorage.php +++ b/src/Meilisearch/MeilisearchStorage.php @@ -4,6 +4,14 @@ use Meilisearch\Client; use Meilisearch\Endpoints\Indexes; +use Shopware\Storage\Common\Aggregation\AggregationAware; +use Shopware\Storage\Common\Aggregation\AggregationCaster; +use Shopware\Storage\Common\Aggregation\Type\Avg; +use Shopware\Storage\Common\Aggregation\Type\Count; +use Shopware\Storage\Common\Aggregation\Type\Distinct; +use Shopware\Storage\Common\Aggregation\Type\Max; +use Shopware\Storage\Common\Aggregation\Type\Min; +use Shopware\Storage\Common\Aggregation\Type\Sum; use Shopware\Storage\Common\Document\Document; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Exception\NotSupportedByEngine; @@ -28,18 +36,222 @@ use Shopware\Storage\Common\Filter\Type\Not; use Shopware\Storage\Common\Filter\Type\Prefix; use Shopware\Storage\Common\Filter\Type\Suffix; +use Shopware\Storage\Common\Schema\FieldType; use Shopware\Storage\Common\Schema\Schema; use Shopware\Storage\Common\Schema\SchemaUtil; use Shopware\Storage\Common\Storage; use Shopware\Storage\Common\StorageContext; -class MeilisearchStorage implements Storage, FilterAware +class MeilisearchStorage implements Storage, FilterAware, AggregationAware { public function __construct( + private readonly AggregationCaster $caster, private readonly Client $client, private readonly Schema $schema ) {} + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { + $params = [ + 'page' => 0, + 'hitsPerPage' => 0, + ]; + + $filters = $criteria->filters; + if ($criteria->primaries !== null) { + $filters[] = new Any(field: 'key', value: $criteria->primaries); + } + if (!empty($filters)) { + $params['filter'] = $this->parse($filters, $context); + } + + $facets = []; + foreach ($aggregations as $aggregation) { + $translated = SchemaUtil::translated(schema: $this->schema, accessor: $aggregation->field); + if ($translated) { + throw new NotSupportedByEngine('', 'Meilisearch does not support aggregations on translated fields.'); + } + + $type = SchemaUtil::type(schema: $this->schema, accessor: $aggregation->field); + if (in_array($type, [FieldType::TEXT, FieldType::STRING], true)) { + if ($aggregation instanceof Sum || $aggregation instanceof Avg) { + throw new NotSupportedByEngine('', 'Meilisearch does not support sum/avg aggregations on string/text fields.'); + } + } + + $facets[] = $aggregation->field; + } + + $params['facets'] = $facets; + + $response = $this->index()->search( + query: null, + searchParams: $params + ); + + /** @var array $stats */ + $stats = $response->getFacetStats(); + + /** @var array> $distributions */ + $distributions = $response->getFacetDistribution(); + + $result = []; + foreach ($aggregations as $aggregation) { + $type = SchemaUtil::type(schema: $this->schema, accessor: $aggregation->field); + + if ($type === FieldType::BOOL && $aggregation instanceof Min) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: !array_key_exists('false', self::distribution($distributions, $aggregation->field)) + ); + continue; + } + + if ($type === FieldType::BOOL && $aggregation instanceof Max) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: array_key_exists('true', self::distribution($distributions, $aggregation->field)) + ); + continue; + } + + if (in_array($type, [FieldType::STRING, FieldType::TEXT, FieldType::DATETIME], true) && $aggregation instanceof Min) { + $values = array_keys(self::distribution($distributions, $aggregation->field)); + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: min($values) + ); + + continue; + } + + if (in_array($type, [FieldType::STRING, FieldType::TEXT, FieldType::DATETIME], true) && $aggregation instanceof Max) { + $values = array_keys(self::distribution($distributions, $aggregation->field)); + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: max($values) + ); + + continue; + } + + if ($aggregation instanceof Min) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: self::stats($stats, $aggregation->field, 'min') + ); + continue; + } + + if ($aggregation instanceof Max) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: self::stats($stats, $aggregation->field, 'max') + ); + continue; + } + + if ($aggregation instanceof Distinct) { + $values = self::distribution($distributions, $aggregation->field); + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: array_keys($values) + ); + continue; + } + + if ($aggregation instanceof Count) { + $values = self::distribution($distributions, $aggregation->field); + + $values = array_map(fn($value) => ['key' => $value, 'count' => $values[$value]], array_keys($values)); + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $values + ); + + continue; + } + + if ($aggregation instanceof Sum) { + $values = self::distribution($distributions, $aggregation->field); + + $sum = 0; + foreach ($values as $key => $value) { + if (!is_numeric($key) || !is_numeric($value)) { + throw new \RuntimeException('Meilisearch does not return a numeric value for aggregation field ' . $aggregation->field); + } + $sum += (float) $key * (float) $value; + } + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $sum + ); + continue; + } + + if ($aggregation instanceof Avg) { + $values = self::distribution($distributions, $aggregation->field); + + $sum = 0; + $count = 0; + foreach ($values as $key => $value) { + if (!is_numeric($key) || !is_numeric($value)) { + throw new \RuntimeException('Meilisearch does not return a numeric value for aggregation field ' . $aggregation->field); + } + $sum += (float) $key * (float) $value; + $count += $value; + } + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $sum / $count + ); + } + } + + return $result; + } + + /** + * @param array $stats + */ + private static function stats(array $stats, string $field, string $key): mixed + { + if (!isset($stats[$field])) { + throw new \RuntimeException(sprintf('Meilisearch does not return a stats value for aggregation field %s', $field)); + } + + return $stats[$field][$key]; + } + + /** + * @param array> $distributions + * @return array + */ + private static function distribution(array $distributions, string $field): array + { + if (!isset($distributions[$field])) { + throw new \RuntimeException(sprintf('Meilisearch does not return a distribution value for aggregation field %s', $field)); + } + + return $distributions[$field]; + } + public function filter(Criteria $criteria, StorageContext $context): Result { $params = []; diff --git a/src/MongoDB/MongoDBStorage.php b/src/MongoDB/MongoDBStorage.php index 7b134c9..d9c6e70 100644 --- a/src/MongoDB/MongoDBStorage.php +++ b/src/MongoDB/MongoDBStorage.php @@ -4,6 +4,15 @@ use MongoDB\Client; use MongoDB\Collection; +use Shopware\Storage\Common\Aggregation\AggregationAware; +use Shopware\Storage\Common\Aggregation\AggregationCaster; +use Shopware\Storage\Common\Aggregation\Type\Aggregation; +use Shopware\Storage\Common\Aggregation\Type\Avg; +use Shopware\Storage\Common\Aggregation\Type\Count; +use Shopware\Storage\Common\Aggregation\Type\Distinct; +use Shopware\Storage\Common\Aggregation\Type\Max; +use Shopware\Storage\Common\Aggregation\Type\Min; +use Shopware\Storage\Common\Aggregation\Type\Sum; use Shopware\Storage\Common\Document\Document; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Filter\Criteria; @@ -29,17 +38,25 @@ use Shopware\Storage\Common\Filter\Type\Not; use Shopware\Storage\Common\Filter\Type\Prefix; use Shopware\Storage\Common\Filter\Type\Suffix; +use Shopware\Storage\Common\Schema\FieldType; use Shopware\Storage\Common\Schema\Schema; use Shopware\Storage\Common\Schema\SchemaUtil; use Shopware\Storage\Common\Storage; use Shopware\Storage\Common\StorageContext; -class MongoDBStorage implements Storage, FilterAware +class MongoDBStorage implements Storage, FilterAware, AggregationAware { + private const TYPE_MAP = [ + 'root' => 'array', + 'document' => 'array', + 'array' => 'array', + ]; + public function __construct( - private readonly string $database, - private readonly Schema $schema, - private readonly Client $client + private readonly AggregationCaster $caster, + private readonly string $database, + private readonly Schema $schema, + private readonly Client $client ) {} public function setup(): void @@ -47,6 +64,215 @@ public function setup(): void // todo@o.skroblin auto setup feature } + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { + $query = []; + + $options = ['typeMap' => self::TYPE_MAP]; + + $filters = $criteria->filters; + if ($criteria->primaries) { + $filters[] = new Any('key', $criteria->primaries); + } + + if (!empty($filters)) { + $filters = $this->parseFilters($filters, $context); + + $query[] = ['$match' => $filters]; + } + + $parsed = []; + foreach ($aggregations as $aggregation) { + $parsed[$aggregation->name] = $this->parseAggregation( + aggregation: $aggregation, + context: $context + ); + } + + $query[] = ['$facet' => $parsed]; + + $response = $this->collection()->aggregate( + pipeline: $query, + options: $options + ); + + $result = []; + foreach ($response as $value) { + if (!is_array($value)) { + throw new \RuntimeException('Invalid aggregation result'); + } + + $key = (string) array_key_first($value); + + if (!isset($value[$key])) { + throw new \RuntimeException('Invalid aggregation result'); + } + + $aggregation = $this->getAggregation(name: $key, aggregations: $aggregations); + + if ($aggregation instanceof Min) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value[$key][0]['min'] + ); + continue; + } + + if ($aggregation instanceof Max) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value[$key][0]['max'] + ); + continue; + } + if ($aggregation instanceof Sum) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value[$key][0]['sum'] + ); + continue; + } + if ($aggregation instanceof Avg) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value[$key][0]['avg'] + ); + continue; + } + if ($aggregation instanceof Distinct) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: array_column($value[$key], '_id') + ); + continue; + } + + if ($aggregation instanceof Count) { + $values = array_map(function (array $item) { + return ['key' => $item['_id'], 'count' => $item['count']]; + }, $value[$key]); + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $values + ); + continue; + } + + throw new \RuntimeException(sprintf('Unsupported aggregation type %s', $aggregation::class)); + } + + return $result; + } + + /** + * @param Aggregation[] $aggregations + */ + private function getAggregation(string $name, array $aggregations): Aggregation + { + foreach ($aggregations as $aggregation) { + if ($aggregation->name === $name) { + return $aggregation; + } + } + + throw new \RuntimeException(sprintf('Aggregation %s not found', $name)); + } + + /** + * @return array + */ + private function parseAggregation(Aggregation $aggregation, StorageContext $context): array + { + $parsed = []; + + if (!empty($aggregation->filters)) { + $parsed[] = ['$match' => $this->parseFilters($aggregation->filters, $context)]; + } + + $property = SchemaUtil::property(accessor: $aggregation->field); + + $type = SchemaUtil::type(schema: $this->schema, accessor: $property); + + if (in_array($type, [FieldType::OBJECT_LIST, FieldType::LIST], true)) { + $parsed[] = ['$unwind' => '$' . $property]; + } + $field = '$' . $aggregation->field; + + $translated = SchemaUtil::translated(schema: $this->schema, accessor: $aggregation->field); + + if ($translated) { + $field = array_map(function (string $language) use ($field) { + return $field . '.' . $language; + }, $context->languages); + + $field = ['$ifNull' => $field]; + } + + if ($aggregation instanceof Min) { + $parsed[] = [ + '$group' => [ + '_id' => 0, + 'min' => ['$min' => $field], + ] + ]; + return $parsed; + } + if ($aggregation instanceof Max) { + $parsed[] = [ + '$group' => [ + '_id' => 0, + 'max' => ['$max' => $field], + ] + ]; + return $parsed; + } + if ($aggregation instanceof Sum) { + $parsed[] = [ + '$group' => [ + '_id' => 0, + 'sum' => ['$sum' => $field], + ] + ]; + return $parsed; + } + if ($aggregation instanceof Avg) { + $parsed[] = [ + '$group' => [ + '_id' => 0, + 'avg' => ['$avg' => $field], + ] + ]; + return $parsed; + } + if ($aggregation instanceof Count) { + $parsed[] = [ + '$group' => [ + '_id' => $field, + 'count' => ['$sum' => 1], + ] + ]; + return $parsed; + } + if ($aggregation instanceof Distinct) { + $parsed[] = [ + '$group' => [ + '_id' => $field + ] + ]; + + return $parsed; + } + + throw new \RuntimeException(sprintf('Unsupported aggregation type %s', $aggregation::class)); + } + public function remove(array $keys): void { $this->collection()->deleteMany([ @@ -93,6 +319,7 @@ public function filter(Criteria $criteria, StorageContext $context): Result if ($criteria->primaries) { $query['_key'] = ['$in' => $criteria->primaries]; } + if ($criteria->filters) { $filters = $this->parseFilters($criteria->filters, $context); @@ -101,11 +328,7 @@ public function filter(Criteria $criteria, StorageContext $context): Result $cursor = $this->collection()->find($query, $options); - $cursor->setTypeMap([ - 'root' => 'array', - 'document' => 'array', - 'array' => 'array', - ]); + $cursor->setTypeMap(self::TYPE_MAP); $result = []; foreach ($cursor as $item) { diff --git a/src/MySQL/MySQLStorage.php b/src/MySQL/MySQLStorage.php index ff78405..83787a6 100644 --- a/src/MySQL/MySQLStorage.php +++ b/src/MySQL/MySQLStorage.php @@ -5,6 +5,15 @@ use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; +use Shopware\Storage\Common\Aggregation\AggregationAware; +use Shopware\Storage\Common\Aggregation\AggregationCaster; +use Shopware\Storage\Common\Aggregation\Type\Aggregation; +use Shopware\Storage\Common\Aggregation\Type\Avg; +use Shopware\Storage\Common\Aggregation\Type\Count; +use Shopware\Storage\Common\Aggregation\Type\Distinct; +use Shopware\Storage\Common\Aggregation\Type\Max; +use Shopware\Storage\Common\Aggregation\Type\Min; +use Shopware\Storage\Common\Aggregation\Type\Sum; use Shopware\Storage\Common\Document\Document; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Filter\Criteria; @@ -38,19 +47,47 @@ use Shopware\Storage\Common\Util\Uuid; use Shopware\Storage\MySQL\Util\MultiInsert; -class MySQLStorage implements Storage, FilterAware +class MySQLStorage implements Storage, FilterAware, AggregationAware { public function __construct( - private readonly Connection $connection, - private readonly Schema $schema + private readonly AggregationCaster $caster, + private readonly Connection $connection, + private readonly Schema $schema ) {} + private static function escape(string $source): string + { + return '`' . $source . '`'; + } public function setup(): void { // todo@o.skroblin auto setup feature } + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { + $result = []; + + foreach ($aggregations as $aggregation) { + $data = $this->loadAggregation( + aggregation: $aggregation, + criteria: $criteria, + context: $context + ); + + $data = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $data + ); + + $result[$aggregation->name] = $data; + } + + return $result; + } + public function remove(array $keys): void { $this->connection->executeStatement( @@ -74,12 +111,73 @@ public function store(Documents $documents): void $queue->execute(); } + private function loadAggregation(Aggregation $aggregation, Criteria $criteria, StorageContext $context): mixed + { + $query = $this->connection->createQueryBuilder(); + + $query->from($this->schema->source, 'root'); + + $filters = array_merge( + $criteria->filters, + $aggregation->filters + ); + + //todo@skroblin support of post filters? + if (!empty($filters)) { + $parsed = $this->addFilters($query, $filters, $context); + + foreach ($parsed as $filter) { + $query->andWhere($filter); + } + } + + $accessor = $this->getAccessor($query, $aggregation->field, $context); + + if ($aggregation instanceof Min) { + $query->select('MIN(' . $accessor . ')'); + return $query->executeQuery()->fetchOne(); + } + + if ($aggregation instanceof Max) { + $query->select('MAX(' . $accessor . ')'); + return $query->executeQuery()->fetchOne(); + } + + if ($aggregation instanceof Avg) { + $query->select('AVG(' . $accessor . ')'); + return $query->executeQuery()->fetchOne(); + } + + if ($aggregation instanceof Sum) { + $query->select('SUM(' . $accessor . ')'); + return $query->executeQuery()->fetchOne(); + } + + if ($aggregation instanceof Count) { + $query->select([ + $accessor . ' as `key`', + 'COUNT(' . $accessor . ') as count' + ]); + $query->groupBy($accessor); + + return $query->executeQuery()->fetchAllAssociative(); + } + + if ($aggregation instanceof Distinct) { + $query->select('DISTINCT ' . $accessor); + + return $query->executeQuery()->fetchFirstColumn(); + } + + throw new \LogicException(sprintf('Unsupported aggregation type %s', get_class($aggregation))); + } + public function filter(Criteria $criteria, StorageContext $context): Result { $query = $this->connection->createQueryBuilder(); $query->select('root.*'); - $query->from($this->schema->source, 'root'); + $query->from(self::escape($this->schema->source), 'root'); if ($criteria->paging instanceof Page) { $query->setFirstResult(($criteria->paging->page - 1) * $criteria->paging->limit); @@ -313,14 +411,16 @@ private function getAccessor(QueryBuilder $query, string $accessor, StorageConte $root = SchemaUtil::property(accessor: $accessor); - $type = SchemaUtil::type(schema: $this->schema, accessor: $root); - $translated = SchemaUtil::translated(schema: $this->schema, accessor: $root); - $cast = ''; - if ($type === 'bool') { - $cast = ' RETURNING UNSIGNED'; - } + $type = SchemaUtil::type(schema: $this->schema, accessor: $accessor); + + $cast = match ($type) { + FieldType::BOOL => ' RETURNING UNSIGNED', + default => '', + }; + + $type = SchemaUtil::type(schema: $this->schema, accessor: $root); if ($translated) { $selects = []; diff --git a/src/Opensearch/OpenSearchStorage.php b/src/Opensearch/OpensearchStorage.php similarity index 52% rename from src/Opensearch/OpenSearchStorage.php rename to src/Opensearch/OpensearchStorage.php index 735ecf9..da13668 100644 --- a/src/Opensearch/OpenSearchStorage.php +++ b/src/Opensearch/OpensearchStorage.php @@ -3,6 +3,14 @@ namespace Shopware\Storage\Opensearch; use OpenSearch\Client; +use OpenSearchDSL\Aggregation\AbstractAggregation; +use OpenSearchDSL\Aggregation\Bucketing\FilterAggregation; +use OpenSearchDSL\Aggregation\Bucketing\NestedAggregation; +use OpenSearchDSL\Aggregation\Bucketing\TermsAggregation; +use OpenSearchDSL\Aggregation\Metric\MaxAggregation; +use OpenSearchDSL\Aggregation\Metric\AvgAggregation; +use OpenSearchDSL\Aggregation\Metric\MinAggregation; +use OpenSearchDSL\Aggregation\Metric\SumAggregation; use OpenSearchDSL\BuilderInterface; use OpenSearchDSL\Query\Compound\BoolQuery; use OpenSearchDSL\Query\Joining\NestedQuery; @@ -11,10 +19,21 @@ use OpenSearchDSL\Query\TermLevel\TermQuery; use OpenSearchDSL\Query\TermLevel\TermsQuery; use OpenSearchDSL\Query\TermLevel\WildcardQuery; +use OpenSearchDSL\ScriptAwareTrait; use OpenSearchDSL\Search; use OpenSearchDSL\Sort\FieldSort; +use Shopware\Storage\Common\Aggregation\AggregationAware; +use Shopware\Storage\Common\Aggregation\AggregationCaster; +use Shopware\Storage\Common\Aggregation\Type\Aggregation; +use Shopware\Storage\Common\Aggregation\Type\Avg; +use Shopware\Storage\Common\Aggregation\Type\Count; +use Shopware\Storage\Common\Aggregation\Type\Distinct; +use Shopware\Storage\Common\Aggregation\Type\Max; +use Shopware\Storage\Common\Aggregation\Type\Min; +use Shopware\Storage\Common\Aggregation\Type\Sum; use Shopware\Storage\Common\Document\Document; use Shopware\Storage\Common\Document\Documents; +use Shopware\Storage\Common\Exception\NotSupportedByEngine; use Shopware\Storage\Common\Filter\Criteria; use Shopware\Storage\Common\Filter\Paging\Limit; use Shopware\Storage\Common\Filter\Result; @@ -44,9 +63,13 @@ use Shopware\Storage\Common\StorageContext; use Shopware\Storage\Common\Total; -class OpenSearchStorage implements Storage, FilterAware +class OpensearchStorage implements Storage, FilterAware, AggregationAware { - public function __construct(private readonly Client $client, private readonly Schema $schema) {} + public function __construct( + private readonly AggregationCaster $caster, + private readonly Client $client, + private readonly Schema $schema + ) {} public function setup(): void { @@ -85,56 +108,155 @@ public function store(Documents $documents): void return; } - $this->client->bulk(['body' => $body]); + $response = $this->client->bulk(['body' => $body]); - $this->client->indices()->refresh([ - 'index' => $this->schema->source - ]); + if ($response['errors'] === true) { + dump($response); + } } - public function filter( - Criteria $criteria, - StorageContext $context - ): Result { + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { $search = new Search(); - if ($criteria->paging instanceof Page) { - $search->setFrom(($criteria->paging->page - 1) * $criteria->paging->limit); - $search->setSize($criteria->paging->limit); - } elseif ($criteria->paging instanceof Limit) { - $search->setSize($criteria->paging->limit); - } + $search->setSize(0); - if ($criteria->primaries) { - $search->addQuery( - new TermsQuery( - field: '_id', - terms: $criteria->primaries - ) - ); - } + $this->handlePrimaries(criteria: $criteria, search: $search); - $queries = $this->parseRootFilter( - filters: $criteria->filters, - context: $context - ); + $this->handleFilters(criteria: $criteria, search: $search, context: $context); - foreach ($queries as $query) { - $search->addQuery( - query: $query, - boolType: BoolQuery::FILTER - ); + $this->handleAggregations(aggregations: $aggregations, search: $search, context: $context); + + $parsed = $search->toArray(); + + $params = [ + 'index' => $this->schema->source, + 'body' => $parsed + ]; + + try { + $response = $this->client->search($params); + } catch (\Throwable $e) { + dump(json_decode($e->getMessage(), true)); + throw $e; } - if ($criteria->sorting) { - foreach ($criteria->sorting as $sorting) { - $search->addSort(new FieldSort( - field: $sorting->field, - order: $sorting->order - )); + $result = []; + + foreach ($aggregations as $aggregation) { + $type = SchemaUtil::type(schema: $this->schema, accessor: $aggregation->field); + + $translated = SchemaUtil::translated(schema: $this->schema, accessor: $aggregation->field); + + $value = $response['aggregations'][$aggregation->name]; + + // nested aggregation? get the value from the nested path + $value = $value[$aggregation->name] ?? $value; + + if ($aggregation instanceof Avg) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value['value_as_string'] ?? $value['value'] + ); + continue; } + + if ($aggregation instanceof Min) { + $data = $value['value_as_string'] ?? $value['value']; + + if ($type === FieldType::DATETIME && $translated) { + $data = date('Y-m-d H:i:s.000', $data / 1000); + } + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $data + ); + continue; + } + + if ($aggregation instanceof Max) { + $data = $value['value_as_string'] ?? $value['value']; + + if ($type === FieldType::DATETIME && $translated) { + $data = date('Y-m-d H:i:s.000', $data / 1000); + } + + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $data + ); + continue; + } + + if ($aggregation instanceof Sum) { + $result[$aggregation->name] = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $value['value_as_string'] ?? $value['value'] + ); + continue; + } + + if ($aggregation instanceof Count) { + $values = array_map(function ($bucket) { + return [ + 'key' => $bucket['key_as_string'] ?? $bucket['key'], + 'count' => $bucket['doc_count'] + ]; + }, $value['buckets']); + + $values = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $values + ); + + $result[$aggregation->name] = $values; + + continue; + } + + if ($aggregation instanceof Distinct) { + + $values = array_map(function ($bucket) { + return $bucket['key_as_string'] ?? $bucket['key']; + }, $value['buckets']); + + $values = $this->caster->cast( + schema: $this->schema, + aggregation: $aggregation, + data: $values + ); + + $result[$aggregation->name] = $values; + + continue; + } + + throw new \LogicException(sprintf('Unsupported aggregation type %s', $aggregation::class)); } + return $result; + } + + public function filter( + Criteria $criteria, + StorageContext $context + ): Result { + $search = new Search(); + + $this->handlePaging(criteria: $criteria, search: $search); + + $this->handlePrimaries(criteria: $criteria, search: $search); + + $this->handleFilters(criteria: $criteria, search: $search, context: $context); + + $this->handleSorting(criteria: $criteria, search: $search); + $parsed = $search->toArray(); $params = [ @@ -160,6 +282,51 @@ public function filter( ); } + private function handlePrimaries(Criteria $criteria, Search $search): void + { + if ($criteria->primaries) { + $search->addQuery( + new TermsQuery(field: '_id', terms: $criteria->primaries) + ); + } + } + + private function handleFilters(Criteria $criteria, Search $search, StorageContext $context): void + { + $queries = $this->parseRootFilter( + filters: $criteria->filters, + context: $context + ); + + foreach ($queries as $query) { + $search->addQuery(query: $query, boolType: BoolQuery::FILTER); + } + } + + private function handlePaging(Criteria $criteria, Search $search): void + { + if ($criteria->paging instanceof Page) { + $search->setFrom(($criteria->paging->page - 1) * $criteria->paging->limit); + $search->setSize($criteria->paging->limit); + } elseif ($criteria->paging instanceof Limit) { + $search->setSize($criteria->paging->limit); + } + } + + private function handleSorting(Criteria $criteria, Search $search): void + { + if (!$criteria->sorting) { + return; + } + + foreach ($criteria->sorting as $sorting) { + $search->addSort(new FieldSort( + field: $sorting->field, + order: $sorting->order + )); + } + } + /** * @param array $filters * @return array @@ -175,28 +342,18 @@ private function parseRootFilter(array $filters, StorageContext $context): array continue; } - $property = SchemaUtil::property($filter->field); - - $type = SchemaUtil::type(schema: $this->schema, accessor: $property); + $path = $this->getNestedPath($filter->field); - $translated = SchemaUtil::translated(schema: $this->schema, accessor: $filter->field); + $parsed = $this->parse(filter: $filter, context: $context); // object field? created nested - if (in_array($type, [FieldType::OBJECT, FieldType::OBJECT_LIST], true) || $translated) { - $nested = $this->parse( - filter: $filter, - context: $context - ); - - $queries[] = new NestedQuery(path: $property, query: $nested); + if ($path !== null) { + $queries[] = new NestedQuery(path: $path, query: $parsed); continue; } - $queries[] = $this->parse( - filter: $filter, - context: $context - ); + $queries[] = $parsed; } return $queries; @@ -462,4 +619,148 @@ private function parseFilter(Filter $filter, StorageContext $context): BuilderIn throw new \LogicException(sprintf('Unsupported filter type %s', $filter::class)); } + private function parseAggregation(Aggregation $aggregation, StorageContext $context): AbstractAggregation + { + $type = SchemaUtil::type(schema: $this->schema, accessor: $aggregation->field); + + if ($aggregation instanceof Max) { + if (in_array($type, [FieldType::STRING, FieldType::TEXT, FieldType::LIST], true)) { + throw new NotSupportedByEngine('45', 'Max aggregation is not supported for string, text or list fields'); + } + + return new MaxAggregation( + name: $aggregation->name, + field: $aggregation->field + ); + } + + if ($aggregation instanceof Min) { + if (in_array($type, [FieldType::STRING, FieldType::TEXT, FieldType::LIST], true)) { + throw new NotSupportedByEngine('45', 'Min aggregation is not supported for string, text or list fields'); + } + + return new MinAggregation( + name: $aggregation->name, + field: $aggregation->field + ); + } + + if ($aggregation instanceof Sum) { + if (in_array($type, [FieldType::STRING, FieldType::TEXT, FieldType::LIST], true)) { + throw new NotSupportedByEngine('45', 'Sum aggregation is not supported for string, text or list fields'); + } + + return new SumAggregation( + name: $aggregation->name, + field: $aggregation->field + ); + } + + if ($aggregation instanceof Avg) { + if (in_array($type, [FieldType::STRING, FieldType::TEXT, FieldType::LIST], true)) { + throw new NotSupportedByEngine('45', 'Avg aggregation is not supported for string, text or list fields'); + } + + return new AvgAggregation( + name: $aggregation->name, + field: $aggregation->field + ); + } + + if ($aggregation instanceof Distinct) { + if ($type === FieldType::TEXT) { + throw new NotSupportedByEngine('45', 'Distinct aggregation is not supported for text fields'); + } + + return new TermsAggregation( + name: $aggregation->name, + field: $aggregation->field + ); + } + + if ($aggregation instanceof Count) { + if ($type === FieldType::TEXT) { + throw new NotSupportedByEngine('45', 'Distinct aggregation is not supported for count fields'); + } + + return new TermsAggregation( + name: $aggregation->name, + field: $aggregation->field + ); + } + + throw new \LogicException(sprintf('Unsupported aggregation type %s', $aggregation::class)); + } + + /** + * @param array $aggregations + */ + private function handleAggregations( + array $aggregations, + Search $search, + StorageContext $context + ): void { + foreach ($aggregations as $aggregation) { + $parsed = $this->parseAggregation( + aggregation: $aggregation, + context: $context + ); + + $translated = SchemaUtil::translated(schema: $this->schema, accessor: $aggregation->field); + + $path = $this->getNestedPath($aggregation->field); + + if ($translated) { + $parsed->setField(null); + + assert(method_exists($parsed, 'setScript')); + $parsed->setScript([ + 'id' => 'translated', + 'params' => [ + 'languages' => $context->languages, + 'field' => $aggregation->field, + 'fallback' => 0 + ] + ]); + } elseif ($path !== null) { + $parsed = (new NestedAggregation($aggregation->name, $path)) + ->addAggregation($parsed); + } + + if (empty($aggregation->filters)) { + $search->addAggregation($parsed); + continue; + } + + $queries = $this->parseRootFilter( + filters: $aggregation->filters, + context: $context + ); + + $filter = new FilterAggregation( + name: $aggregation->name . '.filter', + filter: new BoolQuery([ + BoolQuery::FILTER => $queries + ]) + ); + + $filter->addAggregation($parsed); + + $search->addAggregation($filter); + } + } + + private function getNestedPath(string $field): ?string + { + $property = SchemaUtil::property($field); + + $type = SchemaUtil::type(schema: $this->schema, accessor: $property); + + // object field? created nested + if (in_array($type, [FieldType::OBJECT, FieldType::OBJECT_LIST], true)) { + return $property; + } + + return null; + } } diff --git a/src/Opensearch/scripts/translated.groovy b/src/Opensearch/scripts/translated.groovy new file mode 100644 index 0000000..5856263 --- /dev/null +++ b/src/Opensearch/scripts/translated.groovy @@ -0,0 +1,11 @@ +def languages = params['languages']; + +for (int i = 0; i < languages.length; i++) { + def field_name = params['field'] + '.' + languages[i]; + + if (doc[field_name].size() > 0 && doc[field_name].value != null) { + return doc[field_name]; + } +} + +return params['default']; diff --git a/tests/Array/ArrayAggregationStorageTest.php b/tests/Array/ArrayAggregationStorageTest.php new file mode 100644 index 0000000..07de4e0 --- /dev/null +++ b/tests/Array/ArrayAggregationStorageTest.php @@ -0,0 +1,25 @@ +getSchema() + ); + } +} diff --git a/tests/Array/ArrayFilterStorageTest.php b/tests/Array/ArrayFilterStorageTest.php index cb86505..1ff9efa 100644 --- a/tests/Array/ArrayFilterStorageTest.php +++ b/tests/Array/ArrayFilterStorageTest.php @@ -3,6 +3,7 @@ namespace Shopware\StorageTests\Array; use Shopware\Storage\Array\ArrayStorage; +use Shopware\Storage\Common\Aggregation\AggregationCaster; use Shopware\Storage\Common\Filter\FilterAware; use Shopware\Storage\Common\Storage; use Shopware\StorageTests\Common\FilterStorageTestBase; @@ -14,6 +15,9 @@ class ArrayFilterStorageTest extends FilterStorageTestBase { public function getStorage(): FilterAware&Storage { - return new ArrayStorage(schema: $this->getSchema()); + return new ArrayStorage( + caster: new AggregationCaster(), + schema: $this->getSchema() + ); } } diff --git a/tests/Common/AggregationStorageTestBase.php b/tests/Common/AggregationStorageTestBase.php new file mode 100644 index 0000000..4cdbdd6 --- /dev/null +++ b/tests/Common/AggregationStorageTestBase.php @@ -0,0 +1,1353 @@ + $expected + */ + #[DataProvider('translatedDateCases')] + #[DataProvider('dateCases')] + public function testDebug( + Aggregation $aggregations, + Documents $input, + array $expected + ): void { + $this->testStorage($aggregations, $input, $expected); + } + + abstract public function getStorage(): AggregationAware&Storage; + + public static function stringCases(): \Generator + { + yield 'Min, string field' => [ + new Min(name: 'min', field: 'stringField'), + new Documents([ + self::document(key: 'key1', stringField: 'a'), + self::document(key: 'key2', stringField: 'b'), + self::document(key: 'key3', stringField: 'c') + ]), + ['min' => 'a'] + ]; + yield 'Max, string field' => [ + new Max(name: 'max', field: 'stringField'), + new Documents([ + self::document(key: 'key1', stringField: 'a'), + self::document(key: 'key2', stringField: 'b'), + self::document(key: 'key3', stringField: 'c') + ]), + ['max' => 'c'] + ]; + yield 'Distinct, string field' => [ + new Distinct(name: 'distinct', field: 'stringField'), + new Documents([ + self::document(key: 'key1', stringField: 'a'), + self::document(key: 'key2', stringField: 'b'), + self::document(key: 'key3', stringField: 'c'), + self::document(key: 'key4', stringField: 'c') + ]), + ['distinct' => ['a', 'b', 'c']] + ]; + yield 'Count, string field' => [ + new Count(name: 'count', field: 'stringField'), + new Documents([ + self::document(key: 'key1', stringField: 'a'), + self::document(key: 'key2', stringField: 'b'), + self::document(key: 'key3', stringField: 'b'), + ]), + [ + 'count' => [ + ['key' => 'a', 'count' => 1], + ['key' => 'b', 'count' => 2] + ] + ] + ]; + } + + public static function textCases(): \Generator + { + yield 'Min, text field' => [ + new Min(name: 'min', field: 'textField'), + new Documents([ + self::document(key: 'key1', textField: 'a'), + self::document(key: 'key2', textField: 'b'), + self::document(key: 'key3', textField: 'c') + ]), + ['min' => 'a'] + ]; + yield 'Max, text field' => [ + new Max(name: 'max', field: 'textField'), + new Documents([ + self::document(key: 'key1', textField: 'a'), + self::document(key: 'key2', textField: 'b'), + self::document(key: 'key3', textField: 'c') + ]), + ['max' => 'c'] + ]; + yield 'Distinct, text field' => [ + new Distinct(name: 'distinct', field: 'textField'), + new Documents([ + self::document(key: 'key1', textField: 'a'), + self::document(key: 'key2', textField: 'b'), + self::document(key: 'key3', textField: 'c'), + self::document(key: 'key4', textField: 'c') + ]), + ['distinct' => ['a', 'b', 'c']] + ]; + yield 'Count, text field' => [ + new Count(name: 'count', field: 'textField'), + new Documents([ + self::document(key: 'key1', textField: 'a'), + self::document(key: 'key2', textField: 'b'), + self::document(key: 'key3', textField: 'b'), + ]), + [ + 'count' => [ + ['key' => 'a', 'count' => 1], + ['key' => 'b', 'count' => 2] + ] + ] + ]; + } + + public static function intCases(): \Generator + { + yield 'Min, int field' => [ + new Min(name: 'min', field: 'intField'), + new Documents([ + self::document(key: 'key1', intField: 1), + self::document(key: 'key2', intField: 2), + self::document(key: 'key3', intField: 3), + ]), + ['min' => 1], + ]; + yield 'Avg, int field' => [ + new Avg(name: 'avg', field: 'intField'), + new Documents([ + self::document(key: 'key1', intField: 1), + self::document(key: 'key2', intField: 2), + self::document(key: 'key3', intField: 3), + ]), + ['avg' => 2], + ]; + yield 'Max, int field' => [ + new Max(name: 'max', field: 'intField'), + new Documents([ + self::document(key: 'key1', intField: 1), + self::document(key: 'key2', intField: 2), + self::document(key: 'key3', intField: 3), + ]), + ['max' => 3], + ]; + yield 'Sum, int field' => [ + new Sum(name: 'sum', field: 'intField'), + new Documents([ + self::document(key: 'key1', intField: 1), + self::document(key: 'key2', intField: 2), + self::document(key: 'key3', intField: 3), + ]), + ['sum' => 6], + ]; + yield 'Count, int field' => [ + new Count(name: 'count', field: 'intField'), + new Documents([ + self::document(key: 'key1', intField: 1), + self::document(key: 'key2', intField: 2), + self::document(key: 'key3', intField: 2), + self::document(key: 'key4', intField: 2), + self::document(key: 'key5', intField: 3), + ]), + [ + 'count' => [ + ['key' => 1, 'count' => 1], + ['key' => 2, 'count' => 3], + ['key' => 3, 'count' => 1] + ] + ], + ]; + yield 'Distinct, int field' => [ + new Distinct(name: 'distinct', field: 'intField'), + new Documents([ + self::document(key: 'key1', intField: 1), + self::document(key: 'key2', intField: 2), + self::document(key: 'key3', intField: 3), + self::document(key: 'key4', intField: 3), + ]), + ['distinct' => [1, 2, 3]], + ]; + } + + public static function floatCases(): \Generator + { + yield 'Avg, float field' => [ + new Avg(name: 'avg', field: 'floatField'), + new Documents([ + self::document(key: 'key1', floatField: 1.1), + self::document(key: 'key2', floatField: 2.2), + self::document(key: 'key3', floatField: 3.3), + ]), + ['avg' => 2.2], + ]; + yield 'Min, float field' => [ + new Min(name: 'min', field: 'floatField'), + new Documents([ + self::document(key: 'key1', floatField: 1.1), + self::document(key: 'key2', floatField: 2.2), + self::document(key: 'key3', floatField: 3.3), + ]), + ['min' => 1.1], + ]; + yield 'Max, float field' => [ + new Max(name: 'max', field: 'floatField'), + new Documents([ + self::document(key: 'key1', floatField: 1.1), + self::document(key: 'key2', floatField: 2.2), + self::document(key: 'key3', floatField: 3.3), + ]), + ['max' => 3.3], + ]; + yield 'Sum, float field' => [ + new Sum(name: 'sum', field: 'floatField'), + new Documents([ + self::document(key: 'key1', floatField: 1.1), + self::document(key: 'key2', floatField: 2.2), + self::document(key: 'key3', floatField: 3.3), + ]), + ['sum' => 6.6], + ]; + yield 'Count, float field' => [ + new Count(name: 'count', field: 'floatField'), + new Documents([ + self::document(key: 'key1', floatField: 1.1), + self::document(key: 'key2', floatField: 2.2), + self::document(key: 'key3', floatField: 2.2), + self::document(key: 'key4', floatField: 2.2), + self::document(key: 'key5', floatField: 3.3), + ]), + [ + 'count' => [ + ['key' => 1.1, 'count' => 1], + ['key' => 2.2, 'count' => 3], + ['key' => 3.3, 'count' => 1] + ] + ], + ]; + yield 'Distinct, float field' => [ + new Distinct(name: 'distinct', field: 'floatField'), + new Documents([ + self::document(key: 'key1', floatField: 1.1), + self::document(key: 'key2', floatField: 2.2), + self::document(key: 'key3', floatField: 3.3), + self::document(key: 'key4', floatField: 3.3), + self::document(key: 'key5', floatField: 3.31), + ]), + ['distinct' => [1.1, 2.2, 3.3, 3.31]], + ]; + } + + public static function boolCases(): \Generator + { + yield 'Min, bool field' => [ + new Min(name: 'min', field: 'boolField'), + new Documents([ + self::document(key: 'key1', boolField: true), + self::document(key: 'key2', boolField: false), + self::document(key: 'key3', boolField: true), + ]), + ['min' => false], + ]; + yield 'Max, bool field' => [ + new Max(name: 'max', field: 'boolField'), + new Documents([ + self::document(key: 'key1', boolField: true), + self::document(key: 'key2', boolField: false), + self::document(key: 'key3', boolField: true), + ]), + ['max' => true], + ]; + yield 'Distinct, bool field' => [ + new Distinct(name: 'distinct', field: 'boolField'), + new Documents([ + self::document(key: 'key1', boolField: true), + self::document(key: 'key2', boolField: false), + self::document(key: 'key3', boolField: true), + self::document(key: 'key4', boolField: true), + self::document(key: 'key5', boolField: false), + ]), + ['distinct' => [false, true]], + ]; + yield 'Count, bool field' => [ + new Count(name: 'count', field: 'boolField'), + new Documents([ + self::document(key: 'key1', boolField: true), + self::document(key: 'key2', boolField: false), + self::document(key: 'key3', boolField: false), + self::document(key: 'key4', boolField: false), + self::document(key: 'key5', boolField: true), + ]), + [ + 'count' => [ + ['key' => false, 'count' => 3], + ['key' => true, 'count' => 2], + ] + ], + ]; + } + + public static function dateCases(): \Generator + { + yield 'Min, date field' => [ + new Min(name: 'min', field: 'dateField'), + new Documents([ + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), + ]), + ['min' => '2021-01-01 00:00:00.000'], + ]; + yield 'Max, date field' => [ + new Max(name: 'max', field: 'dateField'), + new Documents([ + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), + ]), + ['max' => '2021-01-03 00:00:00.000'], + ]; + yield 'Count, date field' => [ + new Count(name: 'count', field: 'dateField'), + new Documents([ + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key4', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key5', dateField: '2021-01-03 00:00:00.000'), + ]), + [ + 'count' => [ + ['key' => '2021-01-01 00:00:00.000', 'count' => 1], + ['key' => '2021-01-02 00:00:00.000', 'count' => 3], + ['key' => '2021-01-03 00:00:00.000', 'count' => 1] + ] + ], + ]; + yield 'Distinct, date field' => [ + new Distinct(name: 'distinct', field: 'dateField'), + new Documents([ + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), + self::document(key: 'key4', dateField: '2021-01-03 00:00:00.000'), + self::document(key: 'key5', dateField: '2021-01-03 00:00:00.000'), + ]), + ['distinct' => ['2021-01-01 00:00:00.000', '2021-01-02 00:00:00.000', '2021-01-03 00:00:00.000']], + ]; + } + + public static function listStringCases(): \Generator + { + yield 'Min, list string field' => [ + new Min(name: 'min', field: 'listField'), + new Documents([ + self::document('key1', listField: ['b', 'c']), + self::document('key2', listField: ['d', 'e', 'f']), + self::document('key3', listField: ['g', 'h', 'i']), + ]), + ['min' => 'b'], + ]; + yield 'Max, list string field' => [ + new Max(name: 'max', field: 'listField'), + new Documents([ + self::document('key1', listField: ['b', 'c']), + self::document('key2', listField: ['d', 'e', 'i']), + self::document('key3', listField: ['g', 'h', 'f']), + ]), + ['max' => 'i'], + ]; + yield 'Distinct, list string field' => [ + new Max(name: 'distinct', field: 'listField'), + new Documents([ + self::document('key1', listField: ['b', 'c']), + self::document('key2', listField: ['d', 'e', 'i']), + self::document('key3', listField: ['g', 'h', 'f']), + ]), + ['distinct' => ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']], + ]; + } + + public static function listFloatCases(): \Generator + { + yield 'Avg, list float field' => []; + yield 'Min, list float field' => []; + yield 'Max, list float field' => []; + yield 'Sum, list float field' => []; + yield 'Count, list float field' => []; + yield 'Distinct, list float field' => []; + } + + public static function listIntCases(): \Generator + { + yield 'Avg, list int field' => [ + new Avg(name: 'avg', field: 'listField'), + new Documents([ + self::document('key1', listField: [1, 2, 3]), + self::document('key2', listField: [2, 4, 3]), + self::document('key3', listField: [2, 5, 5]), + ]), + ['avg' => 3], + ]; + yield 'Min, list int field' => [ + new Min(name: 'min', field: 'listField'), + new Documents([ + self::document('key1', listField: [1, 2, 3]), + self::document('key2', listField: [2, 4, 3]), + self::document('key3', listField: [2, 5, 4]), + ]), + ['min' => 1], + ]; + yield 'Max, list int field' => [ + new Max(name: 'max', field: 'listField'), + new Documents([ + self::document('key1', listField: [1, 2, 3]), + self::document('key2', listField: [2, 4, 3]), + self::document('key3', listField: [2, 5, 4]), + ]), + ['max' => 5], + ]; + yield 'Sum, list int field' => [ + new Sum(name: 'sum', field: 'listField'), + new Documents([ + self::document('key1', listField: [1, 2, 3]), + self::document('key2', listField: [2, 4, 3]), + self::document('key3', listField: [2, 5, 4]), + ]), + ['sum' => 26], + ]; + yield 'Count, list int field' => [ + new Count(name: 'count', field: 'listField'), + new Documents([ + self::document('key1', listField: [1, 2, 3]), + self::document('key2', listField: [2, 4, 3]), + self::document('key3', listField: [2, 5, 3]), + ]), + [ + 'count' => [ + ['key' => 1, 'count' => 1], + ['key' => 2, 'count' => 3], + ['key' => 3, 'count' => 3], + ['key' => 4, 'count' => 1], + ['key' => 5, 'count' => 1], + ] + ], + ]; + yield 'Distinct, list int field' => [ + new Distinct(name: 'distinct', field: 'listField'), + new Documents([ + self::document('key1', listField: [1, 2, 3]), + self::document('key2', listField: [2, 4, 3]), + self::document('key3', listField: [2, 5, 3]), + ]), + ['distinct' => [1, 2, 3, 4, 5]], + ]; + } + + public static function listDateCases(): \Generator + { + yield 'Avg, list date field' => []; + yield 'Min, list date field' => []; + yield 'Max, list date field' => []; + yield 'Sum, list date field' => []; + yield 'Count, list date field' => []; + yield 'Distinct, list date field' => []; + } + + public static function objectStringCases(): \Generator + { + yield 'Min, object string field' => [ + new Min(name: 'min', field: 'objectField.stringField'), + new Documents([ + self::document(key: 'key1', objectField: ['stringField' => 'a']), + self::document(key: 'key2', objectField: ['stringField' => 'b']), + self::document(key: 'key3', objectField: ['stringField' => 'c']) + ]), + ['min' => 'a'] + ]; + yield 'Max, object string field' => [ + new Max(name: 'max', field: 'objectField.stringField'), + new Documents([ + self::document(key: 'key1', objectField: ['stringField' => 'a']), + self::document(key: 'key2', objectField: ['stringField' => 'b']), + self::document(key: 'key3', objectField: ['stringField' => 'c']) + ]), + ['max' => 'c'] + ]; + yield 'Count, object string field' => [ + new Count(name: 'count', field: 'objectField.stringField'), + new Documents([ + self::document(key: 'key1', objectField: ['stringField' => 'a']), + self::document(key: 'key2', objectField: ['stringField' => 'b']), + self::document(key: 'key3', objectField: ['stringField' => 'b']), + ]), + [ + 'count' => [ + ['key' => 'a', 'count' => 1], + ['key' => 'b', 'count' => 2] + ] + ] + ]; + yield 'Distinct, object string field' => [ + new Distinct(name: 'distinct', field: 'objectField.stringField'), + new Documents([ + self::document(key: 'key1', objectField: ['stringField' => 'a']), + self::document(key: 'key2', objectField: ['stringField' => 'b']), + self::document(key: 'key3', objectField: ['stringField' => 'c']), + self::document(key: 'key4', objectField: ['stringField' => 'c']) + ]), + ['distinct' => ['a', 'b', 'c']] + ]; + } + + public static function objectFloatCases(): \Generator + { + yield 'Avg, object float field' => [ + new Avg(name: 'avg', field: 'objectField.floatField'), + new Documents([ + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), + ]), + ['avg' => 2.2], + ]; + yield 'Min, object float field' => [ + new Min(name: 'min', field: 'objectField.floatField'), + new Documents([ + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), + ]), + ['min' => 1.1], + ]; + yield 'Max, object float field' => [ + new Max(name: 'max', field: 'objectField.floatField'), + new Documents([ + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), + ]), + ['max' => 3.3], + ]; + yield 'Sum, object float field' => [ + new Sum(name: 'sum', field: 'objectField.floatField'), + new Documents([ + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), + ]), + ['sum' => 6.6], + ]; + yield 'Count, object float field' => [ + new Count(name: 'count', field: 'objectField.floatField'), + new Documents([ + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 2.2]), + self::document(key: 'key4', objectField: ['floatField' => 2.2]), + self::document(key: 'key5', objectField: ['floatField' => 3.3]), + ]), + [ + 'count' => [ + ['key' => 1.1, 'count' => 1], + ['key' => 2.2, 'count' => 3], + ['key' => 3.3, 'count' => 1] + ] + ], + ]; + yield 'Distinct, object float field' => [ + new Distinct(name: 'distinct', field: 'objectField.floatField'), + new Documents([ + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), + self::document(key: 'key4', objectField: ['floatField' => 3.3]), + ]), + ['distinct' => [1.1, 2.2, 3.3]], + ]; + } + + public static function objectIntCases(): \Generator + { + yield 'Avg, object int field' => [ + new Avg(name: 'avg', field: 'objectField.intField'), + new Documents([ + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), + ]), + ['avg' => 2], + ]; + yield 'Min, object int field' => [ + new Min(name: 'min', field: 'objectField.intField'), + new Documents([ + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), + ]), + ['min' => 1], + ]; + yield 'Max, object int field' => [ + new Max(name: 'max', field: 'objectField.intField'), + new Documents([ + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), + ]), + ['max' => 3], + ]; + yield 'Sum, object int field' => [ + new Sum(name: 'sum', field: 'objectField.intField'), + new Documents([ + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), + ]), + ['sum' => 6], + ]; + yield 'Count, object int field' => [ + new Count(name: 'count', field: 'objectField.intField'), + new Documents([ + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 2]), + self::document(key: 'key4', objectField: ['intField' => 2]), + self::document(key: 'key5', objectField: ['intField' => 3]), + ]), + [ + 'count' => [ + ['key' => 1, 'count' => 1], + ['key' => 2, 'count' => 3], + ['key' => 3, 'count' => 1] + ] + ], + ]; + yield 'Distinct, object int field' => [ + new Distinct(name: 'distinct', field: 'objectField.intField'), + new Documents([ + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), + self::document(key: 'key4', objectField: ['intField' => 3]), + ]), + ['distinct' => [1, 2, 3]], + ]; + } + + public static function objectBoolCases(): \Generator + { + yield 'Min, object bool field' => [ + new Min(name: 'min', field: 'objectField.boolField'), + new Documents([ + self::document(key: 'key1', objectField: ['boolField' => true]), + self::document(key: 'key2', objectField: ['boolField' => false]), + self::document(key: 'key3', objectField: ['boolField' => true]), + ]), + ['min' => false], + ]; + yield 'Max, object bool field' => [ + new Max(name: 'max', field: 'objectField.boolField'), + new Documents([ + self::document(key: 'key1', objectField: ['boolField' => true]), + self::document(key: 'key2', objectField: ['boolField' => false]), + self::document(key: 'key3', objectField: ['boolField' => true]), + ]), + ['max' => true], + ]; + yield 'Count, object bool field' => [ + new Count(name: 'count', field: 'objectField.boolField'), + new Documents([ + self::document(key: 'key1', objectField: ['boolField' => true]), + self::document(key: 'key2', objectField: ['boolField' => false]), + self::document(key: 'key3', objectField: ['boolField' => false]), + self::document(key: 'key4', objectField: ['boolField' => false]), + self::document(key: 'key5', objectField: ['boolField' => true]), + ]), + [ + 'count' => [ + ['key' => false, 'count' => 3], + ['key' => true, 'count' => 2], + ] + ], + ]; + yield 'Distinct, object bool field' => [ + new Distinct(name: 'distinct', field: 'objectField.boolField'), + new Documents([ + self::document(key: 'key1', objectField: ['boolField' => true]), + self::document(key: 'key2', objectField: ['boolField' => false]), + self::document(key: 'key3', objectField: ['boolField' => true]), + self::document(key: 'key4', objectField: ['boolField' => true]), + self::document(key: 'key5', objectField: ['boolField' => false]), + ]), + ['distinct' => [false, true]], + ]; + } + + public static function objectDateCases(): \Generator + { + yield 'Min, object date field' => [ + new Min(name: 'min', field: 'objectField.dateField'), + new Documents([ + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + ]), + ['min' => '2021-01-01 00:00:00.000'], + ]; + yield 'Max, object date field' => [ + new Max(name: 'max', field: 'objectField.dateField'), + new Documents([ + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + ]), + ['max' => '2021-01-03 00:00:00.000'], + ]; + + yield 'Count, object date field' => [ + new Count(name: 'count', field: 'objectField.dateField'), + new Documents([ + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key4', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key5', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + ]), + [ + 'count' => [ + ['key' => '2021-01-01 00:00:00.000', 'count' => 1], + ['key' => '2021-01-02 00:00:00.000', 'count' => 3], + ['key' => '2021-01-03 00:00:00.000', 'count' => 1] + ] + ], + ]; + yield 'Distinct, object date field' => [ + new Distinct(name: 'distinct', field: 'objectField.dateField'), + new Documents([ + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + self::document(key: 'key4', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + self::document(key: 'key5', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + ]), + ['distinct' => ['2021-01-01 00:00:00.000', '2021-01-02 00:00:00.000', '2021-01-03 00:00:00.000']], + ]; + } + + public static function objectListStringCases(): \Generator + { + yield 'Min, object list string field' => [ + new Min(name: 'min', field: 'objectListField.stringField'), + new Documents([ + self::document(key: 'key1', objectListField: [['stringField' => 'c'], ['stringField' => 'b']]), + self::document(key: 'key2', objectListField: [['stringField' => 'a'], ['stringField' => 'd']]), + self::document(key: 'key3', objectListField: [['stringField' => 'e'], ['stringField' => 'f']]), + ]), + ['min' => 'a'] + ]; + yield 'Max, object list string field' => [ + new Max(name: 'max', field: 'objectListField.stringField'), + new Documents([ + self::document(key: 'key1', objectListField: [['stringField' => 'c'], ['stringField' => 'b']]), + self::document(key: 'key2', objectListField: [['stringField' => 'a'], ['stringField' => 'd']]), + self::document(key: 'key3', objectListField: [['stringField' => 'e'], ['stringField' => 'f']]), + ]), + ['max' => 'f'] + ]; + yield 'Count, object list string field' => [ + new Count(name: 'count', field: 'objectListField.stringField'), + new Documents([ + self::document(key: 'key1', objectListField: [['stringField' => 'a'], ['stringField' => 'b']]), + self::document(key: 'key2', objectListField: [['stringField' => 'a'], ['stringField' => 'c']]), + self::document(key: 'key3', objectListField: [['stringField' => 'c'], ['stringField' => 'd']]), + ]), + [ + 'count' => [ + ['key' => 'a', 'count' => 2], + ['key' => 'b', 'count' => 1], + ['key' => 'c', 'count' => 2], + ['key' => 'd', 'count' => 1] + ] + ] + ]; + yield 'Distinct, object list string field' => [ + new Distinct(name: 'distinct', field: 'objectListField.stringField'), + new Documents([ + self::document(key: 'key1', objectListField: [['stringField' => 'a'], ['stringField' => 'b']]), + self::document(key: 'key2', objectListField: [['stringField' => 'a'], ['stringField' => 'c']]), + self::document(key: 'key3', objectListField: [['stringField' => 'c'], ['stringField' => 'd']]), + ]), + ['distinct' => ['a', 'b', 'c', 'd']] + ]; + } + + public static function objectListFloatCases(): \Generator + { + yield 'Avg, object list float field' => [ + new Avg(name: 'avg', field: 'objectListField.floatField'), + new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 3.3], ['floatField' => 4.4]]), + self::document(key: 'key3', objectListField: [['floatField' => 5.5], ['floatField' => 6.6]]), + ]), + ['avg' => 3.85], + ]; + yield 'Min, object list float field' => [ + new Min(name: 'min', field: 'objectListField.floatField'), + new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 3.3], ['floatField' => 4.4]]), + self::document(key: 'key3', objectListField: [['floatField' => 5.5], ['floatField' => 6.6]]), + ]), + ['min' => 1.1], + ]; + yield 'Max, object list float field' => [ + new Max(name: 'max', field: 'objectListField.floatField'), + new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 3.3], ['floatField' => 4.4]]), + self::document(key: 'key3', objectListField: [['floatField' => 5.5], ['floatField' => 6.6]]), + ]), + ['max' => 6.6], + ]; + yield 'Sum, object list float field' => [ + new Sum(name: 'sum', field: 'objectListField.floatField'), + new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 3.3], ['floatField' => 4.4]]), + self::document(key: 'key3', objectListField: [['floatField' => 5.5], ['floatField' => 6.6]]), + ]), + ['sum' => 23.1], + ]; + yield 'Count, object list float field' => [ + new Count(name: 'count', field: 'objectListField.floatField'), + new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 3.3], ['floatField' => 4.4]]), + self::document(key: 'key3', objectListField: [['floatField' => 5.5], ['floatField' => 6.6]]), + ]), + [ + 'count' => [ + ['key' => 1.1, 'count' => 1], + ['key' => 2.2, 'count' => 1], + ['key' => 3.3, 'count' => 1], + ['key' => 4.4, 'count' => 1], + ['key' => 5.5, 'count' => 1], + ['key' => 6.6, 'count' => 1], + ] + ], + ]; + yield 'Distinct, object list float field' => [ + new Distinct(name: 'distinct', field: 'objectListField.floatField'), + new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 3.3], ['floatField' => 4.4]]), + self::document(key: 'key3', objectListField: [['floatField' => 5.5], ['floatField' => 6.6]]), + ]), + ['distinct' => [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]], + ]; + } + + public static function objectListIntCases(): \Generator + { + yield 'Avg, object list int field' => [ + new Avg(name: 'avg', field: 'objectListField.intField'), + new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 3], ['intField' => 4]]), + self::document(key: 'key3', objectListField: [['intField' => 5], ['intField' => 6]]), + ]), + ['avg' => 3.5], + ]; + yield 'Min, object list int field' => [ + new Min(name: 'min', field: 'objectListField.intField'), + new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 3], ['intField' => 4]]), + self::document(key: 'key3', objectListField: [['intField' => 5], ['intField' => 6]]), + ]), + ['min' => 1], + ]; + yield 'Max, object list int field' => [ + new Max(name: 'max', field: 'objectListField.intField'), + new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 3], ['intField' => 4]]), + self::document(key: 'key3', objectListField: [['intField' => 5], ['intField' => 6]]), + ]), + ['max' => 6], + ]; + yield 'Sum, object list int field' => [ + new Sum(name: 'sum', field: 'objectListField.intField'), + new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 3], ['intField' => 4]]), + self::document(key: 'key3', objectListField: [['intField' => 5], ['intField' => 6]]), + ]), + ['sum' => 21], + ]; + yield 'Count, object list int field' => [ + new Count(name: 'count', field: 'objectListField.intField'), + new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 2], ['intField' => 3]]), + self::document(key: 'key2', objectListField: [['intField' => 2], ['intField' => 4]]), + self::document(key: 'key3', objectListField: [['intField' => 4], ['intField' => 2]]), + ]), + [ + 'count' => [ + ['key' => 2, 'count' => 3], + ['key' => 3, 'count' => 1], + ['key' => 4, 'count' => 2], + ] + ], + ]; + yield 'Distinct, object list int field' => [ + new Distinct(name: 'distinct', field: 'objectListField.intField'), + new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 1]]), + self::document(key: 'key2', objectListField: [['intField' => 3], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 2], ['intField' => 6]]), + ]), + ['distinct' => [1, 2, 3, 6]], + ]; + } + + public static function objectListBoolCases(): \Generator + { + yield 'Min, object list bool field' => [ + new Min(name: 'min', field: 'objectListField.boolField'), + new Documents([ + self::document(key: 'key1', objectListField: [['boolField' => true], ['boolField' => false]]), + self::document(key: 'key2', objectListField: [['boolField' => false], ['boolField' => true]]), + self::document(key: 'key3', objectListField: [['boolField' => true], ['boolField' => true]]), + ]), + ['min' => false], + ]; + yield 'Max, object list bool field' => [ + new Max(name: 'max', field: 'objectListField.boolField'), + new Documents([ + self::document(key: 'key1', objectListField: [['boolField' => true], ['boolField' => false]]), + self::document(key: 'key2', objectListField: [['boolField' => false], ['boolField' => true]]), + self::document(key: 'key3', objectListField: [['boolField' => true], ['boolField' => true]]), + ]), + ['max' => true], + ]; + yield 'Count, object list bool field' => [ + new Count(name: 'count', field: 'objectListField.boolField'), + new Documents([ + self::document(key: 'key1', objectListField: [['boolField' => true], ['boolField' => false]]), + self::document(key: 'key2', objectListField: [['boolField' => false]]), + self::document(key: 'key3', objectListField: [['boolField' => true]]), + ]), + [ + 'count' => [ + ['key' => false, 'count' => 2], + ['key' => true, 'count' => 2], + ] + ], + ]; + yield 'Distinct, object list bool field' => [ + new Distinct(name: 'distinct', field: 'objectListField.boolField'), + new Documents([ + self::document(key: 'key1', objectListField: [['boolField' => true], ['boolField' => false]]), + self::document(key: 'key2', objectListField: [['boolField' => false], ['boolField' => true]]), + self::document(key: 'key3', objectListField: [['boolField' => true], ['boolField' => true]]), + ]), + ['distinct' => [false, true]], + ]; + } + + public static function objectListDateCases(): \Generator + { + yield 'Min, object list date field' => [ + new Min(name: 'min', field: 'objectListField.dateField'), + new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-03 00:00:00.000'], ['dateField' => '2021-01-04 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-05 00:00:00.000'], ['dateField' => '2021-01-06 00:00:00.000']]), + ]), + ['min' => '2021-01-01 00:00:00.000'], + ]; + yield 'Max, object list date field' => [ + new Max(name: 'max', field: 'objectListField.dateField'), + new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-03 00:00:00.000'], ['dateField' => '2021-01-04 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-05 00:00:00.000'], ['dateField' => '2021-01-06 00:00:00.000']]), + ]), + ['max' => '2021-01-06 00:00:00.000'], + ]; + yield 'Count, object list date field' => [ + new Count(name: 'count', field: 'objectListField.dateField'), + new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-03 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-03 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-03 00:00:00.000']]), + ]), + [ + 'count' => [ + ['key' => '2021-01-01 00:00:00.000', 'count' => 2], + ['key' => '2021-01-02 00:00:00.000', 'count' => 1], + ['key' => '2021-01-03 00:00:00.000', 'count' => 3], + ] + ], + ]; + yield 'Distinct, object list date field' => [ + new Distinct(name: 'distinct', field: 'objectListField.dateField'), + new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-03 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-03 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-03 00:00:00.000']]), + ]), + ['distinct' => ['2021-01-01 00:00:00.000', '2021-01-02 00:00:00.000', '2021-01-03 00:00:00.000']], + ]; + } + + public static function translatedStringCases(): \Generator + { + yield 'Min, translated string field' => [ + new Min(name: 'min', field: 'translatedString'), + new Documents([ + self::document(key: 'key1', translatedString: ['en' => 'b', 'de' => 'b']), + self::document(key: 'key2', translatedString: ['en' => null, 'de' => 'd']), + self::document(key: 'key3', translatedString: ['de' => 'a']) + ]), + ['min' => 'a'] + ]; + yield 'Max, translated string field' => [ + new Max(name: 'max', field: 'translatedString'), + new Documents([ + self::document(key: 'key1', translatedString: ['en' => 'b', 'de' => 'b']), + self::document(key: 'key2', translatedString: ['en' => null, 'de' => 'd']), + self::document(key: 'key3', translatedString: ['de' => 'a']) + ]), + ['max' => 'd'] + ]; + yield 'Count, translated string field' => [ + new Count(name: 'count', field: 'translatedString'), + new Documents([ + self::document(key: 'key1', translatedString: ['en' => 'a']), + self::document(key: 'key2', translatedString: ['en' => null, 'de' => 'a']), + self::document(key: 'key3', translatedString: ['en' => 'b', 'de' => 'a']), + self::document(key: 'key4', translatedString: ['de' => 'c']), + ]), + [ + 'count' => [ + ['key' => 'a', 'count' => 2], + ['key' => 'b', 'count' => 1], + ['key' => 'c', 'count' => 1], + ] + ] + ]; + yield 'Distinct, translated string field' => [ + new Distinct(name: 'distinct', field: 'translatedString'), + new Documents([ + self::document(key: 'key1', translatedString: ['en' => 'a']), + self::document(key: 'key2', translatedString: ['en' => null, 'de' => 'a']), + self::document(key: 'key3', translatedString: ['en' => 'b', 'de' => 'a']), + self::document(key: 'key4', translatedString: ['de' => 'c']), + ]), + ['distinct' => ['a', 'b', 'c']] + ]; + } + + public static function translatedIntCases(): \Generator + { + yield 'Avg, translated int field' => [ + new Avg(name: 'avg', field: 'translatedInt'), + new Documents([ + self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key2', translatedInt: ['en' => null, 'de' => 3]), + self::document(key: 'key3', translatedInt: ['de' => 8]), + ]), + ['avg' => 4], + ]; + yield 'Min, translated int field' => [ + new Min(name: 'min', field: 'translatedInt'), + new Documents([ + self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key2', translatedInt: ['en' => null, 'de' => 3]), + self::document(key: 'key3', translatedInt: ['de' => 5]), + ]), + ['min' => 1], + ]; + yield 'Max, translated int field' => [ + new Max(name: 'max', field: 'translatedInt'), + new Documents([ + self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key2', translatedInt: ['en' => null, 'de' => 3]), + self::document(key: 'key3', translatedInt: ['de' => 5]), + ]), + ['max' => 5], + ]; + yield 'Sum, translated int field' => [ + new Sum(name: 'sum', field: 'translatedInt'), + new Documents([ + self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key2', translatedInt: ['en' => null, 'de' => 3]), + self::document(key: 'key3', translatedInt: ['de' => 5]), + ]), + ['sum' => 9], + ]; + yield 'Count, translated int field' => [ + new Count(name: 'count', field: 'translatedInt'), + new Documents([ + self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key2', translatedInt: ['en' => null, 'de' => 1]), + self::document(key: 'key3', translatedInt: ['de' => 5]), + self::document(key: 'key4', translatedInt: ['en' => 5]), + ]), + [ + 'count' => [ + ['key' => 1, 'count' => 2], + ['key' => 5, 'count' => 2], + ] + ], + ]; + yield 'Distinct, translated int field' => [ + new Distinct(name: 'distinct', field: 'translatedInt'), + new Documents([ + self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key2', translatedInt: ['en' => null, 'de' => 3]), + self::document(key: 'key3', translatedInt: ['de' => 5]), + ]), + ['distinct' => [1, 3, 5]] + ]; + } + + public static function translatedFloatCases(): \Generator + { + yield 'Avg, translated float field' => [ + new Avg(name: 'avg', field: 'translatedFloat'), + new Documents([ + self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key2', translatedFloat: ['en' => null, 'de' => 3.3]), + self::document(key: 'key3', translatedFloat: ['de' => 8.8]), + ]), + ['avg' => 4.4], + ]; + yield 'Min, translated float field' => [ + new Min(name: 'min', field: 'translatedFloat'), + new Documents([ + self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key2', translatedFloat: ['en' => null, 'de' => 3.3]), + self::document(key: 'key3', translatedFloat: ['de' => 5.5]), + ]), + ['min' => 1.1], + ]; + yield 'Max, translated float field' => [ + new Max(name: 'max', field: 'translatedFloat'), + new Documents([ + self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key2', translatedFloat: ['en' => null, 'de' => 3.3]), + self::document(key: 'key3', translatedFloat: ['de' => 5.5]), + ]), + ['max' => 5.5], + ]; + yield 'Sum, translated float field' => [ + new Sum(name: 'sum', field: 'translatedFloat'), + new Documents([ + self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key2', translatedFloat: ['en' => null, 'de' => 3.3]), + self::document(key: 'key3', translatedFloat: ['de' => 5.5]), + ]), + ['sum' => 9.9], + ]; + yield 'Count, translated float field' => [ + new Count(name: 'count', field: 'translatedFloat'), + new Documents([ + self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key2', translatedFloat: ['en' => null, 'de' => 1.1]), + self::document(key: 'key3', translatedFloat: ['de' => 5.5]), + self::document(key: 'key4', translatedFloat: ['en' => 5.5]), + ]), + [ + 'count' => [ + ['key' => 1.1, 'count' => 2], + ['key' => 5.5, 'count' => 2], + ] + ], + ]; + yield 'Distinct, translated float field' => [ + new Distinct(name: 'distinct', field: 'translatedFloat'), + new Documents([ + self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key2', translatedFloat: ['en' => null, 'de' => 3.3]), + self::document(key: 'key3', translatedFloat: ['de' => 5.5]), + ]), + ['distinct' => [1.1, 3.3, 5.5]] + ]; + } + + public static function translatedBoolCases(): \Generator + { + yield 'Min, translated bool field' => [ + new Min(name: 'min', field: 'translatedBool'), + new Documents([ + self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), + self::document(key: 'key2', translatedBool: ['en' => null, 'de' => true]), + self::document(key: 'key3', translatedBool: ['de' => false]) + ]), + ['min' => false] + ]; + yield 'Max, translated bool field' => [ + new Max(name: 'max', field: 'translatedBool'), + new Documents([ + self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), + self::document(key: 'key2', translatedBool: ['en' => null, 'de' => true]), + self::document(key: 'key3', translatedBool: ['de' => false]) + ]), + ['max' => true] + ]; + yield 'Count, translated bool field' => [ + new Count(name: 'count', field: 'translatedBool'), + new Documents([ + self::document(key: 'key1', translatedBool: ['en' => true]), + self::document(key: 'key2', translatedBool: ['en' => null, 'de' => true]), + self::document(key: 'key3', translatedBool: ['en' => false, 'de' => true]), + self::document(key: 'key4', translatedBool: ['de' => false]), + ]), + [ + 'count' => [ + ['key' => false, 'count' => 2], + ['key' => true, 'count' => 2], + ] + ] + ]; + yield 'Distinct, translated bool field' => [ + new Distinct(name: 'distinct', field: 'translatedBool'), + new Documents([ + self::document(key: 'key1', translatedBool: ['en' => true]), + self::document(key: 'key2', translatedBool: ['en' => null, 'de' => true]), + self::document(key: 'key3', translatedBool: ['en' => false, 'de' => true]), + self::document(key: 'key4', translatedBool: ['de' => false]), + ]), + ['distinct' => [false, true]] + ]; + } + + public static function translatedDateCases(): \Generator + { + yield 'Min, translated date field' => [ + new Min(name: 'min', field: 'translatedDate'), + new Documents([ + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), + self::document(key: 'key2', dateField: '2021-01-01 00:00:00.000', translatedDate: ['en' => null, 'de' => '2021-01-03 00:00:00.000']), + self::document(key: 'key3', dateField: '2021-01-01 00:00:00.000', translatedDate: ['de' => '2021-01-04 00:00:00.000']), + ]), + ['min' => '2021-01-01 00:00:00.000'], + ]; + yield 'Max, translated date field' => [ + new Max(name: 'max', field: 'translatedDate'), + new Documents([ + self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), + self::document(key: 'key2', translatedDate: ['en' => null, 'de' => '2021-01-03 00:00:00.000']), + self::document(key: 'key3', translatedDate: ['de' => '2021-01-04 00:00:00.000']), + ]), + ['max' => '2021-01-04 00:00:00.000'], + ]; + yield 'Count, translated date field' => [ + new Count(name: 'count', field: 'translatedDate'), + new Documents([ + self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), + self::document(key: 'key2', translatedDate: ['en' => null, 'de' => '2021-01-03 00:00:00.000']), + self::document(key: 'key3', translatedDate: ['de' => '2021-01-04 00:00:00.000']), + self::document(key: 'key4', translatedDate: ['en' => '2021-01-04 00:00:00.000']), + ]), + [ + 'count' => [ + ['key' => '2021-01-01 00:00:00.000', 'count' => 1], + ['key' => '2021-01-03 00:00:00.000', 'count' => 1], + ['key' => '2021-01-04 00:00:00.000', 'count' => 2], + ] + ], + ]; + yield 'Distinct, translated date field' => [ + new Distinct(name: 'distinct', field: 'translatedDate'), + new Documents([ + self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), + self::document(key: 'key2', translatedDate: ['en' => null, 'de' => '2021-01-03 00:00:00.000']), + self::document(key: 'key3', translatedDate: ['de' => '2021-01-04 00:00:00.000']), + ]), + ['distinct' => ['2021-01-01 00:00:00.000', '2021-01-03 00:00:00.000', '2021-01-04 00:00:00.000']] + ]; + } + + /** + * @param array $expected + */ + #[DataProvider('stringCases')] + #[DataProvider('textCases')] + #[DataProvider('intCases')] + #[DataProvider('floatCases')] + #[DataProvider('boolCases')] + #[DataProvider('dateCases')] + // #[DataProvider('listStringCases')] + // #[DataProvider('listFloatCases')] + // #[DataProvider('listIntCases')] + // #[DataProvider('listDateCases')] + #[DataProvider('objectStringCases')] + #[DataProvider('objectFloatCases')] + #[DataProvider('objectIntCases')] + #[DataProvider('objectBoolCases')] + #[DataProvider('objectDateCases')] + #[DataProvider('objectListStringCases')] + #[DataProvider('objectListFloatCases')] + #[DataProvider('objectListIntCases')] + #[DataProvider('objectListBoolCases')] + #[DataProvider('objectListDateCases')] + #[DataProvider('translatedStringCases')] + #[DataProvider('translatedIntCases')] + #[DataProvider('translatedFloatCases')] + #[DataProvider('translatedBoolCases')] + #[DataProvider('translatedDateCases')] + public function testStorage( + Aggregation $aggregations, + Documents $input, + array $expected + ): void { + $storage = $this->getStorage(); + + $storage->store($input); + + try { + $context = new StorageContext(languages: ['en', 'de']); + + $loaded = $storage->aggregate( + aggregations: [$aggregations], + criteria: new Criteria(), + context: $context + ); + } catch (NotSupportedByEngine $e) { + static::markTestIncomplete($e->getMessage()); + } + + static::assertEquals($expected, $loaded); + } +} diff --git a/tests/Common/FilterStorageTestBase.php b/tests/Common/FilterStorageTestBase.php index 2baecd8..545ac74 100644 --- a/tests/Common/FilterStorageTestBase.php +++ b/tests/Common/FilterStorageTestBase.php @@ -22,19 +22,62 @@ use Shopware\Storage\Common\Filter\Type\Not; use Shopware\Storage\Common\Filter\Type\Prefix; use Shopware\Storage\Common\Filter\Type\Suffix; -use Shopware\Storage\Common\Schema\Field; -use Shopware\Storage\Common\Schema\FieldType; -use Shopware\Storage\Common\Schema\Schema; use Shopware\Storage\Common\Storage; use Shopware\Storage\Common\StorageContext; abstract class FilterStorageTestBase extends TestCase { - public const TEST_STORAGE = 'test_storage'; + use SchemaStorageTrait; abstract public function getStorage(): FilterAware&Storage; - #[DataProvider('storageProvider')] + #[DataProvider('translatedIntCases')] + public function testDebug( + Documents $input, + Criteria $criteria, + Result $expected + ): void { + $this->testStorage( + input: $input, + criteria: $criteria, + expected: $expected + ); + } + + #[DataProvider('keysCases')] + #[DataProvider('paginationCases')] + #[DataProvider('nestedObjectCases')] + #[DataProvider('stringCases')] + #[DataProvider('textCases')] + #[DataProvider('intCases')] + #[DataProvider('floatCases')] + #[DataProvider('boolCases')] + #[DataProvider('dateCases')] + #[DataProvider('listStringCases')] + #[DataProvider('listFloatCases')] + #[DataProvider('listIntCases')] + #[DataProvider('listDateCases')] + #[DataProvider('objectStringCases')] + #[DataProvider('objectFloatCases')] + #[DataProvider('objectIntCases')] + #[DataProvider('objectBoolCases')] + #[DataProvider('objectDateCases')] + #[DataProvider('objectListStringCases')] + #[DataProvider('objectListFloatCases')] + #[DataProvider('objectListIntCases')] + #[DataProvider('objectListBoolCases')] + #[DataProvider('objectListDateCases')] + #[DataProvider('translatedStringCases')] + #[DataProvider('translatedIntCases')] + #[DataProvider('translatedFloatCases')] + #[DataProvider('translatedBoolCases')] + #[DataProvider('translatedDateCases')] + //#[DataProvider('translatedListCases')] + //#[DataProvider('translatedObjectStringCases')] + //#[DataProvider('translatedObjectIntCases')] + //#[DataProvider('translatedObjectFloatCases')] + //#[DataProvider('translatedObjectBoolCases')] + //#[DataProvider('translatedObjectDateCases')] final public function testStorage(Documents $input, Criteria $criteria, Result $expected): void { $storage = $this->getStorage(); @@ -50,30 +93,6 @@ final public function testStorage(Documents $input, Criteria $criteria, Result $ static::assertEquals($expected, $loaded); } - #[DataProvider('debugProvider')] - public function testDebug( - Documents $input, - Criteria $criteria, - Result $expected - ): void { - $storage = $this->getStorage(); - - $storage->store($input); - - $loaded = $storage->filter($criteria, new StorageContext(languages: ['en', 'de'])); - - static::assertEquals($expected, $loaded); - } - - public static function debugProvider(): \Generator - { - yield 'Smoke test' => [ - 'input' => new Documents(), - 'criteria' => new Criteria(), - 'expected' => new Result([]) - ]; - } - /** * @param string[] $remove * @param array $expected @@ -100,13 +119,13 @@ final public function testRemove(Documents $input, array $remove, array $expecte final public static function removeProvider(): \Generator { - yield 'Test call remove with empty storage' => [ + yield 'call remove with empty storage' => [ 'input' => new Documents(), 'remove' => ['key1', 'key2'], 'expected' => [] ]; - yield 'Test call remove with single key' => [ + yield 'call remove with single key' => [ 'input' => new Documents([ self::document(key: 'key1'), self::document(key: 'key2'), @@ -119,7 +138,7 @@ final public static function removeProvider(): \Generator ] ]; - yield 'Test call remove with multiple keys' => [ + yield 'call remove with multiple keys' => [ 'input' => new Documents([ self::document(key: 'key1'), self::document(key: 'key2'), @@ -132,102 +151,9 @@ final public static function removeProvider(): \Generator ]; } - final protected function getSchema(): Schema - { - return new Schema( - source: self::TEST_STORAGE, - fields: [ - new Field('stringField', FieldType::STRING), - new Field('textField', FieldType::TEXT), - new Field('intField', FieldType::INT), - new Field('floatField', FieldType::FLOAT), - new Field('boolField', FieldType::BOOL), - new Field('dateField', FieldType::DATETIME), - new Field('listField', FieldType::LIST), - - new Field('translatedString', FieldType::STRING, ['translated' => true]), - new Field('translatedText', FieldType::TEXT, ['translated' => true]), - new Field('translatedInt', FieldType::INT, ['translated' => true]), - new Field('translatedFloat', FieldType::FLOAT, ['translated' => true]), - new Field('translatedBool', FieldType::BOOL, ['translated' => true]), - new Field('translatedDate', FieldType::DATETIME, ['translated' => true]), - new Field('translatedList', FieldType::LIST, ['translated' => true]), - - new Field('objectField', FieldType::OBJECT, [], [ - new Field('foo', FieldType::STRING), - new Field('fooInt', FieldType::INT), - new Field('fooFloat', FieldType::FLOAT), - new Field('fooBool', FieldType::BOOL), - new Field('fooDate', FieldType::DATETIME), - new Field('translatedFoo', FieldType::STRING, ['translated' => true]), - new Field('translatedFooInt', FieldType::INT, ['translated' => true]), - new Field('translatedFooFloat', FieldType::FLOAT, ['translated' => true]), - new Field('translatedFooBool', FieldType::BOOL, ['translated' => true]), - new Field('translatedFooDate', FieldType::DATETIME, ['translated' => true]), - new Field('fooObj', FieldType::OBJECT, [], [ - new Field('bar', FieldType::STRING), - ]), - ]), - - new Field('objectListField', FieldType::OBJECT_LIST, [], [ - new Field('foo', FieldType::STRING), - new Field('fooInt', FieldType::INT), - new Field('fooFloat', FieldType::FLOAT), - new Field('fooBool', FieldType::BOOL), - new Field('fooDate', FieldType::DATETIME), - new Field('translatedFoo', FieldType::STRING, ['translated' => true]), - new Field('translatedFooInt', FieldType::INT, ['translated' => true]), - new Field('translatedFooFloat', FieldType::FLOAT, ['translated' => true]), - new Field('translatedFooBool', FieldType::BOOL, ['translated' => true]), - new Field('translatedFooDate', FieldType::DATETIME, ['translated' => true]), - new Field('fooObj', FieldType::OBJECT, [], [ - new Field('bar', FieldType::STRING), - new Field('translatedBar', FieldType::STRING, ['translated' => true]), - ]), - ]), - ] - ); - } - - final public static function storageProvider(): \Generator + final public static function stringCases(): \Generator { - yield 'Smoke test' => [ - 'input' => new Documents(), - 'criteria' => new Criteria(), - 'expected' => new Result([]) - ]; - yield 'Test keys and values' => [ - 'input' => new Documents([ - self::document(key: 'key1'), - self::Document(key: 'key2'), - self::Document(key: 'key3'), - ]), - 'criteria' => new Criteria( - primaries: ['key1', 'key2'] - ), - 'expected' => new Result([ - self::document(key: 'key1'), - self::document(key: 'key2'), - ]) - ]; - yield 'Test pagination' => [ - 'input' => new Documents([ - self::document(key: 'key1'), - self::document(key: 'key2'), - self::document(key: 'key3'), - self::document(key: 'key4'), - self::document(key: 'key5'), - ]), - 'criteria' => new Criteria( - paging: new Page(page: 2, limit: 2) - ), - 'expected' => new Result([ - self::document(key: 'key3'), - self::document(key: 'key4'), - ]) - ]; - - yield 'Test string field equals filter' => [ + yield 'string field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -243,7 +169,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'foo'), ]) ]; - yield 'Test string field equals any filter' => [ + yield 'string field, equals any filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -259,7 +185,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', stringField: 'bar'), ]) ]; - yield 'Test string field not filter' => [ + yield 'string field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -275,7 +201,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'baz'), ]) ]; - yield 'Test string field not any filter' => [ + yield 'string field, not any filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -290,7 +216,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'baz'), ]) ]; - yield 'Test string field contains filter' => [ + yield 'string field, contains filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -306,7 +232,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'baz'), ]) ]; - yield 'Test string field starts-with filter' => [ + yield 'string field, starts-with filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -322,7 +248,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'baz'), ]) ]; - yield 'Test string field ends-with filter' => [ + yield 'string field, ends-with filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -338,7 +264,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'foo-bar'), ]) ]; - yield 'Test string field gte filter' => [ + yield 'string field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'a'), self::document(key: 'key2', stringField: 'b'), @@ -354,7 +280,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'c'), ]) ]; - yield 'Test string field lte filter' => [ + yield 'string field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'a'), self::document(key: 'key2', stringField: 'b'), @@ -370,7 +296,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', stringField: 'b'), ]) ]; - yield 'Test string field gt filter' => [ + yield 'string field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'a'), self::document(key: 'key2', stringField: 'b'), @@ -385,7 +311,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'c'), ]) ]; - yield 'Test string field lt filter' => [ + yield 'string field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'a'), self::document(key: 'key2', stringField: 'b'), @@ -400,7 +326,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key1', stringField: 'a'), ]) ]; - yield 'Test string field gte and lte filter' => [ + yield 'string field, gte and lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'a'), self::document(key: 'key2', stringField: 'b'), @@ -418,7 +344,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: 'c'), ]) ]; - yield 'Test string field null value equals filter' => [ + yield 'string field, null value, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -433,7 +359,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', stringField: null) ]) ]; - yield 'Test string field null value not filter' => [ + yield 'string field, null value, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', stringField: 'foo'), self::document(key: 'key2', stringField: 'bar'), @@ -449,8 +375,11 @@ final public static function storageProvider(): \Generator self::document('key2', stringField: 'bar') ]) ]; + } - yield 'Test text field equals filter' => [ + final public static function textCases(): \Generator + { + yield 'text field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -466,7 +395,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'foo'), ]) ]; - yield 'Test text field equals any filter' => [ + yield 'text field, equals any filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -482,7 +411,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', textField: 'bar'), ]) ]; - yield 'Test text field not filter' => [ + yield 'text field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -498,7 +427,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'baz'), ]) ]; - yield 'Test text field not any filter' => [ + yield 'text field, not any filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -513,7 +442,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'baz'), ]) ]; - yield 'Test text field contains filter' => [ + yield 'text field, contains filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -529,7 +458,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'baz'), ]) ]; - yield 'Test text field starts-with filter' => [ + yield 'text field, starts-with filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -545,7 +474,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'baz'), ]) ]; - yield 'Test text field ends-with filter' => [ + yield 'text field, ends-with filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'foo'), self::document(key: 'key2', textField: 'bar'), @@ -561,7 +490,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'foo-bar'), ]) ]; - yield 'Test text field gte filter' => [ + yield 'text field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'a'), self::document(key: 'key2', textField: 'b'), @@ -577,7 +506,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'c'), ]) ]; - yield 'Test text field lte filter' => [ + yield 'text field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'a'), self::document(key: 'key2', textField: 'b'), @@ -593,7 +522,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', textField: 'b'), ]) ]; - yield 'Test text field gt filter' => [ + yield 'text field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'a'), self::document(key: 'key2', textField: 'b'), @@ -608,7 +537,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'c'), ]) ]; - yield 'Test text field lt filter' => [ + yield 'text field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'a'), self::document(key: 'key2', textField: 'b'), @@ -623,7 +552,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key1', textField: 'a'), ]) ]; - yield 'Test text field gte and lte filter' => [ + yield 'text field, gte and lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', textField: 'a'), self::document(key: 'key2', textField: 'b'), @@ -641,182 +570,11 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', textField: 'c'), ]) ]; + } - yield 'Test date field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'dateField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - ]) - ]; - yield 'Test date field equals any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'dateField', value: ['2021-01-01', '2021-01-02']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - ]) - ]; - yield 'Test date field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'dateField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]) - ]; - yield 'Test date field not any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'dateField', value: ['2021-01-01', '2021-01-02']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]) - ]; - yield 'Test date field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'dateField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]) - ]; - yield 'Test date field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'dateField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - ]) - ]; - yield 'Test date field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'dateField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]) - ]; - yield 'Test date field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'dateField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - ]) - ]; - yield 'Test date field gte and lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - self::document(key: 'key4', dateField: '2021-01-04 00:00:00.000'), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'dateField', value: '2021-01-02'), - new Lte(field: 'dateField', value: '2021-01-03'), - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), - ]) - ]; - yield 'Test date field null value equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01'), - self::document(key: 'key2', dateField: '2021-01-02'), - self::document(key: 'key3', dateField: null), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'dateField', value: null) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', dateField: null) - ]) - ]; - yield 'Test date field null value not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), - self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), - self::document(key: 'key3', dateField: null), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'dateField', value: null) - ] - ), - 'expected' => new Result([ - self::document('key1', dateField: '2021-01-01 00:00:00.000'), - self::document('key2', dateField: '2021-01-02 00:00:00.000') - ]) - ]; - - yield 'Test int field equals filter' => [ + final public static function intCases(): \Generator + { + yield 'int field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -824,14 +582,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'intField', value: 2) + new Equals(field: 'intField', value: 2) ] ), 'expected' => new Result([ self::document(key: 'key2', intField: 2), ]) ]; - yield 'Test int field equals any filter' => [ + yield 'int field, equals any filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -839,7 +597,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'intField', value: [1, 2]) + new Any(field: 'intField', value: [1, 2]) ] ), 'expected' => new Result([ @@ -847,7 +605,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', intField: 2), ]) ]; - yield 'Test int field not filter' => [ + yield 'int field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -855,7 +613,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'intField', value: 2) + new Not(field: 'intField', value: 2) ] ), 'expected' => new Result([ @@ -863,7 +621,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', intField: 3), ]) ]; - yield 'Test int field not any filter' => [ + yield 'int field, not any filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -871,14 +629,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'intField', value: [1, 2]) + new Neither(field: 'intField', value: [1, 2]) ] ), 'expected' => new Result([ self::document(key: 'key3', intField: 3), ]) ]; - yield 'Test int field gte filter' => [ + yield 'int field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -886,7 +644,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'intField', value: 2) + new Gte(field: 'intField', value: 2) ] ), 'expected' => new Result([ @@ -894,7 +652,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', intField: 3), ]) ]; - yield 'Test int field lte filter' => [ + yield 'int field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -902,7 +660,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'intField', value: 2) + new Lte(field: 'intField', value: 2) ] ), 'expected' => new Result([ @@ -910,7 +668,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', intField: 2), ]) ]; - yield 'Test int field gt filter' => [ + yield 'int field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -918,14 +676,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'intField', value: 2) + new Gt(field: 'intField', value: 2) ] ), 'expected' => new Result([ self::document(key: 'key3', intField: 3), ]) ]; - yield 'Test int field lt filter' => [ + yield 'int field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -933,14 +691,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'intField', value: 2) + new Lt(field: 'intField', value: 2) ] ), 'expected' => new Result([ self::document(key: 'key1', intField: 1), ]) ]; - yield 'Test int field gte and lte filter' => [ + yield 'int field, gte and lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -949,8 +707,8 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'intField', value: 2), - new Lte(field: 'intField', value: 3), + new Gte(field: 'intField', value: 2), + new Lte(field: 'intField', value: 3), ] ), 'expected' => new Result([ @@ -958,7 +716,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', intField: 3), ]) ]; - yield 'Test int field null value equals filter' => [ + yield 'int field, null value, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -966,14 +724,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'intField', value: null) + new Equals(field: 'intField', value: null) ] ), 'expected' => new Result([ self::document(key: 'key3', intField: null) ]) ]; - yield 'Test int field null value not filter' => [ + yield 'int field, null value, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', intField: 1), self::document(key: 'key2', intField: 2), @@ -981,7 +739,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'intField', value: null) + new Not(field: 'intField', value: null) ] ), 'expected' => new Result([ @@ -989,8 +747,11 @@ final public static function storageProvider(): \Generator self::document('key2', intField: 2) ]) ]; + } - yield 'Test float field equals filter' => [ + final public static function floatCases(): \Generator + { + yield 'float field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -998,14 +759,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'floatField', value: 2.2) + new Equals(field: 'floatField', value: 2.2) ] ), 'expected' => new Result([ self::document(key: 'key2', floatField: 2.2), ]) ]; - yield 'Test float field equals any filter' => [ + yield 'float field, equals any filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1013,7 +774,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'floatField', value: [1.1, 2.2]) + new Any(field: 'floatField', value: [1.1, 2.2]) ] ), 'expected' => new Result([ @@ -1021,7 +782,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', floatField: 2.2), ]) ]; - yield 'Test float field not filter' => [ + yield 'float field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1029,7 +790,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'floatField', value: 2.2) + new Not(field: 'floatField', value: 2.2) ] ), 'expected' => new Result([ @@ -1037,7 +798,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', floatField: 3.3), ]) ]; - yield 'Test float field not any filter' => [ + yield 'float field, not any filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1045,14 +806,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'floatField', value: [1.1, 2.2]) + new Neither(field: 'floatField', value: [1.1, 2.2]) ] ), 'expected' => new Result([ self::document(key: 'key3', floatField: 3.3), ]) ]; - yield 'Test float field gte filter' => [ + yield 'float field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1060,7 +821,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'floatField', value: 2.2) + new Gte(field: 'floatField', value: 2.2) ] ), 'expected' => new Result([ @@ -1068,7 +829,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', floatField: 3.3), ]) ]; - yield 'Test float field lte filter' => [ + yield 'float field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1076,7 +837,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'floatField', value: 2.2) + new Lte(field: 'floatField', value: 2.2) ] ), 'expected' => new Result([ @@ -1084,7 +845,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key2', floatField: 2.2), ]) ]; - yield 'Test float field gt filter' => [ + yield 'float field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1092,14 +853,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'floatField', value: 2.2) + new Gt(field: 'floatField', value: 2.2) ] ), 'expected' => new Result([ self::document(key: 'key3', floatField: 3.3), ]) ]; - yield 'Test float field lt filter' => [ + yield 'float field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1107,14 +868,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'floatField', value: 2.2) + new Lt(field: 'floatField', value: 2.2) ] ), 'expected' => new Result([ self::document(key: 'key1', floatField: 1.1), ]) ]; - yield 'Test float field gte and lte filter' => [ + yield 'float field, gte and lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1124,7 +885,7 @@ final public static function storageProvider(): \Generator 'criteria' => new Criteria( filters: [ new Gte(field: 'floatField', value: 2.2), - new Lte(field: 'floatField', value: 3.3), + new Lte(field: 'floatField', value: 3.3), ] ), 'expected' => new Result([ @@ -1132,7 +893,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', floatField: 3.3), ]) ]; - yield 'Test float field null value equals filter' => [ + yield 'float field, null value, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1140,14 +901,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'floatField', value: null) + new Equals(field: 'floatField', value: null) ] ), 'expected' => new Result([ self::document(key: 'key3', floatField: null) ]) ]; - yield 'Test float field null value not filter' => [ + yield 'float field, null value, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', floatField: 1.1), self::document(key: 'key2', floatField: 2.2), @@ -1155,7 +916,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'floatField', value: null) + new Not(field: 'floatField', value: null) ] ), 'expected' => new Result([ @@ -1163,8 +924,11 @@ final public static function storageProvider(): \Generator self::document('key2', floatField: 2.2) ]) ]; + } - yield 'Test bool field with equals filter' => [ + final public static function boolCases(): \Generator + { + yield 'bool field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', boolField: true), self::document(key: 'key2', boolField: false), @@ -1172,7 +936,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'boolField', value: true) + new Equals(field: 'boolField', value: true) ] ), 'expected' => new Result([ @@ -1180,7 +944,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', boolField: true), ]) ]; - yield 'Test bool field with not filter' => [ + yield 'bool field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', boolField: true), self::document(key: 'key2', boolField: false), @@ -1188,1331 +952,1191 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'boolField', value: true) + new Not(field: 'boolField', value: true) ] ), 'expected' => new Result([ self::document(key: 'key2', boolField: false), ]) ]; + } - yield 'Test object field equals filter' => [ + final public static function dateCases(): \Generator + { + yield 'date field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'objectField.foo', value: 'baz') + new Equals(field: 'dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['foo' => 'baz']), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), ]) ]; - yield 'Test object field equals any filter' => [ + yield 'date field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'objectField.foo', value: ['baz', 'qux']) + new Any(field: 'dateField', value: ['2021-01-01', '2021-01-02']) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), ]) ]; - yield 'Test object field not filter' => [ + yield 'date field, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'objectField.foo', value: 'baz') + new Not(field: 'dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]) ]; - yield 'Test object field not any filter' => [ + yield 'date field, not any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'objectField.foo', value: ['baz', 'qux']) + new Neither(field: 'dateField', value: ['2021-01-01', '2021-01-02']) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - ]) - ]; - yield 'Test object field contains filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'objectField.foo', value: 'ba') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - ]) - ]; - yield 'Test object field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]) - ]; - yield 'Test object field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - ]) - ]; - yield 'Test object field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]) - ]; - yield 'Test object field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - ]) - ]; - // yield 'Test object field null value equals filter' => [ - // 'input' => new Documents([ - // self::document(key: 'key1', objectField: ['foo' => 'bar']), - // self::document(key: 'key2', objectField: ['foo' => 'baz']), - // self::document(key: 'key3', objectField: null), - // self::document(key: 'key4', objectField: ['foo' => null]), - // ]), - // 'criteria' => new FilterCriteria( - // filters: [ - // new Equals(field: 'objectField.foo', value: null) - // ] - // ), - // 'expected' => new FilterResult([ - // self::document(key: 'key4', objectField: ['foo' => null]), - // ]) - // ]; - // yield 'Test object field nested null value equals filter' => [ - // 'input' => new Documents([ - // self::document(key: 'key1', objectField: ['foo' => 'bar']), - // self::document(key: 'key2', objectField: ['foo' => 'baz']), - // self::document(key: 'key3', objectField: null), - // self::document(key: 'key4', objectField: ['foo' => null]), - // ]), - // 'criteria' => new FilterCriteria( - // filters: [ - // new Equals(field: 'objectField', value: null) - // ] - // ), - // 'expected' => new FilterResult([ - // self::document(key: 'key3', objectField: null), - // ]) - // ]; - // yield 'Test object field null value not filter' => [ - // 'input' => new Documents([ - // self::document(key: 'key1', objectField: ['foo' => 'bar']), - // self::document(key: 'key2', objectField: ['foo' => 'baz']), - // self::document(key: 'key3', objectField: null), - // ]), - // 'criteria' => new Criteria( - // filters: [ - // new Not(field: 'objectField.foo', value: null) - // ] - // ), - // 'expected' => new Result([ - // self::document('key1', objectField: ['foo' => 'bar']), - // self::document('key2', objectField: ['foo' => 'baz']) - // ]) - // ]; - - yield 'Test object field equals filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooInt' => 2]), - ]) - ]; - yield 'Test object field equals any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectField.fooInt', value: [1, 2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]) ]; - yield 'Test object field not filter and int value' => [ + yield 'date field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'objectField.fooInt', value: 2) + new Gte(field: 'dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]) ]; - yield 'Test object field not any filter and int values' => [ + yield 'date field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'objectField.fooInt', value: [1, 2]) + new Lte(field: 'dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), ]) ]; - yield 'Test object field gte filter and int value' => [ + yield 'date field, gt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectField.fooInt', value: 2) + new Gt(field: 'dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]) ]; - yield 'Test object field lte filter and int value' => [ + yield 'date field, lt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'objectField.fooInt', value: 2) + new Lt(field: 'dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), ]) ]; - yield 'Test object field gt filter and int value' => [ + yield 'date field, gte and lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), + self::document(key: 'key4', dateField: '2021-01-04 00:00:00.000'), ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'objectField.fooInt', value: 2) + new Gte(field: 'dateField', value: '2021-01-02'), + new Lte(field: 'dateField', value: '2021-01-03'), ] ), 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: '2021-01-03 00:00:00.000'), ]) ]; - yield 'Test object field lt filter and int value' => [ + yield 'date field, null value, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document(key: 'key1', dateField: '2021-01-01'), + self::document(key: 'key2', dateField: '2021-01-02'), + self::document(key: 'key3', dateField: null), ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'objectField.fooInt', value: 2) + new Equals(field: 'dateField', value: null) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), + self::document(key: 'key3', dateField: null) ]) ]; - yield 'Test object field gte and lte filter and int values' => [ + yield 'date field, null value, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - self::document(key: 'key4', objectField: ['fooInt' => 4]), + self::document(key: 'key1', dateField: '2021-01-01 00:00:00.000'), + self::document(key: 'key2', dateField: '2021-01-02 00:00:00.000'), + self::document(key: 'key3', dateField: null), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectField.fooInt', value: 2), - new Lte(field: 'objectField.fooInt', value: 3), + new Not(field: 'dateField', value: null) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), + self::document('key1', dateField: '2021-01-01 00:00:00.000'), + self::document('key2', dateField: '2021-01-02 00:00:00.000') ]) ]; + } - yield 'Test object field equals filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - ]) - ]; - yield 'Test object field equals any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectField.fooFloat', value: [1.1, 2.2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - ]) - ]; - yield 'Test object field not filter and float values' => [ + final public static function listStringCases(): \Generator + { + yield 'list field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'objectField.fooFloat', value: 2.2) + new Equals(field: 'listField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key2', listField: ['foo', 'baz']), ]) ]; - yield 'Test object field not any filter and float values' => [ + yield 'list field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'objectField.fooFloat', value: [1.1, 2.2]) + new Any(field: 'listField', value: ['baz', 'qux']) ] ), 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]) ]; - yield 'Test object field gte filter and float values' => [ + yield 'list field, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectField.fooFloat', value: 2.2) + new Not(field: 'listField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]) ]; - yield 'Test object field lte filter and float values' => [ + yield 'list field, not any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'objectField.fooFloat', value: 2.2) + new Neither(field: 'listField', value: ['baz', 'qux']) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), + self::document(key: 'key1', listField: ['foo', 'bar']), ]) ]; - yield 'Test object field gt filter and float values' => [ + yield 'list field, contains filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key3', listField: ['foo', 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'objectField.fooFloat', value: 2.2) + new Contains(field: 'listField', value: 'ba') ] ), 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key2', listField: ['foo', 'baz']), ]) ]; - yield 'Test object field lt filter and float values' => [ + yield 'list field, null value, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: null), ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'objectField.fooFloat', value: 2.2) + new Equals(field: 'listField', value: null) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), + self::document(key: 'key3', listField: null) ]) ]; - yield 'Test object field gte and lte filter and float values' => [ + yield 'list field, null value, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - self::document(key: 'key4', objectField: ['fooFloat' => 4.4]), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: null), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectField.fooFloat', value: 2.2), - new Lte(field: 'objectField.fooFloat', value: 3.3), + new Not(field: 'listField', value: null) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), + self::document('key1', listField: [1, 2]), + self::document('key2', listField: [1, 3]) ]) ]; + } - yield 'Test object field equals filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test object field equals any filter and date values' => [ + final public static function listFloatCases(): \Generator + { + yield 'list field, equals filter, float values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1.1, 2.2]), + self::document(key: 'key2', listField: [1.1, 3.3]), + self::document(key: 'key3', listField: [1.1, 4.4]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'objectField.fooDate', value: ['2021-01-02', '2021-01-03']) + new Equals(field: 'listField', value: 3.3) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key2', listField: [1.1, 3.3]), ]) ]; - yield 'Test object field not filter and date values' => [ + yield 'list field, equals any filter, float values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1.1, 2.2]), + self::document(key: 'key2', listField: [1.1, 3.3]), + self::document(key: 'key3', listField: [1.1, 4.4]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'objectField.fooDate', value: '2021-01-02') + new Any(field: 'listField', value: [3.3, 4.4]) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key2', listField: [1.1, 3.3]), + self::document(key: 'key3', listField: [1.1, 4.4]), ]) ]; - yield 'Test object field not any filter and date values' => [ + yield 'list field, not filter, float values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1.1, 2.2]), + self::document(key: 'key2', listField: [1.1, 3.3]), + self::document(key: 'key3', listField: [1.1, 4.4]), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'objectField.fooDate', value: ['2021-01-02', '2021-01-03']) + new Not(field: 'listField', value: 3.3) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), + self::document(key: 'key1', listField: [1.1, 2.2]), + self::document(key: 'key3', listField: [1.1, 4.4]), ]) ]; - yield 'Test object field gte filter and date values' => [ + yield 'list field, not any filter, float values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1.1, 2.2]), + self::document(key: 'key2', listField: [1.1, 3.3]), + self::document(key: 'key3', listField: [1.1, 4.4]), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectField.fooDate', value: '2021-01-02') + new Neither(field: 'listField', value: [3.3, 4.4]) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1.1, 2.2]), ]) ]; - yield 'Test object field lte filter and date values' => [ + } + + final public static function listIntCases(): \Generator + { + yield 'list field, equals filter, int values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: [1, 4]), ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'objectField.fooDate', value: '2021-01-02') + new Equals(field: 'listField', value: 3) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), + self::document(key: 'key2', listField: [1, 3]), ]) ]; - yield 'Test object field gt filter and date values' => [ + yield 'list field, equals any filter, int values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: [1, 4]), ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'objectField.fooDate', value: '2021-01-02') + new Any(field: 'listField', value: [3, 4]) ] ), 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: [1, 4]), ]) ]; - yield 'Test object field lt filter and date values' => [ + yield 'list field, not filter, int values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: [1, 4]), ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'objectField.fooDate', value: '2021-01-02') + new Not(field: 'listField', value: 3) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key3', listField: [1, 4]), ]) ]; - yield 'Test object field gte and lte filter and date values' => [ + yield 'list field, not any filter, int values' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - self::document(key: 'key4', objectField: ['fooDate' => '2021-01-04 00:00:00.000']), + self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key3', listField: [1, 4]), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectField.fooDate', value: '2021-01-02'), - new Lte(field: 'objectField.fooDate', value: '2021-01-03'), + new Neither(field: 'listField', value: [3, 4]) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), + self::document(key: 'key1', listField: [1, 2]), ]) ]; + } - yield 'Test list field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'listField', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: ['foo', 'baz']), - ]) - ]; - yield 'Test list field equals any filter' => [ + final public static function listDateCases(): \Generator + { + yield 'list field, equals filter, date values' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'listField', value: ['baz', 'qux']) + new Equals(field: 'listField', value: '2021-01-03') ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), ]) ]; - yield 'Test list field not filter' => [ + yield 'list field, equals any filter, date values' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'listField', value: 'baz') + new Any(field: 'listField', value: ['2021-01-03', '2021-01-04']) ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key3', listField: ['foo', 'qux']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]) ]; - yield 'Test list field not any filter' => [ + yield 'list field, not filter, date values' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'listField', value: ['baz', 'qux']) + new Not(field: 'listField', value: '2021-01-03') ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: ['foo', 'bar']), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]) ]; - yield 'Test list field contains filter' => [ + yield 'list field, not any filter, date values' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]), 'criteria' => new Criteria( filters: [ - new Contains(field: 'listField', value: 'ba') + new Neither(field: 'listField', value: ['2021-01-03', '2021-01-04']) ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), ]) ]; - yield 'Test list field null value equals filter' => [ + yield 'list field, contains filter, date values' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: null), + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'listField', value: null) + new Contains(field: 'listField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key3', listField: null) + self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), ]) ]; - yield 'Test list field null value not filter' => [ + } + + final public static function nestedObjectCases(): \Generator + { + yield 'nested object' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: null), + self::document(key: 'key1', objectField: ['fooObj' => ['bar' => 'baz']]), + self::document(key: 'key2', objectField: ['fooObj' => ['bar' => 'qux']]), + self::document(key: 'key3', objectField: ['fooObj' => ['bar' => 'quux']]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'listField', value: null) + new Equals(field: 'objectField.fooObj.bar', value: 'qux') ] ), 'expected' => new Result([ - self::document('key1', listField: [1, 2]), - self::document('key2', listField: [1, 3]) + self::document(key: 'key2', objectField: ['fooObj' => ['bar' => 'qux']]), ]) ]; + } - yield 'Test list field equals filter and int values' => [ + final public static function objectStringCases(): \Generator + { + yield 'object string field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'listField', value: 3) + new Equals(field: 'objectField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: [1, 3]), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), ]) ]; - yield 'Test list field equals any filter and int values' => [ + yield 'object string field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'listField', value: [3, 4]) + new Any(field: 'objectField.stringField', value: ['baz', 'qux']) ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]) ]; - yield 'Test list field not filter and int values' => [ + yield 'object string field, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'listField', value: 3) + new Not(field: 'objectField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key3', listField: [1, 4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]) ]; - yield 'Test list field not any filter and int values' => [ + yield 'object string field, not any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'listField', value: [3, 4]) + new Neither(field: 'objectField.stringField', value: ['baz', 'qux']) ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: [1, 2]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), ]) ]; - yield 'Test list field equals filter and float values' => [ + yield 'object string field, contains filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'listField', value: 3.3) + new Contains(field: 'objectField.stringField', value: 'ba') ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: [1.1, 3.3]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), ]) ]; - yield 'Test list field equals any filter and float values' => [ + yield 'object string field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'listField', value: [3.3, 4.4]) + new Gte(field: 'objectField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]) ]; - yield 'Test list field not filter and float values' => [ + yield 'object string field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'listField', value: 3.3) + new Lte(field: 'objectField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key3', listField: [1.1, 4.4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), ]) ]; - yield 'Test list field not any filter and float values' => [ + yield 'object string field, gt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'listField', value: [3.3, 4.4]) + new Gt(field: 'objectField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: [1.1, 2.2]), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]) ]; - yield 'Test list field equals filter and date values' => [ + yield 'object string field, lt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), + self::document(key: 'key2', objectField: ['stringField' => 'baz']), + self::document(key: 'key3', objectField: ['stringField' => 'qux']), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'listField', value: '2021-01-03') + new Lt(field: 'objectField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), + self::document(key: 'key1', objectField: ['stringField' => 'bar']), ]) ]; - yield 'Test list field equals any filter and date values' => [ + + // yield 'object field null value equals filter' => [ + // 'input' => new Documents([ + // self::document(key: 'key1', objectField: ['stringField' => 'bar']), + // self::document(key: 'key2', objectField: ['stringField' => 'baz']), + // self::document(key: 'key3', objectField: null), + // self::document(key: 'key4', objectField: ['stringField' => null]), + // ]), + // 'criteria' => new FilterCriteria( + // filters: [ + // new Equals(field: 'objectField.stringField', value: null) + // ] + // ), + // 'expected' => new FilterResult([ + // self::document(key: 'key4', objectField: ['stringField' => null]), + // ]) + // ]; + // yield 'object field nested null value equals filter' => [ + // 'input' => new Documents([ + // self::document(key: 'key1', objectField: ['stringField' => 'bar']), + // self::document(key: 'key2', objectField: ['stringField' => 'baz']), + // self::document(key: 'key3', objectField: null), + // self::document(key: 'key4', objectField: ['stringField' => null]), + // ]), + // 'criteria' => new FilterCriteria( + // filters: [ + // new Equals(field: 'objectField', value: null) + // ] + // ), + // 'expected' => new FilterResult([ + // self::document(key: 'key3', objectField: null), + // ]) + // ]; + // yield 'object field null value not filter' => [ + // 'input' => new Documents([ + // self::document(key: 'key1', objectField: ['stringField' => 'bar']), + // self::document(key: 'key2', objectField: ['stringField' => 'baz']), + // self::document(key: 'key3', objectField: null), + // ]), + // 'criteria' => new Criteria( + // filters: [ + // new Not(field: 'objectField.stringField', value: null) + // ] + // ), + // 'expected' => new Result([ + // self::document('key1', objectField: ['stringField' => 'bar']), + // self::document('key2', objectField: ['stringField' => 'baz']) + // ]) + // ]; + } + + final public static function objectFloatCases(): \Generator + { + yield 'object float field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'listField', value: ['2021-01-03', '2021-01-04']) + new Equals(field: 'objectField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), ]) ]; - yield 'Test list field not filter and date values' => [ + yield 'object float field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'listField', value: '2021-01-03') + new Any(field: 'objectField.floatField', value: [1.1, 2.2]) ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), ]) ]; - yield 'Test list field not any filter and date values' => [ + yield 'object float field, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'listField', value: ['2021-01-03', '2021-01-04']) + new Not(field: 'objectField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]) ]; - yield 'Test list field contains filter and date values' => [ + yield 'object float field, not any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Contains(field: 'listField', value: '2021-01-02') + new Neither(field: 'objectField.floatField', value: [1.1, 2.2]) ] ), 'expected' => new Result([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]) ]; - - yield 'Test list object field equals filter and string value' => [ + yield 'object float field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'objectListField.foo', value: 'baz-2') + new Gte(field: 'objectField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]) ]; - yield 'Test list object field equals any filter and string value' => [ + yield 'object float field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'objectListField.foo', value: ['bar-2', 'qux-2']) + new Lte(field: 'objectField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), ]) ]; - yield 'Test list object field contains filter and string value' => [ + yield 'object float field, gt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Contains(field: 'objectListField.foo', value: 'baz') + new Gt(field: 'objectField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]) ]; - yield 'Test list object field starts-with filter and string value' => [ + yield 'object float field, lt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]), 'criteria' => new Criteria( filters: [ - new Prefix(field: 'objectListField.foo', value: 'qu') + new Lt(field: 'objectField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), ]) ]; - yield 'Test list object field ends-with filter and string value' => [ + yield 'object float field, gte and lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key1', objectField: ['floatField' => 1.1]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), + self::document(key: 'key4', objectField: ['floatField' => 4.4]), ]), 'criteria' => new Criteria( filters: [ - new Suffix(field: 'objectListField.foo', value: 'z-2') + new Gte(field: 'objectField.floatField', value: 2.2), + new Lte(field: 'objectField.floatField', value: 3.3), ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), + self::document(key: 'key2', objectField: ['floatField' => 2.2]), + self::document(key: 'key3', objectField: ['floatField' => 3.3]), ]) ]; + } - yield 'Test list object field equals filter and int value' => [ + final public static function objectIntCases(): \Generator + { + yield 'object int field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'objectListField.fooInt', value: 2) + new Equals(field: 'objectField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), + self::document(key: 'key2', objectField: ['intField' => 2]), ]) ]; - yield 'Test list object field equals any filter and int value' => [ + yield 'object int field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'objectListField.fooInt', value: [10, 22]) + new Any(field: 'objectField.intField', value: [1, 2]) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), ]) ]; - yield 'Test list object field gte filter and int value' => [ + yield 'object int field, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectListField.fooInt', value: 22) + new Not(field: 'objectField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]) ]; - yield 'Test list object field lte filter and int value' => [ + yield 'object int field, not any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'objectListField.fooInt', value: 2) + new Neither(field: 'objectField.intField', value: [1, 2]) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]) ]; - yield 'Test list object field gt filter and int value' => [ + yield 'object int field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'objectListField.fooInt', value: 2) + new Gte(field: 'objectField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]) ]; - yield 'Test list object field lt filter and int value' => [ + yield 'object int field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'objectListField.fooInt', value: 20) + new Lte(field: 'objectField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), ]) ]; - - yield 'Test list object field equals filter and float value' => [ + yield 'object int field, gt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'objectListField.fooFloat', value: 2.2) + new Gt(field: 'objectField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]) ]; - yield 'Test list object field equals any filter and float value' => [ + yield 'object int field, lt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]), - 'criteria' => new Criteria( filters: [ - new Any(field: 'objectListField.fooFloat', value: [10.1, 22.2]) + new Lt(field: 'objectField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['intField' => 1]), ]) ]; - yield 'Test list object field gte filter and float value' => [ + yield 'object int field, gte and lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['intField' => 1]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), + self::document(key: 'key4', objectField: ['intField' => 4]), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectListField.fooFloat', value: 22.2) + new Gte(field: 'objectField.intField', value: 2), + new Lte(field: 'objectField.intField', value: 3), ] ), 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key2', objectField: ['intField' => 2]), + self::document(key: 'key3', objectField: ['intField' => 3]), ]) ]; - yield 'Test list object field lte filter and float value' => [ + } + + final public static function objectBoolCases(): \Generator + { + yield 'object bool field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['boolField' => true]), + self::document(key: 'key2', objectField: ['boolField' => false]), + self::document(key: 'key3', objectField: ['boolField' => true]), ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'objectListField.fooFloat', value: 2.2) + new Equals(field: 'objectField.boolField', value: true) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), + self::document(key: 'key1', objectField: ['boolField' => true]), + self::document(key: 'key3', objectField: ['boolField' => true]), ]) ]; - yield 'Test list object field gt filter and float value' => [ + } + + final public static function objectDateCases(): \Generator + { + yield 'object date field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'objectListField.fooFloat', value: 2.2) + new Equals(field: 'objectField.dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), ]) ]; - yield 'Test list object field lt filter and float value' => [ + yield 'object date field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'objectListField.fooFloat', value: 20.1) + new Any(field: 'objectField.dateField', value: ['2021-01-02', '2021-01-03']) ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]) ]; - - yield 'Test list object field equals filter and date value' => [ + yield 'object date field, not filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'objectListField.fooDate', value: '2021-01-02 00:00:00.000') + new Not(field: 'objectField.dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]) ]; - yield 'Test list object field equals any filter and date value' => [ + yield 'object date field, not any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'objectListField.fooDate', value: ['2021-01-10 00:00:00.000', '2021-01-22 00:00:00.000']) + new Neither(field: 'objectField.dateField', value: ['2021-01-02', '2021-01-03']) ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), ]) ]; - - yield 'Test list object field gte filter and date value' => [ + yield 'object date field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'objectListField.fooDate', value: '2021-01-22 00:00:00.000') + new Gte(field: 'objectField.dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]) ]; - yield 'Test list object field lte filter and date value' => [ + yield 'object date field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'objectListField.fooDate', value: '2021-01-02 00:00:00.000') + new Lte(field: 'objectField.dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), ]) ]; - yield 'Test list object field gt filter and date value' => [ + yield 'object date field, gt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'objectListField.fooDate', value: '2021-01-02 00:00:00.000') + new Gt(field: 'objectField.dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]) ]; - yield 'Test list object field lt filter and date value' => [ + yield 'object date field, lt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'objectListField.fooDate', value: '2021-01-20 00:00:00.000') + new Lt(field: 'objectField.dateField', value: '2021-01-02') ] ), 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), ]) ]; - - yield 'Test nested object' => [ + yield 'object date field, gte and lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooObj' => ['bar' => 'baz']]), - self::document(key: 'key2', objectField: ['fooObj' => ['bar' => 'qux']]), - self::document(key: 'key3', objectField: ['fooObj' => ['bar' => 'quux']]), + self::document(key: 'key1', objectField: ['dateField' => '2021-01-01 00:00:00.000']), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), + self::document(key: 'key4', objectField: ['dateField' => '2021-01-04 00:00:00.000']), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'objectField.fooObj.bar', value: 'qux') + new Gte(field: 'objectField.dateField', value: '2021-01-02'), + new Lte(field: 'objectField.dateField', value: '2021-01-03'), ] ), 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooObj' => ['bar' => 'qux']]), + self::document(key: 'key2', objectField: ['dateField' => '2021-01-02 00:00:00.000']), + self::document(key: 'key3', objectField: ['dateField' => '2021-01-03 00:00:00.000']), ]) ]; + } - yield 'Test translated string field equals filter' => [ + final public static function translatedStringCases(): \Generator + { + yield 'translated string field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2521,7 +2145,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedString', value: 'foo') + new Equals(field: 'translatedString', value: 'foo') ] ), 'expected' => new Result([ @@ -2530,7 +2154,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedString: ['de' => 'foo']), ]) ]; - yield 'Test translated string field equals-any filter' => [ + yield 'translated string field, equals-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2539,7 +2163,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedString', value: ['foo', 'bar']) + new Any(field: 'translatedString', value: ['foo', 'bar']) ] ), 'expected' => new Result([ @@ -2548,7 +2172,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), ]) ]; - yield 'Test translated string field not filter' => [ + yield 'translated string field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2557,7 +2181,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedString', value: 'foo') + new Not(field: 'translatedString', value: 'foo') ] ), 'expected' => new Result([ @@ -2565,7 +2189,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), ]) ]; - yield 'Test translated string field not any filter' => [ + yield 'translated string field, not any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2574,14 +2198,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedString', value: ['foo', 'bar']) + new Neither(field: 'translatedString', value: ['foo', 'bar']) ] ), 'expected' => new Result([ self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), ]) ]; - yield 'Test translated string field contains filter' => [ + yield 'translated string field, contains filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'boo']), @@ -2590,7 +2214,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Contains(field: 'translatedString', value: 'oo') + new Contains(field: 'translatedString', value: 'oo') ] ), 'expected' => new Result([ @@ -2599,7 +2223,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedString: ['en' => 'foo', 'de' => 'bar']), ]) ]; - yield 'Test translated string field starts-with filter' => [ + yield 'translated string field, starts-with filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2608,7 +2232,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Prefix(field: 'translatedString', value: 'foo') + new Prefix(field: 'translatedString', value: 'foo') ] ), 'expected' => new Result([ @@ -2616,7 +2240,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), ]) ]; - yield 'Test translated string field ends-with filter' => [ + yield 'translated string field, ends-with filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2625,7 +2249,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Suffix(field: 'translatedString', value: 'o') + new Suffix(field: 'translatedString', value: 'o') ] ), 'expected' => new Result([ @@ -2633,7 +2257,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), ]) ]; - yield 'Test translated string field gte filter' => [ + yield 'translated string field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), self::document(key: 'key2', translatedString: ['en' => 'c']), @@ -2642,7 +2266,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'translatedString', value: 'b') + new Gte(field: 'translatedString', value: 'b') ] ), 'expected' => new Result([ @@ -2651,7 +2275,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), ]) ]; - yield 'Test translated string field gt filter' => [ + yield 'translated string field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), self::document(key: 'key2', translatedString: ['en' => 'c']), @@ -2660,14 +2284,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'translatedString', value: 'b') + new Gt(field: 'translatedString', value: 'b') ] ), 'expected' => new Result([ self::document(key: 'key2', translatedString: ['en' => 'c']), ]) ]; - yield 'Test translated string field lte filter' => [ + yield 'translated string field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), self::document(key: 'key2', translatedString: ['en' => 'c']), @@ -2676,7 +2300,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'translatedString', value: 'b') + new Lte(field: 'translatedString', value: 'b') ] ), 'expected' => new Result([ @@ -2685,7 +2309,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), ]) ]; - yield 'Test translated string field lt filter' => [ + yield 'translated string field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), self::document(key: 'key2', translatedString: ['en' => 'c']), @@ -2694,14 +2318,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'translatedString', value: 'b') + new Lt(field: 'translatedString', value: 'b') ] ), 'expected' => new Result([ self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), ]) ]; - yield 'Test translated string field equals filter and empty string' => [ + yield 'translated string field, equals filter, empty string' => [ 'input' => new Documents([ self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), self::document(key: 'key2', translatedString: ['en' => 'foo']), @@ -2710,7 +2334,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedString', value: 'foo') + new Equals(field: 'translatedString', value: 'foo') ] ), 'expected' => new Result([ @@ -2718,8 +2342,11 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedString: ['de' => 'foo']), ]) ]; + } - yield 'Test translated int field equals filter' => [ + final public static function translatedIntCases(): \Generator + { + yield 'translated int field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 2]), @@ -2728,7 +2355,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedInt', value: 2) + new Equals(field: 'translatedInt', value: 2) ] ), 'expected' => new Result([ @@ -2737,7 +2364,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedInt: ['de' => 2]), ]) ]; - yield 'Test translated int field equals-any filter' => [ + yield 'translated int field, equals-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 2]), @@ -2746,7 +2373,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedInt', value: [2, 3, 4]) + new Any(field: 'translatedInt', value: [2, 3, 4]) ] ), 'expected' => new Result([ @@ -2755,7 +2382,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedInt: ['de' => 4]), ]) ]; - yield 'Test translated int field not filter' => [ + yield 'translated int field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 2]), @@ -2764,14 +2391,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedInt', value: 2) + new Not(field: 'translatedInt', value: 2) ] ), 'expected' => new Result([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), ]) ]; - yield 'Test translated int field not-any filter' => [ + yield 'translated int field, not-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 2]), @@ -2780,12 +2407,12 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedInt', value: [1, 2]) + new Neither(field: 'translatedInt', value: [1, 2]) ] ), 'expected' => new Result([]) ]; - yield 'Test translated int field gte filter' => [ + yield 'translated int field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 3]), @@ -2794,7 +2421,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'translatedInt', value: 2) + new Gte(field: 'translatedInt', value: 2) ] ), 'expected' => new Result([ @@ -2802,7 +2429,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), ]) ]; - yield 'Test translated int field gt filter' => [ + yield 'translated int field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 3]), @@ -2811,14 +2438,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'translatedInt', value: 2) + new Gt(field: 'translatedInt', value: 2) ] ), 'expected' => new Result([ self::document(key: 'key2', translatedInt: ['en' => 3]), ]) ]; - yield 'Test translated int field lte filter' => [ + yield 'translated int field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 3]), @@ -2827,7 +2454,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'translatedInt', value: 2) + new Lte(field: 'translatedInt', value: 2) ] ), 'expected' => new Result([ @@ -2836,7 +2463,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedInt: ['de' => 1]), ]) ]; - yield 'Test translated int field lt filter' => [ + yield 'translated int field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), self::document(key: 'key2', translatedInt: ['en' => 3]), @@ -2845,7 +2472,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'translatedInt', value: 2) + new Lt(field: 'translatedInt', value: 2) ] ), 'expected' => new Result([ @@ -2853,8 +2480,11 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedInt: ['de' => 1]), ]) ]; + } - yield 'Test translated float field equals filter' => [ + final public static function translatedFloatCases(): \Generator + { + yield 'translated float field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 2.2]), @@ -2863,7 +2493,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedFloat', value: 2.2) + new Equals(field: 'translatedFloat', value: 2.2) ] ), 'expected' => new Result([ @@ -2872,7 +2502,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedFloat: ['de' => 2.2]), ]) ]; - yield 'Test translated float field equals-any filter' => [ + yield 'translated float field, equals-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 2.2]), @@ -2881,7 +2511,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedFloat', value: [2.2, 3.3, 4.4]) + new Any(field: 'translatedFloat', value: [2.2, 3.3, 4.4]) ] ), 'expected' => new Result([ @@ -2890,7 +2520,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedFloat: ['de' => 4.4]), ]) ]; - yield 'Test translated float field not filter' => [ + yield 'translated float field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 2.2]), @@ -2899,14 +2529,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedFloat', value: 2.2) + new Not(field: 'translatedFloat', value: 2.2) ] ), 'expected' => new Result([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), ]) ]; - yield 'Test translated float field not-any filter' => [ + yield 'translated float field, not-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 2.2]), @@ -2915,12 +2545,12 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedFloat', value: [1.1, 2.2]) + new Neither(field: 'translatedFloat', value: [1.1, 2.2]) ] ), 'expected' => new Result([]) ]; - yield 'Test translated float field gte filter' => [ + yield 'translated float field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 3.3]), @@ -2929,7 +2559,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'translatedFloat', value: 2.2) + new Gte(field: 'translatedFloat', value: 2.2) ] ), 'expected' => new Result([ @@ -2937,7 +2567,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), ]) ]; - yield 'Test translated float field gt filter' => [ + yield 'translated float field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 3.3]), @@ -2946,14 +2576,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'translatedFloat', value: 2.2) + new Gt(field: 'translatedFloat', value: 2.2) ] ), 'expected' => new Result([ self::document(key: 'key2', translatedFloat: ['en' => 3.3]), ]) ]; - yield 'Test translated float field lte filter' => [ + yield 'translated float field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 3.3]), @@ -2962,7 +2592,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'translatedFloat', value: 2.2) + new Lte(field: 'translatedFloat', value: 2.2) ] ), 'expected' => new Result([ @@ -2971,7 +2601,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedFloat: ['de' => 1.1]), ]) ]; - yield 'Test translated float field lt filter' => [ + yield 'translated float field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), self::document(key: 'key2', translatedFloat: ['en' => 3.3]), @@ -2980,7 +2610,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'translatedFloat', value: 2.2) + new Lt(field: 'translatedFloat', value: 2.2) ] ), 'expected' => new Result([ @@ -2988,8 +2618,11 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedFloat: ['de' => 1.1]), ]) ]; + } - yield 'Test translated bool field equals filter' => [ + final public static function translatedBoolCases(): \Generator + { + yield 'translated bool field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), self::document(key: 'key2', translatedBool: ['en' => false]), @@ -2998,7 +2631,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedBool', value: false) + new Equals(field: 'translatedBool', value: false) ] ), 'expected' => new Result([ @@ -3007,7 +2640,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedBool: ['de' => false]), ]) ]; - yield 'Test translated bool field not filter' => [ + yield 'translated bool field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), self::document(key: 'key2', translatedBool: ['en' => false]), @@ -3016,15 +2649,18 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedBool', value: false) + new Not(field: 'translatedBool', value: false) ] ), 'expected' => new Result([ self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), ]) ]; + } - yield 'Test translated date field equals filter' => [ + final public static function translatedDateCases(): \Generator + { + yield 'translated date field, equals filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), @@ -3033,7 +2669,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedDate', value: '2021-01-02 00:00:00.000') + new Equals(field: 'translatedDate', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ @@ -3042,7 +2678,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), ]) ]; - yield 'Test translated date field equals-any filter' => [ + yield 'translated date field, equals-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), @@ -3051,7 +2687,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedDate', value: ['2021-01-02 00:00:00.000', '2021-01-03 00:00:00.000']) + new Any(field: 'translatedDate', value: ['2021-01-02 00:00:00.000', '2021-01-03 00:00:00.000']) ] ), 'expected' => new Result([ @@ -3060,7 +2696,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), ]) ]; - yield 'Test translated date field not filter' => [ + yield 'translated date field, not filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), @@ -3069,14 +2705,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedDate', value: '2021-01-02 00:00:00.000') + new Not(field: 'translatedDate', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), ]) ]; - yield 'Test translated date field not-any filter' => [ + yield 'translated date field, not-any filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), @@ -3085,12 +2721,12 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedDate', value: ['2021-01-01 00:00:00.000', '2021-01-02 00:00:00.000']) + new Neither(field: 'translatedDate', value: ['2021-01-01 00:00:00.000', '2021-01-02 00:00:00.000']) ] ), 'expected' => new Result([]) ]; - yield 'Test translated date field gte filter' => [ + yield 'translated date field, gte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), @@ -3099,7 +2735,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gte(field: 'translatedDate', value: '2021-01-02 00:00:00.000') + new Gte(field: 'translatedDate', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ @@ -3107,7 +2743,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), ]) ]; - yield 'Test translated date field gt filter' => [ + yield 'translated date field, gt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), @@ -3116,14 +2752,14 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Gt(field: 'translatedDate', value: '2021-01-02 00:00:00.000') + new Gt(field: 'translatedDate', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), ]) ]; - yield 'Test translated date field lte filter' => [ + yield 'translated date field, lte filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), @@ -3132,7 +2768,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lte(field: 'translatedDate', value: '2021-01-02 00:00:00.000') + new Lte(field: 'translatedDate', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ @@ -3141,7 +2777,7 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), ]) ]; - yield 'Test translated date field lt filter' => [ + yield 'translated date field, lt filter' => [ 'input' => new Documents([ self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), @@ -3150,7 +2786,7 @@ final public static function storageProvider(): \Generator ]), 'criteria' => new Criteria( filters: [ - new Lt(field: 'translatedDate', value: '2021-01-02 00:00:00.000') + new Lt(field: 'translatedDate', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ @@ -3158,340 +2794,444 @@ final public static function storageProvider(): \Generator self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), ]) ]; + } - yield 'Test translated list field equals filter and string values' => [ + final public static function objectListStringCases(): \Generator + { + yield 'list object string field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key1', objectListField: [['stringField' => 'bar'], ['stringField' => 'bar-2']]), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedString', value: 'bar') + new Equals(field: 'objectListField.stringField', value: 'baz-2') ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]) ]; - yield 'Test translated list field equals-any filter and string values' => [ + yield 'list object string field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'baz']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key1', objectListField: [['stringField' => 'bar'], ['stringField' => 'bar-2']]), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedString', value: ['bar', 'baz']) + new Any(field: 'objectListField.stringField', value: ['bar-2', 'qux-2']) ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'baz']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key1', objectListField: [['stringField' => 'bar'], ['stringField' => 'bar-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]) ]; - yield 'Test translated list field not filter and string values' => [ + yield 'list object string field, contains filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key1', objectListField: [['stringField' => 'bar'], ['stringField' => 'bar-2']]), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedString', value: 'bar') + new Contains(field: 'objectListField.stringField', value: 'baz') ] ), 'expected' => new Result([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]) ]; - yield 'Test translated list field not-any filter and string values' => [ + yield 'list object string field, starts-with filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key1', objectListField: [['stringField' => 'bar'], ['stringField' => 'bar-2']]), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedString', value: ['foo', 'bar']) + new Prefix(field: 'objectListField.stringField', value: 'qu') ] ), - 'expected' => new Result([]) + 'expected' => new Result([ + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), + ]) ]; - yield 'Test translated list field contains filter and string values' => [ + yield 'list object string field, ends-with filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key1', objectListField: [['stringField' => 'bar'], ['stringField' => 'bar-2']]), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]), 'criteria' => new Criteria( filters: [ - new Contains(field: 'translatedString', value: 'ba') + new Suffix(field: 'objectListField.stringField', value: 'z-2') ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), + self::document(key: 'key2', objectListField: [['stringField' => 'baz'], ['stringField' => 'baz-2']]), + self::document(key: 'key3', objectListField: [['stringField' => 'qux'], ['stringField' => 'qux-2'], ['stringField' => 'baz-2']]), ]) ]; + } - yield 'Test translated list field equals filter and int values' => [ + final public static function objectListFloatCases(): \Generator + { + yield 'list object float field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedInt', value: 2) + new Equals(field: 'objectListField.floatField', value: 2.2) ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), ]) ]; - yield 'Test translated list field equals-any filter and int values' => [ + yield 'list object float field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 3]), - self::document(key: 'key4', translatedInt: ['de' => 2]), + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), ]), + 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedInt', value: [2, 3]) + new Any(field: 'objectListField.floatField', value: [10.1, 22.2]) ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 3]), - self::document(key: 'key4', translatedInt: ['de' => 2]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), ]) ]; - yield 'Test translated list field not filter and int values' => [ + yield 'list object float field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedInt', value: 2) + new Gte(field: 'objectListField.floatField', value: 22.2) ] ), 'expected' => new Result([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), ]) ]; - yield 'Test translated list field not-any filter and int values' => [ + yield 'list object float field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedInt', value: [1, 2]) + new Lte(field: 'objectListField.floatField', value: 2.2) ] ), - 'expected' => new Result([]) + 'expected' => new Result([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + ]) + ]; + yield 'list object float field, gt filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), + ]), + 'criteria' => new Criteria( + filters: [ + new Gt(field: 'objectListField.floatField', value: 2.2) + ] + ), + 'expected' => new Result([ + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), + ]) + ]; + yield 'list object float field, lt filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + self::document(key: 'key3', objectListField: [['floatField' => 20.1], ['floatField' => 22.2], ['floatField' => 24.2]]), + ]), + 'criteria' => new Criteria( + filters: [ + new Lt(field: 'objectListField.floatField', value: 20.1) + ] + ), + 'expected' => new Result([ + self::document(key: 'key1', objectListField: [['floatField' => 1.1], ['floatField' => 2.2]]), + self::document(key: 'key2', objectListField: [['floatField' => 10.1], ['floatField' => 2.2]]), + ]) ]; + } - yield 'Test translated list field equals filter and float values' => [ + final public static function objectListIntCases(): \Generator + { + yield 'list object int field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedFloat', value: 2.2) + new Equals(field: 'objectListField.intField', value: 2) ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), ]) ]; - yield 'Test translated list field equals-any filter and float values' => [ + yield 'list object int field, equals any filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 3.3]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedFloat', value: [2.2, 3.3]) + new Any(field: 'objectListField.intField', value: [10, 22]) ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 3.3]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), ]) ]; - yield 'Test translated list field not filter and float values' => [ + yield 'list object int field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedFloat', value: 2.2) + new Gte(field: 'objectListField.intField', value: 22) ] ), 'expected' => new Result([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), ]) ]; - yield 'Test translated list field not-any filter and float values' => [ + yield 'list object int field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedFloat', value: [1.1, 2.2]) + new Lte(field: 'objectListField.intField', value: 2) ] ), - 'expected' => new Result([]) + 'expected' => new Result([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + ]) + ]; + yield 'list object int field, gt filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), + ]), + 'criteria' => new Criteria( + filters: [ + new Gt(field: 'objectListField.intField', value: 2) + ] + ), + 'expected' => new Result([ + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), + ]) + ]; + yield 'list object int field, lt filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + self::document(key: 'key3', objectListField: [['intField' => 20], ['intField' => 22], ['intField' => 24]]), + ]), + 'criteria' => new Criteria( + filters: [ + new Lt(field: 'objectListField.intField', value: 20) + ] + ), + 'expected' => new Result([ + self::document(key: 'key1', objectListField: [['intField' => 1], ['intField' => 2]]), + self::document(key: 'key2', objectListField: [['intField' => 10], ['intField' => 2]]), + ]) ]; + } - yield 'Test translated list field equals filter and bool values' => [ + final public static function objectListBoolCases(): \Generator + { + yield 'object list bool field, equals filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), + self::document(key: 'key1', objectListField: [['boolField' => true]]), + self::document(key: 'key2', objectListField: [['boolField' => false]]), + self::document(key: 'key3', objectListField: [['boolField' => false], ['boolField' => true]]), ]), 'criteria' => new Criteria( filters: [ - new Equals(field: 'translatedBool', value: false) + new Equals(field: 'objectListField.boolField', value: true) ] ), 'expected' => new Result([ - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), + self::document(key: 'key1', objectListField: [['boolField' => true]]), + self::document(key: 'key3', objectListField: [['boolField' => false], ['boolField' => true]]), ]) ]; - yield 'Test translated list field equals-any filter and bool values' => [ + } + + final public static function objectListDateCases(): \Generator + { + yield 'list object date field, gte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => true]), - self::document(key: 'key4', translatedBool: ['de' => false]), + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), ]), 'criteria' => new Criteria( filters: [ - new Any(field: 'translatedBool', value: [false, true]) + new Gte(field: 'objectListField.dateField', value: '2021-01-22 00:00:00.000') ] ), 'expected' => new Result([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => true]), - self::document(key: 'key4', translatedBool: ['de' => false]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), ]) ]; - yield 'Test translated list field not filter and bool values' => [ + yield 'list object date field, lte filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), ]), 'criteria' => new Criteria( filters: [ - new Not(field: 'translatedBool', value: false) + new Lte(field: 'objectListField.dateField', value: '2021-01-02 00:00:00.000') ] ), 'expected' => new Result([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), ]) ]; - yield 'Test translated list field not-any filter and bool values' => [ + yield 'list object date field, gt filter' => [ 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), ]), 'criteria' => new Criteria( filters: [ - new Neither(field: 'translatedBool', value: [true, false]) + new Gt(field: 'objectListField.dateField', value: '2021-01-02 00:00:00.000') ] ), - 'expected' => new Result([]) + 'expected' => new Result([ + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), + ]) + ]; + yield 'list object date field, lt filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), + ]), + 'criteria' => new Criteria( + filters: [ + new Lt(field: 'objectListField.dateField', value: '2021-01-20 00:00:00.000') + ] + ), + 'expected' => new Result([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + ]) + ]; + yield 'list object date field, equals filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), + ]), + 'criteria' => new Criteria( + filters: [ + new Equals(field: 'objectListField.dateField', value: '2021-01-02 00:00:00.000') + ] + ), + 'expected' => new Result([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + ]) + ]; + yield 'list object date field, equals any filter' => [ + 'input' => new Documents([ + self::document(key: 'key1', objectListField: [['dateField' => '2021-01-01 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), + ]), + 'criteria' => new Criteria( + filters: [ + new Any(field: 'objectListField.dateField', value: ['2021-01-10 00:00:00.000', '2021-01-22 00:00:00.000']) + ] + ), + 'expected' => new Result([ + self::document(key: 'key2', objectListField: [['dateField' => '2021-01-10 00:00:00.000'], ['dateField' => '2021-01-02 00:00:00.000']]), + self::document(key: 'key3', objectListField: [['dateField' => '2021-01-20 00:00:00.000'], ['dateField' => '2021-01-22 00:00:00.000'], ['dateField' => '2021-01-24 00:00:00.000']]), + ]) ]; } - /** - * @param array|null $objectField - * @param array|null $listField - * @param array>|null $objectListField - * @param array|null $translatedString - * @param array|null $translatedInt - * @param array|null $translatedFloat - * @param array|null $translatedBool - * @param array|null $translatedDate - */ - protected static function document( - string $key, - ?string $stringField = null, - ?bool $boolField = null, - ?string $textField = null, - ?string $dateField = null, - ?int $intField = null, - ?float $floatField = null, - ?array $objectField = null, - ?array $listField = null, - ?array $objectListField = null, - ?array $translatedString = null, - ?array $translatedInt = null, - ?array $translatedFloat = null, - ?array $translatedBool = null, - ?array $translatedDate = null, - ): Document { - return new Document( - key: $key, - data: [ - 'stringField' => $stringField, - 'textField' => $textField, - 'dateField' => $dateField, - 'boolField' => $boolField, - 'intField' => $intField, - 'floatField' => $floatField, - 'objectField' => $objectField, - 'listField' => $listField, - 'objectListField' => $objectListField, - 'translatedString' => $translatedString, - 'translatedInt' => $translatedInt, - 'translatedFloat' => $translatedFloat, - 'translatedBool' => $translatedBool, - 'translatedDate' => $translatedDate, - ] - ); + final public static function keysCases(): \Generator + { + yield 'keys and values' => [ + 'input' => new Documents([ + self::document(key: 'key1'), + self::Document(key: 'key2'), + self::Document(key: 'key3'), + ]), + 'criteria' => new Criteria( + primaries: ['key1', 'key2'] + ), + 'expected' => new Result([ + self::document(key: 'key1'), + self::document(key: 'key2'), + ]) + ]; + } + + final public static function paginationCases(): \Generator + { + yield 'pagination' => [ + 'input' => new Documents([ + self::document(key: 'key1'), + self::document(key: 'key2'), + self::document(key: 'key3'), + self::document(key: 'key4'), + self::document(key: 'key5'), + ]), + 'criteria' => new Criteria( + paging: new Page(page: 2, limit: 2) + ), + 'expected' => new Result([ + self::document(key: 'key3'), + self::document(key: 'key4'), + ]) + ]; } } diff --git a/tests/Common/SchemaStorageTrait.php b/tests/Common/SchemaStorageTrait.php new file mode 100644 index 0000000..1debdda --- /dev/null +++ b/tests/Common/SchemaStorageTrait.php @@ -0,0 +1,130 @@ +storageName, + fields: [ + new Field('stringField', FieldType::STRING), + new Field('textField', FieldType::TEXT), + new Field('intField', FieldType::INT), + new Field('floatField', FieldType::FLOAT), + new Field('boolField', FieldType::BOOL), + new Field('dateField', FieldType::DATETIME), + new Field('listField', FieldType::LIST), + + new Field('translatedString', FieldType::STRING, ['translated' => true]), + new Field('translatedText', FieldType::TEXT, ['translated' => true]), + new Field('translatedInt', FieldType::INT, ['translated' => true]), + new Field('translatedFloat', FieldType::FLOAT, ['translated' => true]), + new Field('translatedBool', FieldType::BOOL, ['translated' => true]), + new Field('translatedDate', FieldType::DATETIME, ['translated' => true]), + new Field('translatedList', FieldType::LIST, ['translated' => true]), + + new Field('objectField', FieldType::OBJECT, [], [ + new Field('stringField', FieldType::STRING), + new Field('textField', FieldType::TEXT), + new Field('intField', FieldType::INT), + new Field('floatField', FieldType::FLOAT), + new Field('boolField', FieldType::BOOL), + new Field('dateField', FieldType::DATETIME), + new Field('listField', FieldType::LIST), + + new Field('translatedString', FieldType::STRING, ['translated' => true]), + new Field('translatedText', FieldType::TEXT, ['translated' => true]), + new Field('translatedInt', FieldType::INT, ['translated' => true]), + new Field('translatedFloat', FieldType::FLOAT, ['translated' => true]), + new Field('translatedBool', FieldType::BOOL, ['translated' => true]), + new Field('translatedDate', FieldType::DATETIME, ['translated' => true]), + new Field('translatedList', FieldType::LIST, ['translated' => true]), + + new Field('fooObj', FieldType::OBJECT, [], [ + new Field('bar', FieldType::STRING), + ]), + ]), + + new Field('objectListField', FieldType::OBJECT_LIST, [], [ + new Field('stringField', FieldType::STRING), + new Field('textField', FieldType::TEXT), + new Field('intField', FieldType::INT), + new Field('floatField', FieldType::FLOAT), + new Field('boolField', FieldType::BOOL), + new Field('dateField', FieldType::DATETIME), + new Field('listField', FieldType::LIST), + + new Field('translatedString', FieldType::STRING, ['translated' => true]), + new Field('translatedText', FieldType::TEXT, ['translated' => true]), + new Field('translatedInt', FieldType::INT, ['translated' => true]), + new Field('translatedFloat', FieldType::FLOAT, ['translated' => true]), + new Field('translatedBool', FieldType::BOOL, ['translated' => true]), + new Field('translatedDate', FieldType::DATETIME, ['translated' => true]), + new Field('translatedList', FieldType::LIST, ['translated' => true]), + + new Field('fooObj', FieldType::OBJECT, [], [ + new Field('bar', FieldType::STRING), + new Field('translatedBar', FieldType::STRING, ['translated' => true]), + ]), + ]), + ] + ); + } + + /** + * @param array|null $objectField + * @param array|null $listField + * @param array>|null $objectListField + * @param array|null $translatedString + * @param array|null $translatedInt + * @param array|null $translatedFloat + * @param array|null $translatedBool + * @param array|null $translatedDate + */ + protected static function document( + string $key, + ?string $stringField = null, + ?bool $boolField = null, + ?string $textField = null, + ?string $dateField = null, + ?int $intField = null, + ?float $floatField = null, + ?array $objectField = null, + ?array $listField = null, + ?array $objectListField = null, + ?array $translatedString = null, + ?array $translatedInt = null, + ?array $translatedFloat = null, + ?array $translatedBool = null, + ?array $translatedDate = null, + ): Document { + return new Document( + key: $key, + data: [ + 'stringField' => $stringField, + 'textField' => $textField, + 'dateField' => $dateField, + 'boolField' => $boolField, + 'intField' => $intField, + 'floatField' => $floatField, + 'objectField' => $objectField, + 'listField' => $listField, + 'objectListField' => $objectListField, + 'translatedString' => $translatedString, + 'translatedInt' => $translatedInt, + 'translatedFloat' => $translatedFloat, + 'translatedBool' => $translatedBool, + 'translatedDate' => $translatedDate, + ] + ); + } +} diff --git a/tests/Meilisearch/LiveMeilisearchAware.php b/tests/Meilisearch/MeilisearchLiveStorage.php similarity index 76% rename from tests/Meilisearch/LiveMeilisearchAware.php rename to tests/Meilisearch/MeilisearchLiveStorage.php index e91b5ad..bb8d02c 100644 --- a/tests/Meilisearch/LiveMeilisearchAware.php +++ b/tests/Meilisearch/MeilisearchLiveStorage.php @@ -4,6 +4,7 @@ use Meilisearch\Client; use Meilisearch\Contracts\TasksQuery; +use Shopware\Storage\Common\Aggregation\AggregationAware; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Filter\Criteria; use Shopware\Storage\Common\Filter\Result; @@ -11,13 +12,18 @@ use Shopware\Storage\Common\Storage; use Shopware\Storage\Common\StorageContext; -class LiveMeilisearchAware implements Storage, FilterAware +class MeilisearchLiveStorage implements Storage, FilterAware, AggregationAware { public function __construct( - private readonly FilterAware&Storage $storage, + private readonly FilterAware&AggregationAware&Storage $storage, private readonly Client $client ) {} + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { + return $this->storage->aggregate($aggregations, $criteria, $context); + } + public function filter(Criteria $criteria, StorageContext $context): Result { return $this->storage->filter($criteria, $context); diff --git a/tests/Meilisearch/MeilisearchStorageAggregationTest.php b/tests/Meilisearch/MeilisearchStorageAggregationTest.php new file mode 100644 index 0000000..69cf275 --- /dev/null +++ b/tests/Meilisearch/MeilisearchStorageAggregationTest.php @@ -0,0 +1,110 @@ +getClient()->getIndex($this->getSchema()->source); + } catch (ApiException) { + return false; + } + + return true; + } + + protected function setUp(): void + { + parent::setUp(); + + if ($this->exists()) { + $this->index()->deleteAllDocuments(); + + $this->wait(); + + return; + } + + $this->getClient()->deleteIndex($this->getSchema()->source); + + $this->wait(); + + $this->getClient()->createIndex( + uid: $this->getSchema()->source, + options: ['primaryKey' => 'key'] + ); + + $fields = array_map(fn($field) => $field->name, $this->getSchema()->fields); + + $fields[] = 'key'; + + $fields = array_values(array_filter($fields)); + + $this->index() + ->updateFilterableAttributes($fields); + + $this->index() + ->updateSortableAttributes($fields); + + $this->wait(); + } + + private function getClient(): Client + { + if ($this->client === null) { + $this->client = new Client( + url: 'http://localhost:7700', + apiKey: 'UTbXxcv5T5Hq-nCYAjgPJ5lsBxf7PdhgiNexmoTByJk' + ); + } + + return $this->client; + } + + public function getStorage(): AggregationAware&Storage + { + return new MeilisearchLiveStorage( + storage: new MeilisearchStorage( + caster: new AggregationCaster(), + client: $this->getClient(), + schema: $this->getSchema() + ), + client: $this->getClient(), + ); + } + + private function index(): Indexes + { + return $this->getClient()->index($this->getSchema()->source); + } + + private function wait(): void + { + $tasks = new TasksQuery(); + $tasks->setStatuses(['enqueued', 'processing']); + + $tasks = $this->getClient()->getTasks($tasks); + + $ids = array_map(fn($task) => $task['uid'], $tasks->getResults()); + + if (count($ids) === 0) { + return; + } + + $this->getClient()->waitForTasks($ids); + } +} diff --git a/tests/Meilisearch/MeilisearchStorageTest.php b/tests/Meilisearch/MeilisearchStorageTest.php index d9b7802..4fc237e 100644 --- a/tests/Meilisearch/MeilisearchStorageTest.php +++ b/tests/Meilisearch/MeilisearchStorageTest.php @@ -7,6 +7,7 @@ use Meilisearch\Endpoints\Indexes; use Meilisearch\Exceptions\ApiException; use PHPUnit\Framework\Attributes\DataProvider; +use Shopware\Storage\Common\Aggregation\AggregationCaster; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Exception\NotSupportedByEngine; use Shopware\Storage\Common\Filter\Criteria; @@ -80,2231 +81,6 @@ protected function setUp(): void $this->wait(); } - #[DataProvider('debugProvider')] - public function testDebug( - Documents $input, - Criteria $criteria, - Result $expected - ): void { - $storage = $this->getStorage(); - - $storage->store($input); - - try { - $loaded = $storage->filter($criteria, new StorageContext(languages: ['en', 'de'])); - } catch (NotSupportedByEngine $e) { - static::markTestIncomplete($e->getMessage()); - } - - static::assertEquals($expected, $loaded); - } - - public static function debugProvider(): \Generator - { - yield 'Test object field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['foo' => 'baz']), - ]) - ]; - yield 'Test object field equals any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectField.foo', value: ['baz', 'qux']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]) - ]; - yield 'Test object field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]) - ]; - yield 'Test object field not any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'objectField.foo', value: ['baz', 'qux']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - ]) - ]; - yield 'Test object field contains filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'objectField.foo', value: 'ba') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - ]) - ]; - yield 'Test object field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]) - ]; - yield 'Test object field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - ]) - ]; - yield 'Test object field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]) - ]; - yield 'Test object field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - self::document(key: 'key2', objectField: ['foo' => 'baz']), - self::document(key: 'key3', objectField: ['foo' => 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['foo' => 'bar']), - ]) - ]; - - yield 'Test object field equals filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooInt' => 2]), - ]) - ]; - yield 'Test object field equals any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectField.fooInt', value: [1, 2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - ]) - ]; - yield 'Test object field not filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]) - ]; - yield 'Test object field not any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'objectField.fooInt', value: [1, 2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]) - ]; - yield 'Test object field gte filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]) - ]; - yield 'Test object field lte filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - ]) - ]; - yield 'Test object field gt filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]) - ]; - yield 'Test object field lt filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - ]) - ]; - yield 'Test object field gte and lte filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooInt' => 1]), - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - self::document(key: 'key4', objectField: ['fooInt' => 4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.fooInt', value: 2), - new Lte(field: 'objectField.fooInt', value: 3), - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooInt' => 2]), - self::document(key: 'key3', objectField: ['fooInt' => 3]), - ]) - ]; - - yield 'Test object field equals filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - ]) - ]; - yield 'Test object field equals any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectField.fooFloat', value: [1.1, 2.2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - ]) - ]; - yield 'Test object field not filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]) - ]; - yield 'Test object field not any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'objectField.fooFloat', value: [1.1, 2.2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]) - ]; - yield 'Test object field gte filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]) - ]; - yield 'Test object field lte filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - ]) - ]; - yield 'Test object field gt filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]) - ]; - yield 'Test object field lt filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - ]) - ]; - yield 'Test object field gte and lte filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooFloat' => 1.1]), - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - self::document(key: 'key4', objectField: ['fooFloat' => 4.4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.fooFloat', value: 2.2), - new Lte(field: 'objectField.fooFloat', value: 3.3), - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooFloat' => 2.2]), - self::document(key: 'key3', objectField: ['fooFloat' => 3.3]), - ]) - ]; - - yield 'Test object field equals filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test object field equals any filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectField.fooDate', value: ['2021-01-02', '2021-01-03']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]) - ]; - yield 'Test object field not filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]) - ]; - yield 'Test object field not any filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'objectField.fooDate', value: ['2021-01-02', '2021-01-03']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - ]) - ]; - yield 'Test object field gte filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]) - ]; - yield 'Test object field lte filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test object field gt filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]) - ]; - yield 'Test object field lt filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectField.fooDate', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - ]) - ]; - yield 'Test object field gte and lte filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooDate' => '2021-01-01 00:00:00.000']), - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - self::document(key: 'key4', objectField: ['fooDate' => '2021-01-04 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectField.fooDate', value: '2021-01-02'), - new Lte(field: 'objectField.fooDate', value: '2021-01-03'), - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooDate' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', objectField: ['fooDate' => '2021-01-03 00:00:00.000']), - ]) - ]; - - yield 'Test list field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'listField', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: ['foo', 'baz']), - ]) - ]; - yield 'Test list field equals any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'listField', value: ['baz', 'qux']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]) - ]; - yield 'Test list field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'listField', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]) - ]; - yield 'Test list field not any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'listField', value: ['baz', 'qux']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: ['foo', 'bar']), - ]) - ]; - yield 'Test list field contains filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - self::document(key: 'key3', listField: ['foo', 'qux']), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'listField', value: 'ba') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: ['foo', 'bar']), - self::document(key: 'key2', listField: ['foo', 'baz']), - ]) - ]; - yield 'Test list field null value equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: null), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'listField', value: null) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', listField: null) - ]) - ]; - yield 'Test list field null value not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: null), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'listField', value: null) - ] - ), - 'expected' => new Result([ - self::document('key1', listField: [1, 2]), - self::document('key2', listField: [1, 3]) - ]) - ]; - - yield 'Test list field equals filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'listField', value: 3) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: [1, 3]), - ]) - ]; - yield 'Test list field equals any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'listField', value: [3, 4]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), - ]) - ]; - yield 'Test list field not filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'listField', value: 3) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key3', listField: [1, 4]), - ]) - ]; - yield 'Test list field not any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1, 2]), - self::document(key: 'key2', listField: [1, 3]), - self::document(key: 'key3', listField: [1, 4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'listField', value: [3, 4]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: [1, 2]), - ]) - ]; - yield 'Test list field equals filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'listField', value: 3.3) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: [1.1, 3.3]), - ]) - ]; - yield 'Test list field equals any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'listField', value: [3.3, 4.4]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), - ]) - ]; - yield 'Test list field not filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'listField', value: 3.3) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key3', listField: [1.1, 4.4]), - ]) - ]; - yield 'Test list field not any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: [1.1, 2.2]), - self::document(key: 'key2', listField: [1.1, 3.3]), - self::document(key: 'key3', listField: [1.1, 4.4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'listField', value: [3.3, 4.4]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: [1.1, 2.2]), - ]) - ]; - yield 'Test list field equals filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'listField', value: '2021-01-03') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - ]) - ]; - yield 'Test list field equals any filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'listField', value: ['2021-01-03', '2021-01-04']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]) - ]; - yield 'Test list field not filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'listField', value: '2021-01-03') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]) - ]; - yield 'Test list field not any filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'listField', value: ['2021-01-03', '2021-01-04']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - ]) - ]; - yield 'Test list field contains filter and date values' => [ - 'input' => new Documents([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - self::document(key: 'key2', listField: ['2021-01-01', '2021-01-03']), - self::document(key: 'key3', listField: ['2021-01-01', '2021-01-04']), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'listField', value: '2021-01-02') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', listField: ['2021-01-01', '2021-01-02']), - ]) - ]; - - yield 'Test list object field equals filter and string value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectListField.foo', value: 'baz-2') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]) - ]; - yield 'Test list object field equals any filter and string value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectListField.foo', value: ['bar-2', 'qux-2']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]) - ]; - yield 'Test list object field contains filter and string value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'objectListField.foo', value: 'baz') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]) - ]; - yield 'Test list object field starts-with filter and string value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Prefix(field: 'objectListField.foo', value: 'qu') - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]) - ]; - yield 'Test list object field ends-with filter and string value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['foo' => 'bar'], ['foo' => 'bar-2']]), - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Suffix(field: 'objectListField.foo', value: 'z-2') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['foo' => 'baz'], ['foo' => 'baz-2']]), - self::document(key: 'key3', objectListField: [['foo' => 'qux'], ['foo' => 'qux-2'], ['foo' => 'baz-2']]), - ]) - ]; - - yield 'Test list object field equals filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectListField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - ]) - ]; - yield 'Test list object field equals any filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectListField.fooInt', value: [10, 22]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]) - ]; - yield 'Test list object field gte filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectListField.fooInt', value: 22) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]) - ]; - yield 'Test list object field lte filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectListField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - ]) - ]; - yield 'Test list object field gt filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectListField.fooInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]) - ]; - yield 'Test list object field lt filter and int value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - self::document(key: 'key3', objectListField: [['fooInt' => 20], ['fooInt' => 22], ['fooInt' => 24]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectListField.fooInt', value: 20) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooInt' => 1], ['fooInt' => 2]]), - self::document(key: 'key2', objectListField: [['fooInt' => 10], ['fooInt' => 2]]), - ]) - ]; - - yield 'Test list object field equals filter and float value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectListField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - ]) - ]; - yield 'Test list object field equals any filter and float value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]), - - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectListField.fooFloat', value: [10.1, 22.2]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]) - ]; - yield 'Test list object field gte filter and float value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectListField.fooFloat', value: 22.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]) - ]; - - yield 'Test list object field lte filter and float value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectListField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - ]) - ]; - yield 'Test list object field gt filter and float value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectListField.fooFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]) - ]; - yield 'Test list object field lt filter and float value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - self::document(key: 'key3', objectListField: [['fooFloat' => 20.1], ['fooFloat' => 22.2], ['fooFloat' => 24.2]]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectListField.fooFloat', value: 20.1) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooFloat' => 1.1], ['fooFloat' => 2.2]]), - self::document(key: 'key2', objectListField: [['fooFloat' => 10.1], ['fooFloat' => 2.2]]), - ]) - ]; - - yield 'Test list object field equals filter and date value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectListField.fooDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - ]) - ]; - yield 'Test list object field equals any filter and date value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'objectListField.fooDate', value: ['2021-01-10 00:00:00.000', '2021-01-22 00:00:00.000']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]) - ]; - - yield 'Test list object field gte filter and date value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'objectListField.fooDate', value: '2021-01-22 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]) - ]; - yield 'Test list object field lte filter and date value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'objectListField.fooDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - ]) - ]; - yield 'Test list object field gt filter and date value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'objectListField.fooDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]) - ]; - yield 'Test list object field lt filter and date value' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key3', objectListField: [['fooDate' => '2021-01-20 00:00:00.000'], ['fooDate' => '2021-01-22 00:00:00.000'], ['fooDate' => '2021-01-24 00:00:00.000']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'objectListField.fooDate', value: '2021-01-20 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', objectListField: [['fooDate' => '2021-01-01 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - self::document(key: 'key2', objectListField: [['fooDate' => '2021-01-10 00:00:00.000'], ['fooDate' => '2021-01-02 00:00:00.000']]), - ]) - ]; - - yield 'Test nested object' => [ - 'input' => new Documents([ - self::document(key: 'key1', objectField: ['fooObj' => ['bar' => 'baz']]), - self::document(key: 'key2', objectField: ['fooObj' => ['bar' => 'qux']]), - self::document(key: 'key3', objectField: ['fooObj' => ['bar' => 'quux']]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'objectField.fooObj.bar', value: 'qux') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', objectField: ['fooObj' => ['bar' => 'qux']]), - ]) - ]; - - yield 'Test translated string field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedString', value: 'foo') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['de' => 'foo']), - ]) - ]; - yield 'Test translated string field equals-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedString', value: ['foo', 'bar']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - ]) - ]; - yield 'Test translated string field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedString', value: 'foo') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), - ]) - ]; - yield 'Test translated string field not any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedString', value: ['foo', 'bar']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), - ]) - ]; - yield 'Test translated string field contains filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'boo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'foo', 'de' => 'bar']), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'translatedString', value: 'oo') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'boo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'foo', 'de' => 'bar']), - ]) - ]; - yield 'Test translated string field starts-with filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'baz', 'de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Prefix(field: 'translatedString', value: 'foo') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - ]) - ]; - yield 'Test translated string field ends-with filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['en' => 'ob', 'de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Suffix(field: 'translatedString', value: 'o') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'foo']), - ]) - ]; - yield 'Test translated string field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), - self::document(key: 'key2', translatedString: ['en' => 'c']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'b']), - self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'translatedString', value: 'b') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'c']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'b']), - self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), - ]) - ]; - yield 'Test translated string field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), - self::document(key: 'key2', translatedString: ['en' => 'c']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'b']), - self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'translatedString', value: 'b') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'c']), - ]) - ]; - yield 'Test translated string field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), - self::document(key: 'key2', translatedString: ['en' => 'c']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'b']), - self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'translatedString', value: 'b') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'b']), - self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), - ]) - ]; - yield 'Test translated string field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), - self::document(key: 'key2', translatedString: ['en' => 'c']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'b']), - self::document(key: 'key4', translatedString: ['en' => 'b', 'de' => 'a']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'translatedString', value: 'b') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedString: ['en' => 'a', 'de' => 'b']), - ]) - ]; - yield 'Test translated string field equals filter and empty string' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'bar', 'de' => 'foo']), - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key3', translatedString: ['en' => '', 'de' => 'foo']), - self::document(key: 'key4', translatedString: ['de' => 'foo']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedString', value: 'foo') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'foo']), - self::document(key: 'key4', translatedString: ['de' => 'foo']), - ]) - ]; - - yield 'Test translated int field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]) - ]; - yield 'Test translated int field equals-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 3]), - self::document(key: 'key4', translatedInt: ['de' => 4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedInt', value: [2, 3, 4]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 3]), - self::document(key: 'key4', translatedInt: ['de' => 4]), - ]) - ]; - yield 'Test translated int field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - ]) - ]; - yield 'Test translated int field not-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedInt', value: [1, 2]) - ] - ), - 'expected' => new Result([]) - ]; - yield 'Test translated int field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 3]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 3]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - ]) - ]; - yield 'Test translated int field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 3]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 3]), - ]) - ]; - yield 'Test translated int field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 3]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 1]), - ]) - ]; - yield 'Test translated int field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 3]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 1]), - ]) - ]; - - yield 'Test translated float field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]) - ]; - yield 'Test translated float field equals-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 3.3]), - self::document(key: 'key4', translatedFloat: ['de' => 4.4]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedFloat', value: [2.2, 3.3, 4.4]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 3.3]), - self::document(key: 'key4', translatedFloat: ['de' => 4.4]), - ]) - ]; - yield 'Test translated float field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - ]) - ]; - yield 'Test translated float field not-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedFloat', value: [1.1, 2.2]) - ] - ), - 'expected' => new Result([]) - ]; - yield 'Test translated float field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 3.3]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 1.1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 3.3]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - ]) - ]; - yield 'Test translated float field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 3.3]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 1.1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 3.3]), - ]) - ]; - yield 'Test translated float field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 3.3]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 1.1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 1.1]), - ]) - ]; - yield 'Test translated float field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 3.3]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 1.1]), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 1.1]), - ]) - ]; - - yield 'Test translated bool field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedBool', value: false) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]) - ]; - yield 'Test translated bool field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedBool', value: false) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - ]) - ]; - - yield 'Test translated date field equals filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test translated date field equals-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-03 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedDate', value: ['2021-01-02 00:00:00.000', '2021-01-03 00:00:00.000']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-03 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test translated date field not filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test translated date field not-any filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-02 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedDate', value: ['2021-01-01 00:00:00.000', '2021-01-02 00:00:00.000']) - ] - ), - 'expected' => new Result([]) - ]; - yield 'Test translated date field gte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gte(field: 'translatedDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - ]) - ]; - yield 'Test translated date field gt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Gt(field: 'translatedDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), - ]) - ]; - yield 'Test translated date field lte filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lte(field: 'translatedDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), - ]) - ]; - yield 'Test translated date field lt filter' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key2', translatedDate: ['en' => '2021-01-03 00:00:00.000']), - self::document(key: 'key3', translatedDate: ['en' => null, 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), - ]), - 'criteria' => new Criteria( - filters: [ - new Lt(field: 'translatedDate', value: '2021-01-02 00:00:00.000') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedDate: ['en' => '2021-01-01 00:00:00.000', 'de' => '2021-01-02 00:00:00.000']), - self::document(key: 'key4', translatedDate: ['de' => '2021-01-01 00:00:00.000']), - ]) - ]; - - yield 'Test translated list field equals filter and string values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedString', value: 'bar') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]) - ]; - yield 'Test translated list field equals-any filter and string values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'baz']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedString', value: ['bar', 'baz']) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'baz']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]) - ]; - yield 'Test translated list field not filter and string values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedString', value: 'bar') - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - ]) - ]; - yield 'Test translated list field not-any filter and string values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedString', value: ['foo', 'bar']) - ] - ), - 'expected' => new Result([]) - ]; - yield 'Test translated list field contains filter and string values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedString: ['en' => 'foo', 'de' => 'bar']), - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]), - 'criteria' => new Criteria( - filters: [ - new Contains(field: 'translatedString', value: 'ba') - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedString: ['en' => 'bar']), - self::document(key: 'key3', translatedString: ['en' => null, 'de' => 'bar']), - self::document(key: 'key4', translatedString: ['de' => 'bar']), - ]) - ]; - - yield 'Test translated list field equals filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]) - ]; - yield 'Test translated list field equals-any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 3]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedInt', value: [2, 3]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 3]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]) - ]; - yield 'Test translated list field not filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedInt', value: 2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - ]) - ]; - yield 'Test translated list field not-any filter and int values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedInt: ['en' => 1, 'de' => 2]), - self::document(key: 'key2', translatedInt: ['en' => 2]), - self::document(key: 'key3', translatedInt: ['en' => null, 'de' => 2]), - self::document(key: 'key4', translatedInt: ['de' => 2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedInt', value: [1, 2]) - ] - ), - 'expected' => new Result([]) - ]; - - yield 'Test translated list field equals filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]) - ]; - yield 'Test translated list field equals-any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 3.3]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedFloat', value: [2.2, 3.3]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 3.3]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]) - ]; - yield 'Test translated list field not filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedFloat', value: 2.2) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - ]) - ]; - yield 'Test translated list field not-any filter and float values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedFloat: ['en' => 1.1, 'de' => 2.2]), - self::document(key: 'key2', translatedFloat: ['en' => 2.2]), - self::document(key: 'key3', translatedFloat: ['en' => null, 'de' => 2.2]), - self::document(key: 'key4', translatedFloat: ['de' => 2.2]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedFloat', value: [1.1, 2.2]) - ] - ), - 'expected' => new Result([]) - ]; - - yield 'Test translated list field equals filter and bool values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]), - 'criteria' => new Criteria( - filters: [ - new Equals(field: 'translatedBool', value: false) - ] - ), - 'expected' => new Result([ - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]) - ]; - yield 'Test translated list field equals-any filter and bool values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => true]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]), - 'criteria' => new Criteria( - filters: [ - new Any(field: 'translatedBool', value: [false, true]) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => true]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]) - ]; - yield 'Test translated list field not filter and bool values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]), - 'criteria' => new Criteria( - filters: [ - new Not(field: 'translatedBool', value: false) - ] - ), - 'expected' => new Result([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - ]) - ]; - yield 'Test translated list field not-any filter and bool values' => [ - 'input' => new Documents([ - self::document(key: 'key1', translatedBool: ['en' => true, 'de' => false]), - self::document(key: 'key2', translatedBool: ['en' => false]), - self::document(key: 'key3', translatedBool: ['en' => null, 'de' => false]), - self::document(key: 'key4', translatedBool: ['de' => false]), - ]), - 'criteria' => new Criteria( - filters: [ - new Neither(field: 'translatedBool', value: [true, false]) - ] - ), - 'expected' => new Result([]) - ]; - } - private function getClient(): Client { if ($this->client === null) { @@ -2319,8 +95,9 @@ private function getClient(): Client public function getStorage(): FilterAware&Storage { - return new LiveMeilisearchAware( + return new MeilisearchLiveStorage( storage: new MeilisearchStorage( + caster: new AggregationCaster(), client: $this->getClient(), schema: $this->getSchema() ), diff --git a/tests/MongoDB/MongoDBAggregateStorageTest.php b/tests/MongoDB/MongoDBAggregateStorageTest.php new file mode 100644 index 0000000..38c9aaf --- /dev/null +++ b/tests/MongoDB/MongoDBAggregateStorageTest.php @@ -0,0 +1,51 @@ +client === null) { + $this->client = new Client('mongodb://localhost:27017'); + } + + return $this->client; + } + + protected function setUp(): void + { + parent::setUp(); + $this->getClient()->dropDatabase('test'); + } + + protected function tearDown(): void + { + parent::tearDown(); + $this->getClient()->dropDatabase('test'); + } + + public function getStorage(): AggregationAware&Storage + { + return new MongoDBStorage( + caster: new AggregationCaster(), + database: 'test', + schema: $this->getSchema(), + client: $this->getClient(), + ); + } +} diff --git a/tests/MongoDB/MongoDBFilterStorageTest.php b/tests/MongoDB/MongoDBFilterStorageTest.php index 05570d5..3ab009f 100644 --- a/tests/MongoDB/MongoDBFilterStorageTest.php +++ b/tests/MongoDB/MongoDBFilterStorageTest.php @@ -3,6 +3,7 @@ namespace Shopware\StorageTests\MongoDB; use MongoDB\Client; +use Shopware\Storage\Common\Aggregation\AggregationCaster; use Shopware\Storage\Common\Filter\FilterAware; use Shopware\Storage\Common\Storage; use Shopware\Storage\MongoDB\MongoDBStorage; @@ -39,6 +40,7 @@ protected function tearDown(): void public function getStorage(): FilterAware&Storage { return new MongoDBStorage( + caster: new AggregationCaster(), database: 'test', schema: $this->getSchema(), client: $this->getClient(), diff --git a/tests/MySQL/MySQLAggregationStorageTest.php b/tests/MySQL/MySQLAggregationStorageTest.php new file mode 100644 index 0000000..4793456 --- /dev/null +++ b/tests/MySQL/MySQLAggregationStorageTest.php @@ -0,0 +1,54 @@ +getConnection() + ->executeStatement('DROP TABLE IF EXISTS `test_storage`'); + + $this->getConnection() + ->executeStatement((string) file_get_contents(__DIR__ . '/test_storage.sql')); + } + + public function getStorage(): AggregationAware&Storage + { + return new MySQLStorage( + caster: new AggregationCaster(), + connection: $this->getConnection(), + schema: $this->getSchema() + ); + } + + private static function getConnection(): Connection + { + if (self::$connection) { + return self::$connection; + } + + $params = [ + 'url' => 'mysql://shopware:shopware@127.0.0.1:3306/shopware', + 'charset' => 'utf8mb4', + 'driverOptions' => [ + \PDO::ATTR_STRINGIFY_FETCHES => true, + \PDO::ATTR_TIMEOUT => 5, + ], + ]; + + return self::$connection = DriverManager::getConnection($params); + } +} diff --git a/tests/MySQL/MySQLFilterStorageTest.php b/tests/MySQL/MySQLFilterStorageTest.php index c6c0462..1b8f170 100644 --- a/tests/MySQL/MySQLFilterStorageTest.php +++ b/tests/MySQL/MySQLFilterStorageTest.php @@ -4,13 +4,8 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; -use PHPUnit\Framework\Attributes\DataProvider; -use Shopware\Storage\Common\Document\Documents; -use Shopware\Storage\Common\Filter\Criteria; -use Shopware\Storage\Common\Filter\Result; +use Shopware\Storage\Common\Aggregation\AggregationCaster; use Shopware\Storage\Common\Filter\FilterAware; -use Shopware\Storage\Common\Filter\Type\Equals; -use Shopware\Storage\Common\Filter\Type\Not; use Shopware\Storage\Common\Storage; use Shopware\Storage\MySQL\MySQLStorage; use Shopware\StorageTests\Common\FilterStorageTestBase; @@ -36,6 +31,7 @@ protected function setUp(): void public function getStorage(): FilterAware&Storage { return new MySQLStorage( + caster: new AggregationCaster(), connection: $this->getConnection(), schema: $this->getSchema() ); diff --git a/tests/Opensearch/OpensearchLiveStorage.php b/tests/Opensearch/OpensearchLiveStorage.php index 312fd08..f4015e0 100644 --- a/tests/Opensearch/OpensearchLiveStorage.php +++ b/tests/Opensearch/OpensearchLiveStorage.php @@ -3,6 +3,8 @@ namespace Shopware\StorageTests\Opensearch; use OpenSearch\Client; +use Shopware\Storage\Common\Aggregation\AggregationAware; +use Shopware\Storage\Common\Aggregation\Type\Aggregation; use Shopware\Storage\Common\Document\Documents; use Shopware\Storage\Common\Filter\Criteria; use Shopware\Storage\Common\Filter\Result; @@ -11,23 +13,25 @@ use Shopware\Storage\Common\Storage; use Shopware\Storage\Common\StorageContext; -class OpensearchLiveStorage implements FilterAware, Storage +class OpensearchLiveStorage implements FilterAware, Storage, AggregationAware { public function __construct( private readonly Client $client, - private readonly FilterAware&Storage $decorated, + private readonly FilterAware&AggregationAware&Storage $decorated, private readonly Schema $schema ) {} public function remove(array $keys): void { $this->decorated->remove($keys); + $this->client->indices()->refresh(['index' => $this->schema->source]); } public function store(Documents $documents): void { $this->decorated->store($documents); + $this->client->indices()->refresh(['index' => $this->schema->source]); } @@ -36,6 +40,14 @@ public function filter(Criteria $criteria, StorageContext $context): Result return $this->decorated->filter($criteria, $context); } + /** + * {@inheritDoc} + */ + public function aggregate(array $aggregations, Criteria $criteria, StorageContext $context): array + { + return $this->decorated->aggregate($aggregations, $criteria, $context); + } + public function setup(): void { $this->decorated->setup(); diff --git a/tests/Opensearch/OpensearchStorageAggregationTest.php b/tests/Opensearch/OpensearchStorageAggregationTest.php new file mode 100644 index 0000000..04cb4dd --- /dev/null +++ b/tests/Opensearch/OpensearchStorageAggregationTest.php @@ -0,0 +1,197 @@ +client === null) { + $builder = ClientBuilder::create(); + $builder->setHosts(['http://localhost:9200']); + + $this->client = $builder->build(); + } + + return $this->client; + } + + protected function setUp(): void + { + parent::setUp(); + + $this->createIndex(); + + $this->createScripts(); + } + + private function createIndex(): void + { + $exists = $this->getClient() + ->indices() + ->exists(['index' => $this->getSchema()->source]); + + if ($exists) { + //$this->getClient()->indices()->delete(['index' => $this->getSchema()->source]); + // delete all documents from index + $this->getClient()->deleteByQuery([ + 'index' => $this->getSchema()->source, + 'body' => [ + 'query' => ['match_all' => new \stdClass()] + ] + ]); + return; + } + + $this->getClient()->indices()->create([ + 'index' => $this->getSchema()->source, + 'body' => [ + 'mappings' => [ + 'properties' => [ + 'key' => ['type' => 'keyword'], + 'stringField' => ['type' => 'keyword'], + 'intField' => ['type' => 'integer'], + 'floatField' => ['type' => 'double'], + 'boolField' => ['type' => 'boolean'], + 'dateField' => [ + 'type' => 'date', + 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', + 'ignore_malformed' => true, + ], + 'listField' => ['type' => 'keyword'], + 'translatedString' => [ + 'type' => 'object', + 'properties' => [ + 'de' => ['type' => 'keyword'], + 'en' => ['type' => 'keyword'], + ] + ], + 'translatedInt' => [ + 'type' => 'object', + 'properties' => [ + 'de' => ['type' => 'integer'], + 'en' => ['type' => 'integer'], + ] + ], + 'translatedFloat' => [ + 'type' => 'object', + 'properties' => [ + 'de' => ['type' => 'double'], + 'en' => ['type' => 'double'], + ] + ], + 'translatedBool' => [ + 'type' => 'object', + 'properties' => [ + 'de' => ['type' => 'boolean'], + 'en' => ['type' => 'boolean'], + ] + ], + 'translatedDate' => [ + 'type' => 'object', + 'properties' => [ + 'de' => [ + 'type' => 'date', + 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', + 'ignore_malformed' => true, + ], + 'en' => [ + 'type' => 'date', + 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', + 'ignore_malformed' => true, + ], + ] + ], + 'objectField' => [ + 'type' => 'nested', + 'properties' => [ + 'stringField' => ['type' => 'keyword'], + 'intField' => ['type' => 'integer'], + 'floatField' => ['type' => 'double'], + 'boolField' => ['type' => 'boolean'], + 'dateField' => [ + 'type' => 'date', + 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', + 'ignore_malformed' => true, + ] + ] + ], + 'objectListField' => [ + 'type' => 'nested', + 'properties' => [ + 'stringField' => ['type' => 'keyword'], + 'intField' => ['type' => 'integer'], + 'floatField' => ['type' => 'double'], + 'boolField' => ['type' => 'boolean'], + 'dateField' => [ + 'type' => 'date', + 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', + 'ignore_malformed' => true, + ] + ] + ], + ] + ] + ] + ]); + } + + private function createScripts(): void + { + $this->getClient()->putScript([ + 'id' => 'translated', + 'body' => [ + 'script' => [ + 'lang' => 'painless', + 'source' => file_get_contents(__DIR__ . '/../../src/Opensearch/scripts/translated.groovy') + ] + ] + ]); + + } + + public static function tearDownAfterClass(): void + { + parent::tearDownAfterClass(); + + $builder = ClientBuilder::create(); + $builder->setHosts(['http://localhost:9200']); + $client = $builder->build(); + + $exists = $client->indices() + ->exists(['index' => 'test_storage']); + + if ($exists) { + $client->indices() + ->delete(['index' => 'test_storage']); + } + } + + public function getStorage(): AggregationAware&Storage + { + return new OpensearchLiveStorage( + $this->getClient(), + new OpensearchStorage( + new AggregationCaster(), + $this->getClient(), + $this->getSchema() + ), + $this->getSchema() + ); + } +} diff --git a/tests/Opensearch/OpenSearchStorageTest.php b/tests/Opensearch/OpensearchStorageFilterTest.php similarity index 77% rename from tests/Opensearch/OpenSearchStorageTest.php rename to tests/Opensearch/OpensearchStorageFilterTest.php index 3f25ee6..ff31fd5 100644 --- a/tests/Opensearch/OpenSearchStorageTest.php +++ b/tests/Opensearch/OpensearchStorageFilterTest.php @@ -4,15 +4,16 @@ use OpenSearch\Client; use OpenSearch\ClientBuilder; +use Shopware\Storage\Common\Aggregation\AggregationCaster; use Shopware\Storage\Common\Filter\FilterAware; use Shopware\Storage\Common\Storage; -use Shopware\Storage\Opensearch\OpenSearchStorage; +use Shopware\Storage\Opensearch\OpensearchStorage; use Shopware\StorageTests\Common\FilterStorageTestBase; /** - * @covers \Shopware\Storage\Opensearch\OpenSearchStorage + * @covers \Shopware\Storage\Opensearch\OpensearchStorage */ -class OpenSearchStorageTest extends FilterStorageTestBase +class OpensearchStorageFilterTest extends FilterStorageTestBase { private ?Client $client = null; @@ -54,36 +55,45 @@ protected function setUp(): void 'properties' => [ 'key' => ['type' => 'keyword'], 'stringField' => ['type' => 'keyword'], + 'intField' => ['type' => 'integer'], + 'floatField' => ['type' => 'double'], + 'boolField' => ['type' => 'boolean'], + 'dateField' => [ + 'type' => 'date', + 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', + 'ignore_malformed' => true, + ], + 'listField' => ['type' => 'keyword'], 'translatedString' => [ - 'type' => 'nested', + 'type' => 'object', 'properties' => [ 'de' => ['type' => 'keyword'], 'en' => ['type' => 'keyword'], ] ], 'translatedInt' => [ - 'type' => 'nested', + 'type' => 'object', 'properties' => [ 'de' => ['type' => 'integer'], 'en' => ['type' => 'integer'], ] ], 'translatedFloat' => [ - 'type' => 'nested', + 'type' => 'object', 'properties' => [ - 'de' => ['type' => 'float'], - 'en' => ['type' => 'float'], + 'de' => ['type' => 'double'], + 'en' => ['type' => 'double'], ] ], 'translatedBool' => [ - 'type' => 'nested', + 'type' => 'object', 'properties' => [ 'de' => ['type' => 'boolean'], 'en' => ['type' => 'boolean'], ] ], 'translatedDate' => [ - 'type' => 'nested', + 'type' => 'object', 'properties' => [ 'de' => [ 'type' => 'date', @@ -97,23 +107,14 @@ protected function setUp(): void ], ] ], - 'intField' => ['type' => 'integer'], - 'floatField' => ['type' => 'float'], - 'boolField' => ['type' => 'boolean'], - 'dateField' => [ - 'type' => 'date', - 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', - 'ignore_malformed' => true, - ], - 'listField' => ['type' => 'keyword'], 'objectField' => [ 'type' => 'nested', 'properties' => [ - 'foo' => ['type' => 'keyword'], - 'fooInt' => ['type' => 'integer'], - 'fooFloat' => ['type' => 'float'], - 'fooBool' => ['type' => 'boolean'], - 'fooDate' => [ + 'stringField' => ['type' => 'keyword'], + 'intField' => ['type' => 'integer'], + 'floatField' => ['type' => 'double'], + 'boolField' => ['type' => 'boolean'], + 'dateField' => [ 'type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', 'ignore_malformed' => true, @@ -123,11 +124,11 @@ protected function setUp(): void 'objectListField' => [ 'type' => 'nested', 'properties' => [ - 'foo' => ['type' => 'keyword'], - 'fooInt' => ['type' => 'integer'], - 'fooFloat' => ['type' => 'float'], - 'fooBool' => ['type' => 'boolean'], - 'fooDate' => [ + 'stringField' => ['type' => 'keyword'], + 'intField' => ['type' => 'integer'], + 'floatField' => ['type' => 'double'], + 'boolField' => ['type' => 'boolean'], + 'dateField' => [ 'type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss.000||strict_date_optional_time||epoch_millis', 'ignore_malformed' => true, @@ -149,11 +150,11 @@ public static function tearDownAfterClass(): void $client = $builder->build(); $exists = $client->indices() - ->exists(['index' => FilterStorageTestBase::TEST_STORAGE]); + ->exists(['index' => 'test_storage']); if ($exists) { $client->indices() - ->delete(['index' => FilterStorageTestBase::TEST_STORAGE]); + ->delete(['index' => 'test_storage']); } } @@ -161,7 +162,8 @@ public function getStorage(): FilterAware&Storage { return new OpensearchLiveStorage( $this->getClient(), - new OpenSearchStorage( + new OpensearchStorage( + new AggregationCaster(), $this->getClient(), $this->getSchema() ),