AbstractQueryProvider::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
/**
3
 *
4
 * @author Mihkel Viilveer <[email protected]>
5
 * @date 1.09.2014
6
 */
7
8
namespace opus\elastic\search;
9
10
use Elastica\Filter\Term;
11
use Elastica\Query\BoolQuery;
12
use Elastica\Query\MatchAll;
13
use opus\elastic\components\Query;
14
use opus\elastic\components\QueryBuilder;
15
use yii\base\BaseObject;
16
use yii\di\ServiceLocator;
17
use yii\elasticsearch\ActiveQuery;
18
use yii\elasticsearch\ActiveRecord;
19
use yii\helpers\ArrayHelper;
20
21
/**
22
 * Class AbstractQueryProvider
23
 *
24
 * @author Mihkel Viilveer <[email protected]>
25
 * @package opus\elastic\search
26
 */
27
abstract class AbstractQueryProvider extends BaseObject
28
{
29
    /**
30
     * Initial request params, that contains all the parameters in filter/query part
31
     * @var mixed[]
32
     */
33
    protected $requestParams = [];
34
35
    /**
36
     * Query instance
37
     * @var Query
38
     */
39
    protected $query;
40
41
    /**
42
     * QueryBuilder instance to handle yii default syntax eg ['not' => ['id' => 'test']]
43
     * @var QueryBuilder
44
     */
45
    private $queryBuilder;
46
47
    /**
48
     * This service locator holds all attribute handlers
49
     * @var ServiceLocator
50
     */
51
    protected $locator;
52
53
    /**
54
     * Class instance for formatting results after successful request to elasticsearch
55
     * @var string
56
     */
57
    public $resultsFormatter;
58
59
    /**
60
     * Return the searchable data model
61
     *
62
     * @return ActiveRecord
63
     */
64
    abstract public function getModel();
65
66
    /**
67
     * Returns query handlers for special cases
68
     * @return array
69
     */
70
    abstract public function attributeHandlers();
71
72
    /**
73
     * @param array $requestParams
74
     * @param array $config
75
     */
76
    public function __construct($requestParams = [], $config = [])
77
    {
78
        parent::__construct($config);
79
80
        $this->queryBuilder = \Yii::createObject(QueryBuilder::class, [null]);
81
82
        $this->locator = new ServiceLocator();
83
        $this->locator->setComponents($this->attributeHandlers());
84
85
        $query = $this->getQueryInstance();
86
        $query->query = new BoolQuery();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Elastica\Query\BoolQuery() of type object<Elastica\Query\BoolQuery> is incompatible with the declared type array|string|object<Elastica\Query\Bool> of property $query.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
87
        $query->filter = new \opus\elastic\elastica\filter\BoolQuery();
88
        $query->limit = ArrayHelper::remove($requestParams, 'limit', 0);
89
        $query->offset = ArrayHelper::remove($requestParams, 'offset', 0);
90
        $query->orderBy = ArrayHelper::remove($requestParams, 'sort');
0 ignored issues
show
Documentation Bug introduced by
It seems like \yii\helpers\ArrayHelper...$requestParams, 'sort') of type * is incompatible with the declared type array of property $orderBy.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
91
92
        $this->requestParams = $requestParams;
93
94
        $this->setAttributes();
95
    }
96
    /**
97
     * This method is used to add conditions to query
98
     *
99
     * @param string $attribute
100
     * @param string|array $value
101
     * @return $this
102
     */
103
    public function setAttribute($attribute, $value)
104
    {
105
        $query = $this->getQueryInstance();
106
        if ($this->locator->has($attribute)) {
107
            /** @var QueryHandlerInterface $specialHandler */
108
            $specialHandler = $this->locator->get($attribute);
109
            list($query->query, $query->filter, $query->aggregations) = $specialHandler->handle([
110
                'query' => $query->query,
111
                'filter' => $query->filter,
112
                'aggregations' => $query->aggregations,
113
                'value' => $value
114
            ]);
115
        } elseif ($this->isValidAttribute($attribute)) {
116
            $filter = new Term(
0 ignored issues
show
Deprecated Code introduced by
The class Elastica\Filter\Term has been deprecated with message: Filters are deprecated. Use queries in filter context. See https://www.elastic.co/guide/en/elasticsearch/reference/2.0/query-dsl-filters.html

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
117
                [$attribute => $value]);
118
            $query->filter->addMust($filter);
119
        }
120
        return $this;
121
    }
