Passed
Push — main ( 13ac57...e30bbc )
by Simon
01:10
created

QueryBuilder::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 7
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
            if ($type !== 'wildcard') {
130
                $q = $this->getFieldBoosting($term, $type, $q);
131
            }
132
        }
133
134
        return $q;
135
    }
136
137
    /**
138
     * @param mixed $term
139
     * @param string $type
140
     * @param array $q
141
     * @return array
142
     */
143
    private function getFieldBoosting(mixed $term, string $type, array $q): array
144
    {
145
        $shoulds = [];
146
        $queryBoosts = $this->query->getBoostedFields();
147
        if ($term['boost'] > 1 && count($term['fields'])) {
148
            foreach ($term['fields'] as $field) {
149
                $shoulds[] = $this->addShould($type, $field, $term['text'], $term['boost']);
150
            }
151
        }
152
        foreach ($queryBoosts as $field => $boost) {
153
            $shoulds[] = $this->addShould($type, $field, $term['text'], $boost);
154
        }
155
        if (count($shoulds)) {
156
            $q['should'] = $shoulds;
157
        }
158
159
        return $q;
160
    }
161
162
    /**
163
     * @param mixed $query
164
     */
165
    public function setQuery($query): void
166
    {
167
        $this->query = $query;
168
    }
169
170
    /**
171
     * @param mixed $index
172
     */
173
    public function setIndex($index): void
174
    {
175
        $this->index = $index;
176
    }
177
178
    /**
179
     * @param string $type
180
     * @param string $field
181
     * @param $text
182
     * @param int $boost
183
     * @return array
184
     */
185
    private function addShould(string $type, string $field, $text, int $boost): array
186
    {
187
        $should = [
188
            $type => [
189
                $field => [
190
                    'query' => $text,
191
                    'boost' => $boost
192
                ]
193
            ]
194
        ];
195
196
        return $should;
197
    }
198
}
199