Completed
Push — master ( 2566be...2cb116 )
by Maxence
01:40 queued 10s
created

SearchService   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 4
dl 0
loc 202
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A searchRequest() 0 34 4
A getDocument() 0 29 1
B getDocumentInfos() 0 25 6
A updateSearchResult() 0 13 2
A parseSearchEntry() 0 20 2
A parseSearchEntryExcerpts() 0 14 3
1
<?php
2
declare(strict_types=1);
3
4
5
/**
6
 * FullTextSearch_Elasticsearch - Use Elasticsearch to index the content of your nextcloud
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later. See the COPYING file.
10
 *
11
 * @author Maxence Lange <[email protected]>
12
 * @copyright 2018
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
31
namespace OCA\FullTextSearch_Elasticsearch\Service;
32
33
34
use daita\MySmallPhpTools\Traits\TArrayTools;
35
use Elasticsearch\Client;
36
use Exception;
37
use OC\FullTextSearch\Model\DocumentAccess;
38
use OC\FullTextSearch\Model\IndexDocument;
39
use OCA\FullTextSearch_Elasticsearch\Exceptions\ConfigurationException;
40
use OCA\FullTextSearch_Elasticsearch\Exceptions\SearchQueryGenerationException;
41
use OCP\FullTextSearch\Model\IDocumentAccess;
42
use OCP\FullTextSearch\Model\IIndexDocument;
43
use OCP\FullTextSearch\Model\ISearchResult;
44
45
46
/**
47
 * Class SearchService
48
 *
49
 * @package OCA\FullTextSearch_Elasticsearch\Service
50
 */
