Passed
Pull Request — main (#43)
by Simon
01:07
created

QueryBuilder::getFilters()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
c 1
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 self
27
     */
28
    protected static function init(ElasticQuery $query, ElasticIndex $index): self
29
    {
30
        $self = new self();
31
        $self->setIndex($index);
32
        $self->setQuery($query);
33
34
        return $self;
35
    }
36
37
    /**
38
     * @param ElasticQuery $query
39
     * @param ElasticIndex $index
40
     * @return array
41
     */
42
    public static function buildQuery(BaseQuery $query, CoreIndex $index): array
43
    {
44
        $self = self::init($query, $index);
45
        $filters = $self->getFilters($index, $query);
46
        $orFilters = $self->getOrFilters($query);
47
        // Always primarily search against the _text field, that's where all content is
48
        $terms = $self->getUserQuery($query); // There's always a term
49
        if (count($filters)) {
50
            $filters = ['filter' => ['bool' => ['must' => $filters]]];
51
            $terms = array_merge($terms, $filters);
52
        }
53
        if (count($orFilters)) {
54
            $terms['filter']['bool']['should'] = $orFilters;
55
        }
56
57
        return [
58
            'index' => $index->getIndexName(),
59
            'from'  => $query->getStart(),
60
            'size'  => $query->getRows(),
61
            'body'  => [
62
                'query' => [
63
                    'bool' => $terms,
64
                ],
65
            ]
66
        ];
67
    }
68
69
    /**
70
     * Required must-be filters if they're here.
71
     * @param CoreIndex|ElasticIndex $index
72
     * @param ElasticQuery|BaseQuery $query
73
     * @return array[]
74
     */
75
    private function getFilters(CoreIndex|ElasticIndex $index, ElasticQuery|BaseQuery $query): array
76
    {
77
        // Default,
78
        $filters = [
79
            [
80
                'terms' => [
81
                    'ViewStatus' => $index->getViewStatusFilter(),
82
                ]
83
            ]
84
        ];
85
        if (count($query->getFilters())) {
86
            foreach ($query->getFilters() as $key => $value) {
87
                $value = is_array($value) ?: [$value];
88
                $filters[] = ['terms' => [$key => $value]];
89
            }
90
        }
91
92
        return $filters;
93
    }
94
95
    /**
96
     * Create the "should" filter, that is OR instead of AND
97
     * @param BaseQuery $query
98
     * @return array
99
     */
100
    private function getOrFilters(BaseQuery $query)
101
    {
102
        $filters = [];
103
        if (count($query->getOrFilters())) {
104
            foreach ($query->getOrFilters() as $key => $value) {
105
                $value = is_array($value) ?: [$value];
106
                $filters[] = ['terms' => [$key => $value]];
107
            }
108
        }
109
110
        return $filters;
111
    }
112
113
    /**
114
     * this allows for multiple search terms to be entered
115
     * @param ElasticQuery|BaseQuery $query
116
     * @return array
117
     */
118
    private function getUserQuery(ElasticQuery|BaseQuery $query): array
119
    {
120
        $q = [];
121
        $terms = $query->getTerms();
122
        $type = 'match';
123
        if (!count($terms)) {
124
            $type = 'wildcard';
125
            $terms = ['*'];
126
        }
127
        foreach ($terms as $term) {
128
            $q['must'][] = ['match' => ['_text' => $term['text']]];
129
            $q = $this->getFieldBoosting($term, $type, $q);
130
        }
131
132
        return $q;
133
    }
134
135
    /**
136
     * @param mixed $term
137
     * @param string $type
138
     * @param array $q
139
     * @return array
140
     */
141
    private function getFieldBoosting(mixed $term, string $type, array $q): array
142
    {
143
        $shoulds = [];
144
        $queryBoosts = $this->query->getBoostedFields();
145
        if ($term['boost'] > 1 && count($term['fields'])) {
146
            foreach ($term['fields'] as $field) {
147
                $shoulds[] = $this->addShould($type, $field, $term['text'], $term['boost']);
148
            }
149
        }
150
        foreach ($queryBoosts as $field => $boost) {
151
            $shoulds[] = $this->addShould($type, $field, $term['text'], $boost);
152
        }
153
        if (count($shoulds)) {
154
            $q['should'] = $shoulds;
155
        }
156
157
        return $q;
158
    }
159
160
    /**
161
     * @return mixed
162
     */
163
    public function getQuery()
164
    {
165
        return $this->query;
166
    }
167
168
    /**
169
     * @param mixed $query
170
     */
171
    public function setQuery($query): void
172
    {
173
        $this->query = $query;
174
    }
175
176
    /**
177
     * @return mixed
178
     */
179
    public function getIndex()
180
    {
181
        return $this->index;
182
    }
183
184
    /**
185
     * @param mixed $index
186
     */
187
    public function setIndex($index): void
188
    {
189
        $this->index = $index;
190
    }
191
192
    /**
193
     * @param string $type
194
     * @param string $field
195
     * @param $text
196
     * @param int $boost
197
     * @return array
198
     */
199
    private function addShould(string $type, string $field, $text, int $boost): array
200
    {
201
        $should = [
202
            $type => [
203
                $field => [
204
                    'query' => $text,
205
                    'boost' => $boost
206
                ]
207
            ]
208
        ];
209
210
        return $should;
211
    }
212
}
213