Skip to content

Commit

Permalink
Add ability to create a request filter with query builder filter
Browse files Browse the repository at this point in the history
- Also you can extend RequestFilter to prepare request filter faster
- You can add BoolFilter to filter entries by Term query filter (false / true)
- Add ability to create a map of request attributes that will be created into query builder filters

$this->addQueryFilters([
            'gender' => TermFilter::class,
            'city' => MatchFilter::class,
            'country_code' => TermFilter::class,
            'citizen_of' => TermFilter::class,
            'phone' => MatchFilter::class,
        ]);
  • Loading branch information
pionl committed Apr 17, 2020
1 parent 0be448b commit b772c4d
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/Search/Request/BoolFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Lelastico\Search\Request;

use Erichard\ElasticQueryBuilder\Filter\Filter;
use Illuminate\Http\Request;
use Lelastico\Search\Query\GivenFilters;

/**
* Adds term filter when given bool value is in the request.
*/
class BoolFilter extends RequestFilter
{
public function __construct(Request $request, string $requestKey, string $fieldName = null)
{
$fieldName = is_string($fieldName) ? $fieldName : $requestKey;
parent::__construct($request, $requestKey, function ($value) use ($fieldName) {
return new GivenFilters([Filter::term()->setField($fieldName)->setValue($value)]);
});

// Ensure that value is boolean
if ('true' === $this->value || '1' === $this->value) {
$this->value = true;
} elseif ('false' === $this->value || '0' === $this->value) {
$this->value = false;
} else {
$this->value = null;
}
}

public function canApply(): bool
{
return true === $this->value || false === $this->value;
}
}
20 changes: 20 additions & 0 deletions src/Search/Request/Filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Lelastico\Search\Request;

use Erichard\ElasticQueryBuilder\Filter\MatchFilter;
use Erichard\ElasticQueryBuilder\Filter\TermFilter;
use Illuminate\Http\Request;
use Lelastico\Search\Query\AbstractBuilder;

Expand Down Expand Up @@ -42,6 +44,24 @@ public function addFilter(AbstractFilter $filter): self
return $this;
}

/**
* Creates RequestQueryFilter from the array and adds them as filter. Each $createQueryFilters is keyed by the
* request key (value is in the same format as $createQueryFilters parameter in RequestQueryFilter construct).
*
* @param array $requestQueryFilterMap example: $this->addQueryFilters([
* 'citizen_of' => TermFilter::class,
* 'phone' => [MatchFilter::class, 'phones'],
* 'is_verified' => TermFilter::class,
* ]);
* @param bool $scoring Does filter counts to scoring?
*/
public function addQueryFilters(array $requestQueryFilterMap, bool $scoring = false)
{
foreach ($requestQueryFilterMap as $requestKey => $createQueryFilters) {
$this->addFilter(new RequestQueryFilter($this->request, $requestKey, $createQueryFilters, $scoring));
}
}

/**
* Adds queries into query builder from applicable filters. Setups page / per_page from the request.
*
Expand Down
56 changes: 56 additions & 0 deletions src/Search/Request/RequestFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Lelastico\Search\Request;

use Illuminate\Http\Request;
use Lelastico\Search\Query\AbstractQuery;

class RequestFilter extends AbstractFilter
{
/**
* @var string|null
*/
public $value;

/**
* @var string
*/
public $requestKey;

/**
* Used for building a query with the value.
*
* @var string|callable
*/
public $queryClass;

/**
* Request filter that will create a query of given class with request value only if the value is not empty.
*
* @param Request $request
* @param string $requestKey resolves value from the $request under given key
* @param string|callable $queryClass Creates a query of given class with value as first argument (provide closure
* that will create query, first argument contains value).
* Example: fn($value) => new NameQuery($value)
*/
public function __construct(Request $request, string $requestKey, $queryClass)
{
$this->value = $request->get($requestKey);
$this->requestKey = $requestKey;
$this->queryClass = $queryClass;
}

public function createQuery(): AbstractQuery
{
if (is_callable($this->queryClass)) {
return call_user_func($this->queryClass, $this->value);
}

return new $this->queryClass($this->value);
}

public function canApply(): bool
{
return !empty($this->value);
}
}
73 changes: 73 additions & 0 deletions src/Search/Request/RequestQueryFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Lelastico\Search\Request;

use Erichard\ElasticQueryBuilder\Filter\Filter;
use Exception;
use Illuminate\Http\Request;
use Lelastico\Search\Query\GivenFilters;

class RequestQueryFilter extends RequestFilter
{
/**
* Request filter that will create a query builder filters using $createQueryFilters closure with request value
* only if the value is not empty.
*
* @param Request $request
* @param string $requestKey
* @param callable|string|array $createQueryFilters Creates a a list of query filters (instances of
* Erichard\ElasticQueryBuilder\Filter\Filter) with given value.
* 1. You can provide callable that will create the filters
* fn($value) =>
* [Filter::match()->->setField()->setQuery($value)]
* 2. You can provide which query builder class to build (field
* will be set from request key) like TermFilter::class, $value
* will be set using setValue or setQuery.
* 3. You can provide which query builder class to build and
* field name to use by passing an array as following example:
* [TermFilter::class, 'test']
* @param bool $scoring Does filter counts to scoring?
*/
public function __construct(Request $request, string $requestKey, $createQueryFilters, bool $scoring = false)
{
parent::__construct(
$request,
$requestKey,
function ($value) use ($createQueryFilters, $requestKey, $scoring) {
// Create builder filter
if (is_callable($createQueryFilters)) {
return new GivenFilters(call_user_func($createQueryFilters, $value), $scoring);
}

// We need to get desired builder filter class and field name - if string is provided, then the
// key is same as request key. Otherwise we can get the class and field name from
// array [class, fieldName]
if (is_string($createQueryFilters)) {
$builderFilterClass = $createQueryFilters;
$field = $requestKey;
} elseif (is_array($createQueryFilters)) {
list($builderFilterClass, $field) = $createQueryFilters;
} else {
throw new Exception('Un supported $createQueryFilters type. Expects: callable, string or array');
}

/** @var Filter $filter */
$filter = new $builderFilterClass();

// Does the filter support setField?
if (method_exists($filter, 'setField')) {
$filter->setField($field);
}

// Should we set value by using setValue or setQuery?
if (method_exists($filter, 'setValue')) {
$filter->setValue($value);
} elseif (method_exists($filter, 'setQuery')) {
$filter->setQuery($value);
}

return new GivenFilters([$filter], $scoring);
}
);
}
}

0 comments on commit b772c4d

Please sign in to comment.