From 17d1dc37402d67dba55a681d0b913bc03b1fb5ac Mon Sep 17 00:00:00 2001 From: Vladimir Zbrailov Date: Fri, 21 Aug 2020 12:12:51 +0300 Subject: [PATCH 1/3] added support of or operation and nested conditions --- src/Builders/FilterBuilder.php | 300 ++++++++++++++++++++++++++++----- 1 file changed, 258 insertions(+), 42 deletions(-) diff --git a/src/Builders/FilterBuilder.php b/src/Builders/FilterBuilder.php index 5f47fa5..8e97998 100644 --- a/src/Builders/FilterBuilder.php +++ b/src/Builders/FilterBuilder.php @@ -16,6 +16,7 @@ class FilterBuilder extends Builder public $wheres = [ 'must' => [], 'must_not' => [], + 'should' => [], ]; /** @@ -82,23 +83,26 @@ public function __construct(Model $model, $callback = null, $softDelete = false) * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-query.html Range query * * Supported operators are =, >, <, >=, <=, <> - * @param string $field Field name - * @param mixed $value Scalar value or an array - * @return $this + * + * @param string|\Closure $field + * @param null $operator + * @param null $value + * @param string $boolean + * @return $this|FilterBuilder */ - public function where($field, $value) + public function where($field, $operator = null, $value = null, $boolean = 'must') { - $args = func_get_args(); - - if (count($args) === 3) { - [$field, $operator, $value] = $args; - } else { - $operator = '='; + if ($field instanceof \Closure) { + return $this->whereNested($field, $boolean); } + [$value, $operator] = $this->prepareValueAndOperator( + $value, $operator, func_num_args() === 2 + ); + switch ($operator) { case '=': - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'term' => [ $field => $value, ], @@ -106,7 +110,7 @@ public function where($field, $value) break; case '>': - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'range' => [ $field => [ 'gt' => $value, @@ -116,7 +120,7 @@ public function where($field, $value) break; case '<': - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'range' => [ $field => [ 'lt' => $value, @@ -126,7 +130,7 @@ public function where($field, $value) break; case '>=': - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'range' => [ $field => [ 'gte' => $value, @@ -136,7 +140,7 @@ public function where($field, $value) break; case '<=': - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'range' => [ $field => [ 'lte' => $value, @@ -147,17 +151,83 @@ public function where($field, $value) case '!=': case '<>': - $this->wheres['must_not'][] = [ + $term = [ 'term' => [ $field => $value, ], ]; + $this->setNegativeCondition($term, $boolean); break; } return $this; } + /** + * @param $column + * @param null $operator + * @param null $value + * @return $this|\Illuminate\Database\Query\Builder + */ + public function orWhere($column, $operator = null, $value = null) + { + [$value, $operator] = $this->prepareValueAndOperator( + $value, $operator, func_num_args() === 2 + ); + + return $this->where($column, $operator, $value, 'should'); + } + + /** + * @param Closure $callback + * @param string $boolean + * @return $this + */ + public function whereNested(\Closure $callback, $boolean = 'must') + { + /** @var $filter FilterBuilder */ + call_user_func($callback, $filter = $this->model::search('*')); + + $payload = $filter->buildPayload(); + $this->wheres[$boolean][] = $payload[0]['body']['query']['bool']['filter']; + + return $this; + } + + /** + * Prepare the value and operator for a where clause. + * + * @param string $value + * @param string $operator + * @param bool $useDefault + * @return array + * + * @throws \InvalidArgumentException + */ + public function prepareValueAndOperator($value, $operator, $useDefault = false) + { + if ($useDefault) { + return [$operator, '=']; + } + + return [$value, $operator]; + } + + /** + * @param $condition + * @param string $boolean + */ + public function setNegativeCondition($condition, $boolean = 'must') + { + if ($boolean == 'should') { + $cond['bool']['must_not'][] = $condition; + + $this->wheres[$boolean][] = $cond; + } else { + $this->wheres['must_not'][] = $condition; + } + } + /** * Add a whereIn condition. * @@ -165,11 +235,12 @@ public function where($field, $value) * * @param string $field * @param array $value + * @param string $boolean * @return $this */ - public function whereIn($field, array $value) + public function whereIn($field, array $value, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'terms' => [ $field => $value, ], @@ -178,6 +249,16 @@ public function whereIn($field, array $value) return $this; } + /** + * @param $field + * @param array $value + * @return $this + */ + public function orWhereIn($field, array $value) + { + return $this->whereIn($field, $value, 'should'); + } + /** * Add a whereNotIn condition. * @@ -185,15 +266,29 @@ public function whereIn($field, array $value) * * @param string $field * @param array $value + * @param string $boolean * @return $this */ - public function whereNotIn($field, array $value) + public function whereNotIn($field, array $value, $boolean = 'must') { - $this->wheres['must_not'][] = [ + $term = [ 'terms' => [ $field => $value, ], ]; + $this->setNegativeCondition($term, $boolean); + + return $this; + } + + /** + * @param $field + * @param array $value + * @return $this + */ + public function orWhereNotIn($field, array $value) + { + return $this->whereNotIn($field, $value, 'should'); return $this; } @@ -205,11 +300,12 @@ public function whereNotIn($field, array $value) * * @param string $field * @param array $value + * @param string $boolean * @return $this */ - public function whereBetween($field, array $value) + public function whereBetween($field, array $value, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'range' => [ $field => [ 'gte' => $value[0], @@ -221,6 +317,16 @@ public function whereBetween($field, array $value) return $this; } + /** + * @param $field + * @param array $value + * @return $this + */ + public function orWhereBetween($field, array $value) + { + return $this->whereBetween($field, $value); + } + /** * Add a whereNotBetween condition. * @@ -228,11 +334,12 @@ public function whereBetween($field, array $value) * * @param string $field * @param array $value + * @param string $boolean * @return $this */ - public function whereNotBetween($field, array $value) + public function whereNotBetween($field, array $value, $boolean = 'must') { - $this->wheres['must_not'][] = [ + $term = [ 'range' => [ $field => [ 'gte' => $value[0], @@ -240,21 +347,32 @@ public function whereNotBetween($field, array $value) ], ], ]; + $this->setNegativeCondition($term, $boolean); return $this; } + /** + * @param $field + * @param array $value + */ + public function orWhereNotBetween($field, array $value) + { + return $this->whereNotBetween($field, $value, 'should'); + } + /** * Add a whereExists condition. * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html Exists query * * @param string $field + * @param string $boolean * @return $this */ - public function whereExists($field) + public function whereExists($field, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'exists' => [ 'field' => $field, ], @@ -263,35 +381,58 @@ public function whereExists($field) return $this; } + /** + * @param $field + * @return $this + */ + public function orWhereExists($field) + { + return $this->whereExists($field, 'should'); + } + /** * Add a whereNotExists condition. * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html Exists query * * @param string $field + * @param string $boolean * @return $this */ - public function whereNotExists($field) + public function whereNotExists($field, $boolean = 'must') { - $this->wheres['must_not'][] = [ + $term = [ 'exists' => [ 'field' => $field, ], ]; + $this->setNegativeCondition($term, $boolean); return $this; } + /** + * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html Exists query + * + * @param string $field + * @return $this|FilterBuilder + */ + public function orWhereNotExists($field) + { + return $this->whereNotExists($field, 'should'); + } + /** * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html Match query * * @param string $field * @param string $value + * @param string $boolean * @return $this */ - public function whereMatch($field, $value) + public function whereMatch($field, $value, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'match' => [ $field => $value, ], @@ -300,24 +441,47 @@ public function whereMatch($field, $value) return $this; } + /** + * @param $field + * @param $value + * @return $this + */ + public function orWhereMatch($field, $value) + { + return $this->whereMatch($field, $value, 'should'); + } + + /** * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html Match query * * @param string $field * @param string $value + * @param string $boolean * @return $this */ - public function whereNotMatch($field, $value) + public function whereNotMatch($field, $value, $boolean = 'must') { - $this->wheres['must_not'][] = [ + $term = [ 'match' => [ $field => $value, ], ]; + $this->setNegativeCondition($term, $boolean); return $this; } + /** + * @param $field + * @param $value + * @return $this + */ + public function orWhereNotMatch($field, $value) + { + return $this->whereNotMatch($field, $value, 'should'); + } + /** * Add a whereRegexp condition. * @@ -326,11 +490,12 @@ public function whereNotMatch($field, $value) * @param string $field * @param string $value * @param string $flags + * @param string $boolean * @return $this */ - public function whereRegexp($field, $value, $flags = 'ALL') + public function whereRegexp($field, $value, $flags = 'ALL', $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'regexp' => [ $field => [ 'value' => $value, @@ -342,6 +507,11 @@ public function whereRegexp($field, $value, $flags = 'ALL') return $this; } + public function orWhereRegexp($field, $value, $flags = 'ALL') + { + return $this->whereRegexp($field, $value, $flags, 'should'); + } + /** * Add a whereGeoDistance condition. * @@ -350,11 +520,12 @@ public function whereRegexp($field, $value, $flags = 'ALL') * @param string $field * @param string|array $value * @param int|string $distance + * @param string $boolean * @return $this */ - public function whereGeoDistance($field, $value, $distance) + public function whereGeoDistance($field, $value, $distance, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'geo_distance' => [ 'distance' => $distance, $field => $value, @@ -364,6 +535,17 @@ public function whereGeoDistance($field, $value, $distance) return $this; } + /** + * @param $field + * @param $value + * @param $distance + * @return $this + */ + public function orWhereGeoDistance($field, $value, $distance) + { + return $this->whereGeoDistance($field, $value, $distance, 'should'); + } + /** * Add a whereGeoBoundingBox condition. * @@ -371,11 +553,12 @@ public function whereGeoDistance($field, $value, $distance) * * @param string $field * @param array $value + * @param string $boolean * @return $this */ - public function whereGeoBoundingBox($field, array $value) + public function whereGeoBoundingBox($field, array $value, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'geo_bounding_box' => [ $field => $value, ], @@ -384,6 +567,16 @@ public function whereGeoBoundingBox($field, array $value) return $this; } + /** + * @param $field + * @param $value + * @return $this + */ + public function orWhereGeoBoundingBox($field, $value) + { + return $this->whereGeoBoundingBox($field, $value, 'should'); + } + /** * Add a whereGeoPolygon condition. * @@ -391,11 +584,12 @@ public function whereGeoBoundingBox($field, array $value) * * @param string $field * @param array $points + * @param string $boolean * @return $this */ - public function whereGeoPolygon($field, array $points) + public function whereGeoPolygon($field, array $points, $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'geo_polygon' => [ $field => [ 'points' => $points, @@ -406,6 +600,16 @@ public function whereGeoPolygon($field, array $points) return $this; } + /** + * @param $field + * @param array $points + * @return $this + */ + public function orWhereGeoPolygon($field, array $points) + { + return $this->whereGeoPolygon($field, $points, 'should'); + } + /** * Add a whereGeoShape condition. * @@ -414,11 +618,12 @@ public function whereGeoPolygon($field, array $points) * @param string $field * @param array $shape * @param string $relation + * @param string $boolean * @return $this */ - public function whereGeoShape($field, array $shape, $relation = 'INTERSECTS') + public function whereGeoShape($field, array $shape, $relation = 'INTERSECTS', $boolean = 'must') { - $this->wheres['must'][] = [ + $this->wheres[$boolean][] = [ 'geo_shape' => [ $field => [ 'shape' => $shape, @@ -430,6 +635,17 @@ public function whereGeoShape($field, array $shape, $relation = 'INTERSECTS') return $this; } + /** + * @param $field + * @param array $shape + * @param string $relation + * @return $this + */ + public function orWhereGeoShape($field, array $shape, $relation = 'INTERSECTS') + { + return $this->whereGeoShape($field, $shape, $relation, 'should'); + } + /** * Add a orderBy clause. * From 44dc20262531a7f80ec92dcc4f0bdfcc85d1c865 Mon Sep 17 00:00:00 2001 From: Vladimir Zbrailov Date: Fri, 21 Aug 2020 12:24:56 +0300 Subject: [PATCH 2/3] style fix --- src/Builders/FilterBuilder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Builders/FilterBuilder.php b/src/Builders/FilterBuilder.php index 8e97998..881674e 100644 --- a/src/Builders/FilterBuilder.php +++ b/src/Builders/FilterBuilder.php @@ -451,7 +451,6 @@ public function orWhereMatch($field, $value) return $this->whereMatch($field, $value, 'should'); } - /** * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html Match query * From 3832a9ef6fc84a6f5f2e337cb5e7ef743fe67a15 Mon Sep 17 00:00:00 2001 From: Vladimir Zbrailov Date: Fri, 21 Aug 2020 12:29:19 +0300 Subject: [PATCH 3/3] test fix --- src/Builders/FilterBuilder.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Builders/FilterBuilder.php b/src/Builders/FilterBuilder.php index 881674e..f3c159d 100644 --- a/src/Builders/FilterBuilder.php +++ b/src/Builders/FilterBuilder.php @@ -16,7 +16,6 @@ class FilterBuilder extends Builder public $wheres = [ 'must' => [], 'must_not' => [], - 'should' => [], ]; /**