51
class SearchService {
52
53
54
	use TArrayTools;
55
56
57
	/** @var SearchMappingService */
58
	private $searchMappingService;
59
60
	/** @var MiscService */
61
	private $miscService;
62
63
64
	/**
65
	 * SearchService constructor.
66
	 *
67
	 * @param SearchMappingService $searchMappingService
68
	 * @param MiscService $miscService
69
	 */
70
	public function __construct(
71
		SearchMappingService $searchMappingService, MiscService $miscService
72
	) {
73
		$this->searchMappingService = $searchMappingService;
74
		$this->miscService = $miscService;
75
	}
76
77
	/**
78
	 * @param Client $client
79
	 * @param ISearchResult $searchResult
80
	 * @param IDocumentAccess $access
81
	 *
82
	 * @throws Exception
83
	 */
84
	public function searchRequest(
85
		Client $client, ISearchResult $searchResult, IDocumentAccess $access
86
	) {
87
		try {
88
			$this->miscService->log('New Search Request; SearchResult Model: ' . json_encode($searchResult), 0);
89
			$query = $this->searchMappingService->generateSearchQuery(
90
				$searchResult->getRequest(), $access, $searchResult->getProvider()
91
																   ->getId()
92
			);
93
		} catch (SearchQueryGenerationException $e) {
94
			return;
95
		}
96
97
		try {
98
			$this->miscService->log('Searching ES: ' . json_encode($query['params']), 0);
99
100
			$result = $client->search($query['params']);
101
		} catch (Exception $e) {
102
			$this->miscService->log(
103
				'debug - request: ' . json_encode($searchResult->getRequest()) . '   - query: '
104
				. json_encode($query)
105
			);
106
			throw $e;
107
		}
108
109
		$this->miscService->log('Result from ES: ' . json_encode($result), 0);
110
		$this->updateSearchResult($searchResult, $result);
0 ignored issues
show
Documentation introduced by
$result is of type callable, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
111
112
		foreach ($result['hits']['hits'] as $entry) {
113
			$searchResult->addDocument($this->parseSearchEntry($entry, $access->getViewerId()));
114
		}
115
116
		$this->miscService->log('Filled SearchResult Model: ' . json_encode($searchResult), 0);
117
	}
118
119
120
	/**
121
	 * @param Client $client
122
	 * @param string $providerId
123
	 * @param string $documentId
124
	 *
125
	 * @return IIndexDocument
126
	 * @throws ConfigurationException
127
	 */
128
	public function getDocument(Client $client, string $providerId, string $documentId
129
	): IIndexDocument {
130
		$query = $this->searchMappingService->getDocumentQuery($providerId, $documentId);
131
		$result = $client->get($query);
132
133
		$access = new DocumentAccess($result['_source']['owner']);
134
		$access->setUsers($result['_source']['users']);
135
		$access->setGroups($result['_source']['groups']);
136
		$access->setCircles($result['_source']['circles']);
137
		$access->setLinks($result['_source']['links']);
138
139
		$index = new IndexDocument($providerId, $documentId);
140
		$index->setAccess($access);
141
		$index->setMetaTags($result['_source']['metatags']);
142
		$index->setSubTags($result['_source']['subtags']);
143
		$index->setTags($result['_source']['tags']);
144
//		$index->setMore($result['_source']['more']);
145
		$index->setHash($result['_source']['hash']);
146
		$index->setSource($result['_source']['source']);
147
		$index->setTitle($result['_source']['title']);
148
		$index->setParts($result['_source']['parts']);
149
150
		$this->getDocumentInfos($index, $result['_source']);
151
152
		$content = $this->get('content', $result['_source'], '');
153
		$index->setContent($content);
154
155
		return $index;
156
	}
157
158
159
	/**
160
	 * @param IndexDocument $index
161
	 * @param $source
162
	 */
163
	private function getDocumentInfos(IndexDocument $index, $source) {
164
		$ak = array_keys($source);
165
		foreach ($ak as $k) {
166
			if (substr($k, 0, 5) !== 'info_') {
167
				continue;
168
			}
169
			$value = $source[$k];
170
			if (is_array($value)) {
171
				$index->setInfoArray($k, $value);
172
				continue;
173
			}
174
175
			if (is_bool($value)) {
176
				$index->setInfoBool($k, $value);
177
				continue;
178
			}
179
180
			if (is_numeric($value)) {
181
				$index->setInfoInt($k, (int)$value);
182
				continue;
183
			}
184
185
			$index->setInfo($k, (string)$value);
186
		}
187
	}
188
189
190
	/**
191
	 * @param ISearchResult $searchResult
192
	 * @param array $result
193
	 */
194
	private function updateSearchResult(ISearchResult $searchResult, array $result) {
195
		$searchResult->setRawResult(json_encode($result));
196
197
		$total = $result['hits']['total'];
198
		if (is_array($total)) {
199
			$total = $total['value'];
200
		}
201
202
		$searchResult->setTotal($total);
203
		$searchResult->setMaxScore($this->getInt('max_score', $result['hits'], 0));
204
		$searchResult->setTime($result['took']);
205
		$searchResult->setTimedOut($result['timed_out']);
206
	}
207
208
209
	/**
210
	 * @param array $entry
211
	 * @param string $viewerId
212
	 *
213
	 * @return IIndexDocument
214
	 */
215
	private function parseSearchEntry(array $entry, string $viewerId): IIndexDocument {
216
		$access = new DocumentAccess();
217
		$access->setViewerId($viewerId);
218
219
		list($providerId, $documentId) = explode(':', $entry['_id'], 2);
220
		$document = new IndexDocument($providerId, $documentId);
221
		$document->setAccess($access);
222
		$document->setHash($this->get('hash', $entry['_source']));
223
		$document->setScore($this->get('_score', $entry, '0'));
224
		$document->setSource($this->get('source', $entry['_source']));
225
		$document->setTitle($this->get('title', $entry['_source']));
226
227
		$document->setExcerpts(
228
			$this->parseSearchEntryExcerpts(
229
				(array_key_exists('highlight', $entry)) ? $entry['highlight'] : []
230
			)
231
		);
232
233
		return $document;
234
	}
235
236
237
	private function parseSearchEntryExcerpts(array $highlights): array {
238
		$result = [];
239
		foreach (array_keys($highlights) as $source) {
240
			foreach ($highlights[$source] as $highlight) {
241
				$result[] =
242
					[
243
						'source'  => $source,
244
						'excerpt' => $highlight
245
					];
246
			}
247
		}
248
249
		return $result;
250
	}
251
252
}
253
254