Skip to content
This repository has been archived by the owner on Mar 4, 2022. It is now read-only.

Commit

Permalink
Merge pull request #1 from cfpinto/custom-builder
Browse files Browse the repository at this point in the history
Added custom builder to allow search with closure and orWhere
  • Loading branch information
cfpinto authored Nov 6, 2018
2 parents 0fc0b93 + 44b60cf commit 5fa4d57
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 3 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,21 @@ Change the config/scout.php file to include elasticsearch settings

## Usage

The driver will work just like described in laravel scout [documentation](https://laravel.com/docs/5.5/scout)
The driver will work just like described in laravel scout [documentation](https://laravel.com/docs/5.5/scout).

If you want the ability to use closures in where and orWhere methods your model must use the Searchable trait included in the package

```php
<?php
namespace App;

use ScoutEngines\Elasticsearch\Traits\Searchable;

class Product extends Model
{
use Searchable;
}
```

## Credits

Expand Down
228 changes: 228 additions & 0 deletions src/Builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
<?php
/**
* Created by PhpStorm.
* User: claudiopinto
* Date: 05/11/2018
* Time: 13:32
*/

namespace ScoutEngines\Elasticsearch;

use Closure;

class Builder extends \Laravel\Scout\Builder
{
const TYPE_AND = 'AND';
const TYPE_OR = 'OR';
const VALID_TYPES = [self::TYPE_AND, self::TYPE_OR];

/**
* @param string|Closure $field
* @param null $value
*
* @return $this|\Laravel\Scout\Builder
* @throws \Exception
*/
public function where($field, $value = null)
{
if ($field instanceof Closure) {
$field($query = new self($this->model, $this->query));
$this->nestedWhere($query);

return $this;
}
$this->addWhere($field, $value);

return $this;
}

/**
* @param string|Closure $field
* @param null $value
*
* @return $this
* @throws \Exception
*/
public function orWhere($field, $value = null)
{
if (is_callable($field)) {
$field($query = new self($this->model, $this->query));
$this->nestedWhere($query, self::TYPE_OR);

return $this;
}
$this->addWhere($field, $value, self::TYPE_OR);

return $this;
}

/**
* @return string
*/
public function toSql()
{
return $this->parseGroup($this->wheres);
}

/**
* @return array
*/
public function toESDL()
{
return $this->parseEsGroup($this->wheres);
}

/**
* @param array $list
*
* @return array
*/
private function parseEsGroup($list = [])
{
$query = [];
if (count($list)) {
$ands = [];
$ors = [];
foreach ($list as $item) {
if ($item['type'] == self::TYPE_OR) {
$ors[] = $item;
} else {
$ands[] = $item;
}
}

$tmp = [
'bool' => []
];

if (count($ors)) {
$tmp['bool'] = ['should' => []];
$using = &$tmp['bool']['should'];

foreach ($ors as $where) {
$using[] = $this->parseEsWhere($where);
}

if (count($ands)) {
$using[] = [
'bool' => [
'must' => $this->parseEsGroup($ands)
]
];
}
} else {
$tmp['bool'] = ['must' => []];
$using = &$tmp['bool']['must'];

foreach ($ands as $where) {
$using[] = $this->parseEsWhere($where);
}
}

$query[] = $tmp;
}

return $query;
}

/**
* @param $where
*
* @return array|null
*/
private function parseEsWhere($where)
{
if (isset($where['field'])) {
return $this->parseEsEntry($where);
}

$group = $this->parseEsGroup($where['group']);

return $group[0] ?? null;
}

/**
* @param $where
*
* @return array
*/
private function parseEsEntry($where)
{
if (is_array($where['value'])) {
return [
'terms' => [
$where['field'] => $where['value']
]
];
}

return [
'match_phrase' => [
$where['field'] => $where['value']
]
];
}

/**
* @param array $list
*
* @return string
*/
private function parseGroup($list = [])
{
$sql = '';
foreach ($list as $i => $item) {
if ($i > 0) {
$sql .= ' ' . $item['type'] . ' ';
}

if (isset($item['field'])) {
$sql .= sprintf('%s = %s', $item['field'], $item['value']);
} elseif (!empty($item['group'])) {
$sql .= sprintf('(%s)', $this->parseGroup($item['group']));
}
}

return $sql;
}

/**
* @param $field
* @param $value
* @param string $type
*
* @throws \Exception
*/
private function addWhere($field, $value, $type = self::TYPE_AND)
{
if (!in_array($type, self::VALID_TYPES)) {
throw new \Exception(sprintf('Invalid type %s', $type));
}

$this->wheres[] = [
'field' => $field,
'value' => $value,
'type' => $type,
];
}

/**
* @param Builder $query
* @param string $type
*
* @throws \Exception
*/
private function nestedWhere(Builder $query, $type = self::TYPE_AND)
{
if (!in_array($type, self::VALID_TYPES)) {
throw new \Exception(sprintf('Invalid type %s', $type));
}

if (count($query->wheres)) {
$this->wheres[] = [
'type' => $type,
'group' => $query->wheres,
];
}
}
}
6 changes: 5 additions & 1 deletion src/Engines/ElasticSearchEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ protected function performSearch(Builder $builder, array $options = [])
]
]
];

if (method_exists($builder->model, 'getFields') && is_array($builder->model->getFields())) {
$params['body']['query']['bool']['must'][0]['query_string']['fields'] = $builder->model->getFields();
}
Expand Down Expand Up @@ -254,6 +254,10 @@ protected function performSearch(Builder $builder, array $options = [])
*/
protected function filters(Builder $builder)
{
if ($builder instanceof \ScoutEngines\Elasticsearch\Builder) {
return ($builder->toESDL());
}

return collect($builder->wheres)->map(
function ($value, $key) {
if (is_array($value)) {
Expand Down
16 changes: 15 additions & 1 deletion src/Traits/Searchable.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,25 @@


use Laravel\Scout\Searchable as SearchableTrait;
use ScoutEngines\Elasticsearch\Builder;

trait Searchable
{
use SearchableTrait;

/**
* @param string $query
* @param Closure $callback
*
* @return Builder
*/
public static function search($query, $callback = null)
{
return new Builder(
new static, $query, $callback, config('scout.soft_delete', false)
);
}

/**
* @return array
*/
Expand All @@ -35,7 +49,7 @@ public function getFields()
{
return $this->searchable;
}

public function setFields(array $fields)
{
$this->searchable = $fields;
Expand Down

0 comments on commit 5fa4d57

Please sign in to comment.