1
|
|
|
<?php namespace Isswp101\Persimmon\QueryBuilder; |
2
|
|
|
|
3
|
|
|
use Illuminate\Support\Collection; |
4
|
|
|
|
5
|
|
|
abstract class Filter { |
6
|
|
|
|
7
|
|
|
const MODE_INCLUDE = 'include'; |
8
|
|
|
const MODE_EXCLUDE = 'exclude'; |
9
|
|
|
const MODE_OFF = 'off'; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Filter mode. |
13
|
|
|
* |
14
|
|
|
* @var string Can be as 'include', 'exclude', 'off'. |
15
|
|
|
*/ |
16
|
|
|
protected $mode; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Filter values. |
20
|
|
|
* |
21
|
|
|
* @var mixed |
22
|
|
|
*/ |
23
|
|
|
protected $values; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* This array contains linked instances of current filter. |
27
|
|
|
* |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $linkedFilters = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Filters are merged using AND (must) by default. |
34
|
|
|
* But you can override it and merge them using OR (should). |
35
|
|
|
* |
36
|
|
|
* @var string |
37
|
|
|
*/ |
38
|
|
|
protected $mergeType = 'AND'; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Linked filters are merged using AND (must) by default. |
42
|
|
|
* But you can override it and merge them using OR (should). |
43
|
|
|
* |
44
|
|
|
* @var string |
45
|
|
|
*/ |
46
|
|
|
protected $logicalOperator = 'OR'; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Constructor. |
50
|
|
|
* |
51
|
|
|
* @param mixed $values Filter values. |
52
|
|
|
* @param string $mode Filter mode. |
53
|
|
|
* @param string $logicalOperator Linked filters will be interconnected via "OR" || "AND". |
54
|
|
|
* @param array $linkedFilters |
55
|
|
|
*/ |
56
|
|
|
public function __construct($values = null, $mode = self::MODE_INCLUDE, $logicalOperator = 'OR', array $linkedFilters = []) { |
57
|
|
|
$this->values = $values; |
58
|
|
|
$this->setOptions($mode, $logicalOperator, $linkedFilters); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Set filter options. |
63
|
|
|
* |
64
|
|
|
* @param string $mode |
65
|
|
|
* @param string $logicalOperator |
66
|
|
|
* @param array $linkedFilters |
67
|
|
|
* @return $this |
68
|
|
|
*/ |
69
|
|
|
public function setOptions($mode = null, $logicalOperator = null, array $linkedFilters = []) { |
70
|
|
|
$this->mode = is_null($mode) ? self::MODE_INCLUDE : $mode; |
71
|
|
|
$this->logicalOperator = is_null($logicalOperator) ? 'OR' : $logicalOperator; |
72
|
|
|
$this->linkedFilters = $linkedFilters; |
73
|
|
|
return $this; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Returns the actual elasticsearch query for one filter. |
78
|
|
|
* { |
79
|
|
|
* "term": { |
80
|
|
|
* "price": "0" |
81
|
|
|
* } |
82
|
|
|
* } |
83
|
|
|
* |
84
|
|
|
* @param mixed $values |
85
|
|
|
* @return array |
86
|
|
|
*/ |
87
|
|
|
abstract public function query($values); |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Returns wrapped elasticsearch filter query. |
91
|
|
|
* { |
92
|
|
|
* "bool": { |
93
|
|
|
* "should": [], |
94
|
|
|
* "must_not": [] |
95
|
|
|
* } |
96
|
|
|
* } |
97
|
|
|
* |
98
|
|
|
* @return array |
99
|
|
|
*/ |
100
|
|
|
public function makeQuery() { |
101
|
|
|
$query = $this->query($this->getValues()); |
102
|
|
|
|
103
|
|
|
$map = [ |
104
|
|
|
'AND' => 'must', |
105
|
|
|
'OR' => 'should' |
106
|
|
|
]; |
107
|
|
|
|
108
|
|
|
if ($this->isInclude()) { |
109
|
|
|
if ($this->getLogicalOperator() == 'AND') { |
110
|
|
|
$query = [ |
111
|
|
|
'bool' => [ |
112
|
|
|
'must' => [ |
113
|
|
|
$query |
114
|
|
|
] |
115
|
|
|
] |
116
|
|
|
]; |
117
|
|
|
} |
118
|
|
|
elseif ($this->getLogicalOperator() == 'OR') { |
119
|
|
|
$query = [ |
120
|
|
|
'bool' => [ |
121
|
|
|
'should' => [ |
122
|
|
|
$query |
123
|
|
|
] |
124
|
|
|
] |
125
|
|
|
]; |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
elseif ($this->isExclude()) { |
129
|
|
|
$mergeOperator = $map[$this->getLogicalOperator()]; |
130
|
|
|
$query = [ |
131
|
|
|
'bool' => [ |
132
|
|
|
$mergeOperator => [ |
133
|
|
|
[ |
134
|
|
|
'bool' => [ |
135
|
|
|
'must_not' => [ |
136
|
|
|
$query |
137
|
|
|
] |
138
|
|
|
] |
139
|
|
|
] |
140
|
|
|
] |
141
|
|
|
] |
142
|
|
|
]; |
143
|
|
|
} |
144
|
|
|
elseif ($this->isOff()) { |
145
|
|
|
$query = []; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
foreach ($this->getLinkedFilters() as $filter) { |
149
|
|
|
/** @var Filter $filter */ |
150
|
|
|
$extraQuery = $filter->makeQuery(); |
151
|
|
|
|
152
|
|
|
// if ($filter->isInclude()) { |
|
|
|
|
153
|
|
|
if ($this->getLogicalOperator() == 'AND') { |
154
|
|
|
$query = $this->mergeBoolQuery($query, $extraQuery, 'must'); |
155
|
|
|
} |
156
|
|
|
elseif ($this->getLogicalOperator() == 'OR') { |
157
|
|
|
$query = $this->mergeBoolQuery($query, $extraQuery, 'should'); |
158
|
|
|
} |
159
|
|
|
// } |
|
|
|
|
160
|
|
|
// elseif ($filter->isExclude()) { |
161
|
|
|
// $query = $this->mergeBoolQuery($query, $extraQuery, 'must_not'); |
162
|
|
|
// } |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
return $query; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Merges elastcisearch query with current filter query. |
170
|
|
|
* |
171
|
|
|
* @param array $query Elastcisearch query. |
172
|
|
|
* @return array Merged elasticsearch query. |
173
|
|
|
*/ |
174
|
|
|
public function mergeQuery(array $query) { |
175
|
|
|
$types = [ |
176
|
|
|
'AND' => 'must', |
177
|
|
|
'OR' => 'should' |
178
|
|
|
]; |
179
|
|
|
$type = $this->getMergeType(); |
180
|
|
|
|
181
|
|
|
$query['body']['filter']['bool'][$types[$type]][] = $this->makeQuery(); |
182
|
|
|
|
183
|
|
|
return $query; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Returns filter mode. |
188
|
|
|
* |
189
|
|
|
* @return string Can be as 'include', 'exclude', 'off'. |
190
|
|
|
*/ |
191
|
|
|
public function getMode() { |
192
|
|
|
return $this->mode; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* Returns true if mode is 'include'. |
197
|
|
|
* |
198
|
|
|
* @return bool |
199
|
|
|
*/ |
200
|
|
|
public function isInclude() { |
201
|
|
|
return $this->getMode() == self::MODE_INCLUDE; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Returns true if mode is 'exclude'. |
206
|
|
|
* |
207
|
|
|
* @return bool |
208
|
|
|
*/ |
209
|
|
|
public function isExclude() { |
210
|
|
|
return $this->getMode() == self::MODE_EXCLUDE; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Returns true if mode is 'off'. |
215
|
|
|
* |
216
|
|
|
* @return bool |
217
|
|
|
*/ |
218
|
|
|
public function isOff() { |
219
|
|
|
return $this->getMode() == self::MODE_OFF; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Retuns filter values as collection. |
224
|
|
|
* |
225
|
|
|
* @return Collection |
226
|
|
|
*/ |
227
|
|
|
public function getValuesAsCollection() { |
228
|
|
|
return new Collection($this->values); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Retuns filter values as array. |
233
|
|
|
* |
234
|
|
|
* @return mixed |
235
|
|
|
*/ |
236
|
|
|
public function getValues() { |
237
|
|
|
return $this->values; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Returns linked instances of current filter. |
242
|
|
|
* |
243
|
|
|
* @return array |
244
|
|
|
*/ |
245
|
|
|
public function getLinkedFilters() { |
246
|
|
|
return $this->linkedFilters; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* Returns true if filter has linked filters. |
251
|
|
|
* |
252
|
|
|
* @return bool |
253
|
|
|
*/ |
254
|
|
|
public function hasLinkedFilters() { |
255
|
|
|
return !empty($this->linkedFilters); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Returns filter merge type. |
260
|
|
|
* |
261
|
|
|
* @return string - "AND" || "OR" |
262
|
|
|
*/ |
263
|
|
|
public function getMergeType() { |
264
|
|
|
return $this->mergeType; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Sets filter merge type. |
269
|
|
|
* |
270
|
|
|
* @param string $mergeType - "AND" || "OR" |
271
|
|
|
*/ |
272
|
|
|
public function setMergeType($mergeType) { |
273
|
|
|
$this->mergeType = $mergeType; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Returns linked filters logical operator. |
278
|
|
|
* |
279
|
|
|
* @return string - "AND" || "OR" |
280
|
|
|
*/ |
281
|
|
|
public function getLogicalOperator() { |
282
|
|
|
return $this->logicalOperator; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Updates linked filters logical operator. |
287
|
|
|
* |
288
|
|
|
* @param string $logicalOperator - "AND" || "OR" |
289
|
|
|
*/ |
290
|
|
|
public function setLogicalOperator($logicalOperator) { |
291
|
|
|
$this->logicalOperator = $logicalOperator; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Returns true if merge type is valid. |
296
|
|
|
* @return bool |
297
|
|
|
*/ |
298
|
|
|
public function isMergeTypeValid() { |
299
|
|
|
return in_array($this->mergeType, ['AND', 'OR']); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Merges BOOL elasticsearch queries. |
304
|
|
|
* |
305
|
|
|
* @param array $query1 |
306
|
|
|
* @param array $query2 |
307
|
|
|
* @param string $type - must, must_not, should |
308
|
|
|
* @return array |
309
|
|
|
*/ |
310
|
|
|
protected function mergeBoolQuery(array $query1, array $query2, $type) { |
311
|
|
|
if (empty($query2['bool'][$type])) { |
312
|
|
|
return $query1; |
313
|
|
|
} |
314
|
|
|
else { |
315
|
|
|
if (empty($query1['bool'][$type])) { |
316
|
|
|
$query1['bool'][$type] = []; |
317
|
|
|
} |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
$query1['bool'][$type] = array_merge($query1['bool'][$type], $query2['bool'][$type]); |
321
|
|
|
|
322
|
|
|
return $query1; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
} |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.