122
123
    /**
124
     * Avoids sending junk to search server
125
     * @param $attribute
126
     * @return bool
127
     * @throws \yii\base\InvalidConfigException
128
     */
129
    protected function isValidAttribute($attribute)
130
    {
131
        return in_array($attribute, $this->getModel()->attributes());
132
    }
133
134
    /**
135
     * Mass sets attributes to query
136
     */
137
    protected function setAttributes()
138
    {
139
        foreach ($this->requestParams as $field => $condition) {
140
            if (is_numeric($field) && is_array($condition)) {
141
                $builtCondition = $this->queryBuilder->buildCondition($condition);
142
                $this->getQueryInstance()->filter->addMust([$builtCondition]);
143
            } else {
144
                $this->setAttribute($field, $condition);
145
            }
146
        }
147
    }
148
149
    /**
150
     * @param bool $multiSearch
151
     * @return ActiveQuery|array
152
     */
153
    public function getQuery($multiSearch = true)
154
    {
155
        $query = $this->getQueryInstance();
156
        $activeQuery = $this->getBaseQuery();
157
158
        if ($multiSearch === true) {
159
            $activeQuery = [
160
                'query' => $activeQuery->query->toArray(),
0 ignored issues
show
Bug introduced by
The method toArray cannot be called on $activeQuery->query (of type array|string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
161
                'from' => $query->offset,
162
                'size' => $query->limit,
163
            ];
164
            if (!is_null($query->orderBy)) {
165
                $activeQuery['sort'] = $this->getQueryInstance()->orderBy;
166
            }
167
            if (!empty($query->aggregations)) {
168
                $activeQuery['aggs'] = $this->getQueryInstance()->aggregations;
169
            }
170
            return $activeQuery;
171
        }
172
173
        return $activeQuery
174
            ->limit($query->limit)
175
            ->offset($query->offset)
176
            ->orderBy($query->orderBy);
177
    }
178
179
    /**
180
     * Returns base query
181
     * @return ActiveQuery
182
     */
183
    private function getBaseQuery()
184
    {
185
        $query = $this->getQueryInstance();
186
187
        $query->query = $query->query == new BoolQuery()
188
            ? new MatchAll() : $query->query;
189
190
        $query->filter = $query->filter == new \Elastica\Query\BoolQuery
191
            ? new \Elastica\Filter\MatchAll() : $query->filter;
0 ignored issues
show
Deprecated Code introduced by
The class Elastica\Filter\MatchAll has been deprecated with message: Filters are deprecated. Use queries in filter context. See https://www.elastic.co/guide/en/elasticsearch/reference/2.0/query-dsl-filters.html

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
192
193
        return $this->getModel()
194
                    ->find()
195
                    ->query($query->query)
196
                    ->where($query->filter)
197
                    ->limit(null)
198
                    ->offset(null)
199
                    ->orderBy(null);
200
201
    }
202
203
    /**
204
     * @param array $aggregations
205
     * @return AbstractQueryProvider
206
     */
207
    public function setAggregations($aggregations)
208
    {
209
        $this->query->aggregations = $aggregations;
210
        return $this;
211
    }
212
213
    /**
214
     * @param array $config
215
     * @throws \yii\base\InvalidConfigException
216
     * @return ResultsFormatterInterface
217
     */
218
    public function getResultsFormatter($config = [])
219
    {
220
        $config = ArrayHelper::merge($config, [
221
            'queryProvider' => $this,
222
            'class' => $this->resultsFormatter
223
        ]);
224
        return \Yii::createObject($config);
225
    }
226
227
    /**
228
     * Returns user search keywords
229
     * @return array
230
     */
231
    public function getSearchKeywords()
232
    {
233
        return $this->requestParams;
234
    }
235
236
    /**
237
     * @return Query
238
     */
239
    public function getQueryInstance()
240
    {
241
        if (is_null($this->query)) {
242
            $this->query = \Yii::createObject(Query::class);
243
        }
244
        return $this->query;
245
    }
246
}
247