QueryBuilder::addQuery()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 7
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2020-2025 Iain Cambridge
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
10
 * the Free Software Foundation, either version 2.1 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20
 */
21
22
namespace Parthenon\Common\Elasticsearch;
23
24
final class QueryBuilder
25
{
26
    private array $filters = [];
27
28
    private array $queries = [];
29
30
    private array $aggregations = [];
31
32
    public function query(string $queryType, string $fieldName, mixed $fieldValue): self
33
    {
34
        $this->addQuery($queryType, 'must', $fieldName, $fieldValue);
35
36
        return $this;
37
    }
38
39
    public function andQuery(string $queryType, string $fieldName, mixed $fieldValue): self
40
    {
41
        $this->addQuery($queryType, 'must', $fieldName, $fieldValue);
42
43
        return $this;
44
    }
45
46
    public function notQuery(string $queryType, string $fieldName, mixed $fieldValue): self
47
    {
48
        $this->addQuery($queryType, 'must_not', $fieldName, $fieldValue);
49
50
        return $this;
51
    }
52
53
    public function orQuery(string $queryType, string $fieldName, mixed $fieldValue): self
54
    {
55
        $this->addQuery($queryType, 'should', $fieldName, $fieldValue);
56
57
        return $this;
58
    }
59
60
    public function filter(string $filterType, string $fieldName, mixed $fieldValue): self
61
    {
62
        $this->addFilter($filterType, 'must', $fieldName, $fieldValue);
63
64
        return $this;
65
    }
66
67
    public function andFilter(string $filterType, string $fieldName, mixed $fieldValue): self
68
    {
69
        $this->addFilter($filterType, 'must', $fieldName, $fieldValue);
70
71
        return $this;
72
    }
73
74
    public function notFilter(string $filterType, string $fieldName, mixed $fieldValue): self
75
    {
76
        $this->addFilter($filterType, 'must_not', $fieldName, $fieldValue);
77
78
        return $this;
79
    }
80
81
    public function orFilter(string $filterType, string $fieldName, mixed $fieldValue): self
82
    {
83
        $this->addFilter($filterType, 'should', $fieldName, $fieldValue);
84
85
        return $this;
86
    }
87
88
    public function aggregation(string $term, string $fieldName): self
89
    {
90
        $this->aggregations[$term] = $fieldName;
91
92
        return $this;
93
    }
94
95
    public function build(): array
96
    {
97
        $output = [];
98
99
        if (1 === sizeof($this->queries) && 0 === sizeof($this->filters) && 0 === sizeof($this->aggregations)) {
100
            $query = current($this->queries);
101
102
            return $this->addAggregation([
103
                'query' => [
104
                    $query['queryType'] => [
105
                        $query['fieldName'] => $query['fieldValue'],
106
                    ],
107
                ],
108
            ]);
109
        }
110
111
        if (1 === sizeof($this->filters)) {
112
            $filters = current($this->filters);
113
            $output['bool'] = [
114
                'filter' => [
115
                    $filters['filterType'] => [
116
                        $filters['fieldName'] => $filters['fieldValue'],
117
                    ],
118
                ],
119
            ];
120
121
            return $this->addAggregation(['query' => $output]);
122
        }
123
124
        if (sizeof($this->filters) > 1 && sizeof($this->queries) > 1) {
125
            $output['bool'] = [
126
            ];
127
        }
128
129
        if (sizeof($this->queries) > 1) {
130
            $output['bool']['must'] = [];
131
            foreach ($this->queries as $query) {
132
                $qualifierType = $query['qualifierType'];
133
                if (!isset($output['bool'][$qualifierType])) {
134
                    $output['bool'][$qualifierType] = [];
135
                }
136
                $output['bool'][$qualifierType][] = [
137
                    $query['queryType'] => [
138
                        $query['fieldName'] => $query['fieldValue'],
139
                    ],
140
                ];
141
            }
142
        }
143
144
        if (sizeof($this->filters) > 1) {
145
            $output['bool']['filter']['bool'] = [];
146
            foreach ($this->filters as $filter) {
147
                $qualifierType = $filter['qualifierType'];
148
                if (!isset($output['bool']['filter']['bool'][$qualifierType])) {
149
                    $output['bool']['filter']['bool'][$qualifierType] = [];
150
                }
151
                $output['bool']['filter']['bool'][$qualifierType][] = [
152
                    $filter['filterType'] => [
153
                        $filter['fieldName'] => $filter['fieldValue'],
154
                    ],
155
                ];
156
            }
157
        }
158
159
        return $this->addAggregation(['query' => $output]);
160
    }
161
162
    private function addAggregation($output)
163
    {
164
        if (0 === sizeof($this->aggregations)) {
165
            return $output;
166
        }
167
168
        $output['aggs'] = [];
169
170
        foreach ($this->aggregations as $term => $fieldName) {
171
            $key = sprintf('agg_%s_%s', $term, $fieldName);
172
            $output['aggs'][$key] = [$term => ['field' => $fieldName]];
173
        }
174
175
        return $output;
176
    }
177
178
    private function addFilter(string $filterType, string $qualifierType, string $fieldName, mixed $fieldValue): void
179
    {
180
        $this->filters[] = [
181
            'filterType' => $filterType,
182
            'qualifierType' => $qualifierType,
183
            'fieldName' => $fieldName,
184
            'fieldValue' => $fieldValue,
185
        ];
186
    }
187
188
    private function addQuery(string $queryType, string $qualifierType, string $fieldName, mixed $fieldValue): void
189
    {
190
        $this->queries[] = [
191
            'queryType' => $queryType,
192
            'qualifierType' => $qualifierType,
193
            'fieldName' => $fieldName,
194
            'fieldValue' => $fieldValue,
195
        ];
196
    }
197
}
198