Mirocow/yii2-elasticsearch

View on GitHub
src/components/queries/QueryBuilder.php

Summary

Maintainability
B
5 hrs
Test Coverage
<?php
namespace mirocow\elasticsearch\components\queries;

use mirocow\elasticsearch\components\queries\Aggregation\Aggregation;
use mirocow\elasticsearch\components\queries\Aggregation\AggregationMulti;
use mirocow\elasticsearch\components\queries\helpers\QueryHelper;
use mirocow\elasticsearch\exceptions\SearchQueryException;
use yii\base\Exception;
use yii\helpers\ArrayHelper;

class QueryBuilder
{
    /**
     * @var null
     */
    private $query = null;

    /**
     * @var array
     */
    private $body = [];

    /**
     * Can accept parameters:
     * '*', false - e.t.c.
     * @var array|string|bool
     */
    private $withSource = false;

    /**
     * @var array
     */
    private $filter = [];

    /**
     * @var array
     */
    private $post_filter = [];

    /**
     * @var array
     */
    private $script_fields = [];

    /**
     * @var array
     */
    private $docvalue_fields = [];

    /**
     * @var Aggregation|AggregationMulti
     */
    public $aggs = [];

    /**
     * @var array
     */
    private $highlight = [];

    /**
     * @var array
     */
    private $_source = [];

    /**
     * @var int
     */
    private $from = 0;

    /**
     * @var int
     */
    private $size = 10000;

    /**
     * @var array
     */
    private $sort = [];

    /**
     * @var array
     */
    private $rescore = [];

    /**
     * @var bool
     */
    private $release = true;

    /**
     * @var bool
     */
    private $store = false;

    /**
     * @var array
     */
    private $result = [];

    /** @var float */
    private $min_score = 0.5;

    /**
     * @param string $key
     * @param $value
     * @return $this
     */
    public function set($key, $value)
    {
        if($value) {
            ArrayHelper::setValue($this->query, $key, $value);
        }
        return $this;
    }

    /**
     * @param string $key
     * @return mixed
     */
    public function get($key)
    {
        return ArrayHelper::getValue($this->query, $key);
    }

