Passed
Push — main ( 2bb698...3003e7 )
by Simon
01:15
created

QueryBuilder::getAndFilters()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 18
rs 10
1
<?php
2
3
namespace Firesphere\ElasticSearch\Queries\Builders;
4
5
use Firesphere\ElasticSearch\Indexes\ElasticIndex;
6
use Firesphere\ElasticSearch\Queries\ElasticQuery;
7
use Firesphere\SearchBackend\Indexes\CoreIndex;
8
use Firesphere\SearchBackend\Interfaces\QueryBuilderInterface;
9
use Firesphere\SearchBackend\Queries\BaseQuery;
10
11
class QueryBuilder implements QueryBuilderInterface
12
{
13
    /**
14
     * @var ElasticQuery
15
     */
16
    protected $query;
17
18
    /**
19
     * @var ElasticIndex
20
     */
21
    protected $index;
22
23
    /**
24
     * @param ElasticQuery $query
25
     * @param ElasticIndex $index
26
     * @return array
27
     */
28
    public static function buildQuery(BaseQuery $query, CoreIndex $index): array
29
    {
30
        $self = self::init($query, $index);
31
        $filters = $self->getFilters($index, $query);
32
        $terms = $self->getUserQuery($query); // There's always a term
33
        $terms = array_merge($terms, $filters);
34
35
        return [
36
            'index' => $index->getIndexName(),
37
            'from'  => $query->getStart(),
38
            'size'  => $query->getRows(),
39
            'body'  => [
40
                'query'     => [
41
                    'bool' => $terms,
42
                ],
43
                'highlight' => $self->getHighlighter()
44
            ]
45
        ];
46
    }
47
48
    /**
49
     * @param ElasticQuery $query
50
     * @param ElasticIndex $index
51
     * @return self
52
     */
53
    protected static function init(ElasticQuery $query, ElasticIndex $index): self
54
    {
55
        $self = new self();
56
        $self->setIndex($index);
57
        $self->setQuery($query);
58
59
        return $self;
60
    }
61
62
    /**
63
     * @param mixed $index
64
     */
65
    public function setIndex($index): void
66
    {
67
        $this->index = $index;
68
    }
69
70
    /**
71
     * @param mixed $query
72
     */
73
    public function setQuery($query): void
74
    {
75
        $this->query = $query;
76
    }
77
78
    /**
79
     * Build the `OR` and `AND` filters
80
     * @param ElasticIndex $index
81
     * @param ElasticQuery $query
82
     * @return array[]
83
     */
84
    private function getFilters(ElasticIndex $index, ElasticQuery $query): array
85
    {
86
        return [
87
            'filter' => [
88
                'bool' => [
89
                    'must'   => $this->getAndFilters($index, $query),
90
                    'should' => $this->getOrFilters($query)
91
                ],
92
            ]
93
        ];
94
    }
95
96
    /**
97
     * Required must-be filters if they're here.
98
     * @param ElasticIndex $index
99
     * @param ElasticQuery $query
100
     * @return array[]
101
     */
102
    private function getAndFilters(ElasticIndex $index, ElasticQuery $query): array
103
    {
104
        // Default,
105
        $filters = [
106
            [
107
                'terms' => [
108
                    'ViewStatus' => $index->getViewStatusFilter(),
109
                ]
110
            ]
111
        ];
112
        if (count($query->getFilters())) {
113
            foreach ($query->getFilters() as $key => $value) {
114
                $value = is_array($value) ?: [$value];
115
                $filters[] = ['terms' => [$key => $value]];
116
            }
117
        }
118
119
        return $filters;
120
    }
121
122
    /**
123
     * Create the "should" filter, that is OR instead of AND
124
     * @param ElasticQuery $query
125
     * @return array
126
     */
127
    private function getOrFilters(ElasticQuery $query): array
128
    {
129
        $filters = [];
130
        if (count($query->getOrFilters())) {
131
            foreach ($query->getOrFilters() as $key => $value) {
132
                $value = is_array($value) ?: [$value];
133
                $filters[] = ['terms' => [$key => $value]];
134
            }
135
        }
136
137
        return $filters;
138
    }
139
140
    /**
141
     * this allows for multiple search terms to be entered
142
     * @param ElasticQuery|BaseQuery $query
143
     * @return array
144
     */
145
    private function getUserQuery(ElasticQuery|BaseQuery $query): array
146
    {
147
        $q = [];
148
        $terms = $query->getTerms();
149
        $type = 'match';
150
        if (!count($terms)) {
151
            $type = 'wildcard';
152
            $terms = ['*'];
153
        }
154
        foreach ($terms as $term) {
155
            $q['must'][] = ['match' => ['_text' => $term['text']]];
156
            if ($type !== 'wildcard') {
157
                $q = $this->getFieldBoosting($term, $type, $q);
158
            }
159
        }
160
161
        return $q;
162
    }
163
164
    /**
165
     * @param mixed $term
166
     * @param string $type
167
     * @param array $q
168
     * @return array
169
     */
170
    private function getFieldBoosting(mixed $term, string $type, array $q): array
171
    {
172
        $shoulds = [];
173
        $queryBoosts = $this->query->getBoostedFields();
174
        if ($term['boost'] > 1 && count($term['fields'])) {
175
            foreach ($term['fields'] as $field) {
176
                $shoulds[] = $this->addShould($type, $field, $term['text'], $term['boost']);
177
            }
178
        }
179
        foreach ($queryBoosts as $field => $boost) {
180
            $shoulds[] = $this->addShould($type, $field, $term['text'], $boost);
181
        }
182
        if (count($shoulds)) {
183
            $q['should'] = $shoulds;
184
        }
185
186
        return $q;
187
    }
188
189
    /**
190
     * @param string $type
191
     * @param string $field
192
     * @param $text
193
     * @param int $boost
194
     * @return array
195
     */
196
    private function addShould(string $type, string $field, $text, int $boost): array
197
    {
198
        $should = [
199
            $type => [
200
                $field => [
201
                    'query' => $text,
202
                    'boost' => $boost
203
                ]
204
            ]
205
        ];
206
207
        return $should;
208
    }
209
210
    private function getHighlighter(): array
211
    {
212
        if ($this->query->isHighlight()) {
213
            $highlights = [];
214
            foreach ($this->index->getFulltextFields() as $field) {
215
                $highlights[$field] = ['type' => 'unified'];
216
            }
217
            return ['fields' => $highlights]
218
                ;
219
        }
220
221
        return [];
222
    }
223
}
224