Completed
Push — master ( 30bc2b...fe4709 )
by Maxence
05:11
created

improveSearchWildcardQueries()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 13
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 13
loc 13
rs 9.4285
cc 3
eloc 7
nc 3
nop 2
1
<?php
2
/**
3
 * FullTextSearch_ElasticSearch - Use Elasticsearch to index the content of your nextcloud
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2018
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\FullTextSearch_ElasticSearch\Service;
28
29
use OCA\FullTextSearch\IFullTextSearchProvider;
30
use OCA\FullTextSearch\Model\DocumentAccess;
31
use OCA\FullTextSearch\Model\SearchRequest;
32
use OCA\FullTextSearch_ElasticSearch\Exceptions\ConfigurationException;
33
34
35
class SearchMappingService {
36
37
	/** @var ConfigService */
38
	private $configService;
39
40
	/** @var MiscService */
41
	private $miscService;
42
43
44
	/**
45
	 * MappingService constructor.
46
	 *
47
	 * @param ConfigService $configService
48
	 * @param MiscService $miscService
49
	 */
50
	public function __construct(ConfigService $configService, MiscService $miscService) {
51
		$this->configService = $configService;
52
		$this->miscService = $miscService;
53
	}
54
55
56
	/**
57
	 * @param IFullTextSearchProvider $provider
58
	 * @param DocumentAccess $access
59
	 * @param SearchRequest $request
60
	 *
61
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
62
	 * @throws ConfigurationException
63
	 */
64
	public function generateSearchQuery(
65
		IFullTextSearchProvider $provider, DocumentAccess $access, SearchRequest $request
66
	) {
67
		$query['params'] = $this->generateSearchQueryParams($provider, $access, $request);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$query was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
68
69
		return $query;
70
	}
71
72
73
	/**
74
	 * @param IFullTextSearchProvider $provider
75
	 * @param DocumentAccess $access
76
	 * @param SearchRequest $request
77
	 *
78
	 * @return array
79
	 * @throws ConfigurationException
80
	 */
81
	public function generateSearchQueryParams(
82
		IFullTextSearchProvider $provider, DocumentAccess $access, SearchRequest $request
83
	) {
84
		$str = strtolower($request->getSearch());
85
86
		$params = [
87
			'index' => $this->configService->getElasticIndex(),
88
			'type'  => 'standard',
89
			'size'  => $request->getSize(),
90
			'from'  => (($request->getPage() - 1) * $request->getSize())
91
		];
92
93
		$bool = [];
94
		$bool['must']['bool']['should'] = $this->generateSearchQueryContent($str);
95
96
		$bool['filter'][]['bool']['must'] = ['term' => ['provider' => $provider->getId()]];
97
		$bool['filter'][]['bool']['should'] = $this->generateSearchQueryAccess($access);
98
		$bool['filter'][]['bool']['should'] = $this->generateSearchQueryTags($request->getTags());
99
100
		$params['body']['query']['bool'] = $bool;
101
		$params['body']['highlight'] = $this->generateSearchHighlighting();
102
103
		$this->improveSearchQuerying($request, $params['body']['query']);
104
105
		return $params;
106
	}
107
108
109
	/**
110
	 * @param SearchRequest $request
111
	 * @param array $arr
112
	 */
113
	private function improveSearchQuerying(SearchRequest $request, &$arr) {
114
		$this->improveSearchWildcardQueries($request, $arr);
115
		$this->improveSearchWildcardFilters($request, $arr);
116
	}
117
118
119
	/**
120
	 * @param SearchRequest $request
121
	 * @param array $arr
122
	 */
123 View Code Duplication
	private function improveSearchWildcardQueries(SearchRequest $request, &$arr) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124
125
		$queries = $request->getWildcardQueries();
126
		foreach ($queries as $query) {
127
			$wildcards = [];
128
			foreach ($query as $entry) {
129
				$wildcards[] = ['wildcard' => $entry];
130
			}
131
132
			array_push($arr['bool']['must']['bool']['should'], $wildcards);
133
		}
134
135
	}
136
137
138
	/**
139
	 * @param SearchRequest $request
140
	 * @param array $arr
141
	 */
142 View Code Duplication
	private function improveSearchWildcardFilters(SearchRequest $request, &$arr) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
143
144
		$filters = $request->getWildcardFilters();
145
		foreach ($filters as $filter) {
146
			$wildcards = [];
147
			foreach ($filter as $entry) {
148
				$wildcards[] = ['wildcard' => $entry];
149
			}
150
151
			$arr['bool']['filter'][]['bool']['should'] = $wildcards;
152
		}
153
154
	}
155
156
157
	/**
158
	 * @param string $string
159
	 *
160
	 * @return array<string,array<string,array>>
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,array<string,array>>[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
161
	 */
162
	private function generateSearchQueryContent($string) {
163
		$queryTitle = $queryContent = [];
164
		$words = explode(' ', $string);
165
		foreach ($words as $word) {
166
167
			$kw = 'prefix';
168
			$this->modifySearchQueryContentOnCompleteWord($kw, $word);
169
170
			array_push($queryTitle, [$kw => ['title' => $word]]);
171
			array_push($queryContent, [$kw => ['content' => $word]]);
172
		}
173
174
		return [
175
			['bool' => ['must' => $queryTitle]],
176
			['bool' => ['must' => $queryContent]]
177
		];
178
	}
179
180
181
	/**
182
	 * @param string $kw
183
	 * @param string $word
184
	 */
185
	private function modifySearchQueryContentOnCompleteWord(&$kw, &$word) {
186
		if (substr($word, 0, 1) !== '"' || substr($word, -1) !== '"') {
187
			return;
188
		}
189
190
		$kw = 'match';
191
		$word = substr($word, 1, -1);
192
	}
193
194
195
	/**
196
	 * @param DocumentAccess $access
197
	 *
198
	 * @return array<string,array>
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,array>[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
199
	 */
200
	private function generateSearchQueryAccess(DocumentAccess $access) {
201
202
		$query = [];
203
		$query[] = ['term' => ['owner' => $access->getViewerId()]];
204
		$query[] = ['term' => ['users' => $access->getViewerId()]];
205
		$query[] = ['term' => ['users' => '__all']];
206
207
		foreach ($access->getGroups() as $group) {
208
			$query[] = ['term' => ['groups' => $group]];
209
		}
210
211
		foreach ($access->getCircles() as $circle) {
212
			$query[] = ['term' => ['circles' => $circle]];
213
		}
214
215
		return $query;
216
	}
217
218
219
	/**
220
	 * @param array $tags
221
	 *
222
	 * @return array<string,array>
223
	 */
224
	private function generateSearchQueryTags($tags) {
225
226
		$query = [];
227
		foreach ($tags as $tag) {
228
			$query[] = ['term' => ['tags' => $tag]];
229
		}
230
231
		return $query;
232
	}
233
234
	private function generateSearchHighlighting() {
235
		return [
236
			'fields'    => ['content' => new \stdClass()],
237
			'pre_tags'  => [''],
238
			'post_tags' => ['']
239
		];
240
	}
241
242
243
}
244