    /**
     * @param $value
     * @return $this
     */
    public function add($value)
    {
        if($value) {
            $this->query = ArrayHelper::merge($this->query, $value);
        }
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-match-query.html
     * @param string|array $query
     * @return $this
     */
    public function query($query = '')
    {
        if($query) {
            $query = QueryHelper::query($query);
            $this->query = $query->query;
        }
        return $this;
    }

    /**
     * The size parameter allows you to configure the maximum amount of hits to be returned.
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-from-size.html
     * @param int $size
     * @return $this
     */
    public function limit(int $size = 10000)
    {
        $this->size = $size;
        return $this;
    }

    /**
     * Pagination of results can be done by using the from and size parameters.
     * The from parameter defines the offset from the first result you want to fetch.
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-from-size.html
     * @param int $from
     * @return $this
     */
    public function offset(int $from = 0)
    {
        $this->from = $from;
        return $this;
    }

    /**
     * Allows to add one or more sort on specific fields. Each sort can be reversed as well.
     * The sort is defined on a per field level, with special field name for _score to sort by score, and _doc to sort by index order.
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-sort.html
     * @param array|null $fieldsName
     * @return $this
     */
    public function sort($fieldsName = [])
    {
        $this->sort = $fieldsName;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-aggregations.html
     * @param Aggregation|AggregationMulti|null $aggregations
     * @return $this
     */
    public function aggregations($aggregations)
    {
        $this->aggs = $aggregations;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-highlighting.html
     * @param array|null $highlight
     * @return $this
     */
    public function highlight(array $highlight = [])
    {
        $this->highlight = $highlight;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-source-filtering.html
     * @param array $source
     * @return $this
     */
    public function source(array $source = [])
    {
        $this->_source = $source;
        return $this;
    }

    /**
     * The query rescorer executes a second query only on the Top-K results returned by the query and post_filter phases.
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-rescore.html
     * @param array $rescore
     * @return $this
     */
    public function rescore(array $rescore = [])
    {
        $this->rescore = $rescore;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-post-filter.html
     * @param array $filter
     * @return $this
     */
    public function filter(array $filter = [])
    {
        $this->filter = $filter;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-post-filter.html
     * @param array $filter
     * @return $this
     */
    public function post_filter(array $filter = [])
    {
        $this->post_filter = $filter;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-script-fields.html
     * @param array $fields
     * @return $this
     */
    public function script_fields(array $fields = [])
    {
        $this->script_fields = $fields;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-docvalue-fields.html
     * @param array $fields
     * @return $this
     */
    public function docvalue_fields(array $fields = [])
    {
        $this->docvalue_fields = $fields;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-min-score.html
     * @param float $min
     * @return $this
     */
    public function min_score($min = 0.5)
    {
        $this->min_score = $min;
        return $this;
    }

    /**
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-source-filtering.html
     * @param array|string|bool $data
     * @return $this
     */
    public function withSource($data = '*')
    {
        $this->withSource = $data;
        return $this;
    }

    /**
     * @return string|array
     */
    protected function prepareQuery()
    {
        return $this->query;
    }

    /**
     * @return string|array
     */
    protected function prepareFilter()
    {
        return $this->filter;
    }

    /**
     * @return string|array
     */
    protected function prepareFrom()
    {
        return $this->from;
    }

    /**
     * @return string|array
     */
    protected function prepareSize()
    {
        return $this->size;
    }

    /**
     * @return string|array
     */
    protected function preparePostfilter()
    {
        return $this->post_filter;
    }

    /**
     * @return string|array
     */
    protected function prepareScriptfields()
    {
        return $this->script_fields;
    }

    /**
     * @return string|array
     */
    protected function prepareDocvaluefields()
    {
        return $this->docvalue_fields;
    }

    /**
     * @return string|array
     */
    protected function prepareMinscore()
    {
        return $this->min_score;
    }

    /**
     * @return array|null
     */
    protected function prepareAggs()
    {
        if(is_array($this->aggs) && $this->aggs){
            throw new SearchQueryException('must be an instance of a class e.g. \mirocow\elasticsearch\components\queries\Aggregation\Aggregation or \mirocow\elasticsearch\components\queries\Aggregation\AggregationMulti or its subclasses.');
        }

        if($this->aggs instanceof Aggregation){
            $aggregations = $this->aggs->generateQuery();
        }

        if($this->aggs instanceof AggregationMulti){
            $aggregations = $this->aggs->generateQuery();
        }

        if(!isset($aggregations)){
            return [];
        }

        return $aggregations;
    }

    /**
     * @return string|array
     */
    protected function prepareHighlight()
    {
        return $this->highlight;
    }

    /**
     * @return string|array
     */
    protected function prepareSort()
    {
        return $this->sort;
    }

    /**
     * @return string|array
     */
    protected function prepareSource()
    {
        return $this->_source;
    }

    /**
     * @return string|array
     */
    protected function prepareRescore()
    {
        return $this->rescore;
    }

    /**
     * @return array Elasticsearch DSL body
     * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-body.html
     */
    public function generateQuery()
    {

        $fields = [

            'query',
            'filter',
            'from',
            'size',
            'aggs',
            'highlight',
            'sort',
            'post_filter',
            'source',
            'rescore',
            //'stored_fields', // TODO: @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-stored-fields.html
            'script_fields',
            'docvalue_fields',
            'min_score',
            //'collapse', // TODO: @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-collapse.html

        ];

        foreach ($fields as $field) {
            if($partQuery = call_user_func_array([$this, 'prepare' . ucwords(str_replace('_', '', $field))], [])) {
                $this->body[$field] = $partQuery;
            }
        }

        /**
         * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/mapping-source-field.html
         */
        if (!$this->_source) {
            $this->body[ '_source' ] = $this->withSource;
        }

        /**
         * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/mapping-id-field.html
         * @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-sort.html
         */
        if (!$this->sort && !is_null($this->sort)) {
            $this->body[ 'sort' ] = QueryHelper::sortBy(['_score' => SORT_DESC]);
        }

        return $this->body;
    }

    /**
     * @return false|string
     */
    public function __toString()
    {
        return json_encode($this->generateQuery());
    }

}