Completed
Push — ezp-30639-deprecated-view-acti... ( 6b3a78...0a459a )
by
unknown
16:22
created

providerForTestFulltextSearchSolr6()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 73

Duplication

Lines 73
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 73
loc 73
rs 8.589
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\API\Repository\Tests;
8
9
use eZ\Publish\API\Repository\SearchService;
10
use eZ\Publish\API\Repository\Values\Content\Content;
11
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
12
use eZ\Publish\API\Repository\Values\Content\Location;
13
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
14
use eZ\Publish\API\Repository\Values\Content\Query;
15
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
16
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
17
use RuntimeException;
18
19
/**
20
 * Test case for full text search in the SearchService.
21
 *
22
 * @see \eZ\Publish\API\Repository\SearchService
23
 * @group integration
24
 * @group search
25
 * @group fulltext
26
 */
27
class SearchServiceFulltextTest extends BaseTest
28
{
29
    protected function setUp(): void
30
    {
31
        parent::setUp();
32
33
        if (
34
            !$this
35
                ->getRepository(false)
36
                ->getSearchService()->supports(SearchService::CAPABILITY_ADVANCED_FULLTEXT)
37
        ) {
38
            $this->markTestSkipped('Engine says it does not support advance fulltext format');
39
        }
40
    }
41
42
    /**
43
     * Create test Content and return Content ID map for subsequent testing.
44
     */
45
    public function testPrepareContent()
46
    {
47
        $repository = $this->getRepository();
48
        $dataMap = [
49
            1 => 'quick',
50
            2 => 'brown',
51
            3 => 'fox',
52
            4 => 'news',
53
            5 => 'quick brown',
54
            6 => 'quick fox',
55
            7 => 'quick news',
56
            8 => 'brown fox',
57
            9 => 'brown news',
58
            10 => 'fox news',
59
            11 => 'quick brown fox',
60
            12 => 'quick brown news',
61
            13 => 'quick fox news',
62
            14 => 'brown fox news',
63
            15 => 'quick brown fox news',
64
        ];
65
66
        $contentService = $repository->getContentService();
67
        $contentTypeService = $repository->getContentTypeService();
68
        $locationService = $repository->getLocationService();
69
70
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
71
72
        $idMap = [];
73
74
        foreach ($dataMap as $key => $string) {
75
            $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
76
            $contentCreateStruct->setField('name', $string);
77
78
            $content = $contentService->publishVersion(
79
                $contentService->createContent(
80
                    $contentCreateStruct,
81
                    [$locationService->newLocationCreateStruct(2)]
82
                )->versionInfo
83
            );
84
85
            $idMap[$key] = $content->id;
86
        }
87
88
        $this->refreshSearch($repository);
89
90
        return $idMap;
91
    }
92
93
    /**
94
     * Return pairs of arguments:
95
     *  - search string for testing
96
     *  - an array of corresponding Content keys as defined in testPrepareContent() method,
97
     *    ordered and grouped by relevancy.
98
     *
99
     * @see testPrepareContent
100
     */
101
    public function providerForTestFulltextSearchSolr7(): array
102
    {
103
        return [
104
            [
105
                'fox',
106
                [3, [6, 8, 10], [11, 13, 14], 15],
107
            ],
108
            [
109
                'quick fox',
110
                $quickOrFox = [6, [11, 13], 15, [1, 3], [5, 7, 8, 10], [12, 14]],
111
            ],
112
            [
113
                'quick OR fox',
114
                $quickOrFox,
115
            ],
116
            [
117
                'quick AND () OR AND fox',
118
                $quickOrFox,
119
            ],
120
            [
121
                '+quick +fox',
122
                $quickAndFox = [6, [11, 13], 15],
123
            ],
124
            [
125
                'quick AND fox',
126
                $quickAndFox,
127
            ],
128
            [
129
                'brown +fox -news',
130
                [8, 11, 3, 6],
131
            ],
132
            [
133
                'quick +fox -news',
134
                [6, 11, 3, 8],
135
            ],
136
            [
137
                'quick brown +fox -news',
138
                $notNewsFox = [11, [6, 8], 3],
139
            ],
140
            [
141
                '((quick AND fox) OR (brown AND fox) OR fox) AND NOT news',
142
                $notNewsFox,
143
            ],
144
            [
145
                '"quick brown"',
146
                [5, [11, 12], 15],
147
            ],
148
            [
149
                '"quick brown" AND fox',
150
                [11, 15],
151
            ],
152
            [
153
                'quick OR brown AND fox AND NOT news',
154
                [11, 8],
155
            ],
156
            [
157
                '(quick OR brown) AND fox AND NOT news',
158
                [11, [6, 8]],
159
            ],
160
            [
161
                '"fox brown"',
162
                [],
163
            ],
164
            [
165
                'qui*',
166
                [[1, 5, 6, 7, 11, 12, 13, 15]],
167
            ],
168
            [
169
                '+qui* +fox',
170
                [6, [11, 13], 15],
171
            ],
172
        ];
173
    }
174
175
    /**
176
     * Test for the findContent() method on Solr >= 7.
177
     *
178
     * @param string $searchString
179
     * @param array $expectedKeys
180
     * @param array $idMap
181
     *
182
     * @depends testPrepareContent
183
     * @dataProvider providerForTestFulltextSearchSolr7
184
     */
185 View Code Duplication
    public function testFulltextContentSearchSolr7(string $searchString, array $expectedKeys, array $idMap): void
186
    {
187
        if (false === $this->isSolrMajorVersionInRange('7.0.0', '8.0.0')) {
188
            $this->markTestSkipped('This test is only relevant for Solr >= 7');
189
        }
190
191
        $this->doTestFulltextContentSearch($searchString, $expectedKeys, $idMap);
192
    }
193
194
    private function doTestFulltextContentSearch(string $searchString, array $expectedKeys, array $idMap): void
195
    {
196
        $repository = $this->getRepository(false);
197
        $searchService = $repository->getSearchService();
198
199
        $query = new Query(['query' => new Criterion\FullText($searchString)]);
200
        $searchResult = $searchService->findContent($query);
201
202
        $this->assertFulltextSearch($searchResult, $expectedKeys, $idMap);
203
    }
204
205
    /**
206
     * Test for the findLocations() method on Solr >= 7.
207
     *
208
     * @param $searchString
209
     * @param array $expectedKeys
210
     * @param array $idMap
211
     *
212
     * @depends testPrepareContent
213
     * @dataProvider providerForTestFulltextSearchSolr7
214
     */
215 View Code Duplication
    public function testFulltextLocationSearchSolr7($searchString, array $expectedKeys, array $idMap): void
216
    {
217
        if (false === $this->isSolrMajorVersionInRange('7.0.0', '8.0.0')) {
218
            $this->markTestSkipped('This test is only relevant for Solr >= 7');
219
        }
220
221
        $this->doTestFulltextLocationSearch($searchString, $expectedKeys, $idMap);
222
    }
223
224
    private function doTestFulltextLocationSearch($searchString, array $expectedKeys, array $idMap): void
225
    {
226
        $repository = $this->getRepository(false);
227
        $searchService = $repository->getSearchService();
228
229
        $query = new LocationQuery(['query' => new Criterion\FullText($searchString)]);
230
        $searchResult = $searchService->findLocations($query);
231
232
        $this->assertFulltextSearch($searchResult, $expectedKeys, $idMap);
233
    }
234
235
    /**
236
     * Assert given $searchResult using $expectedKeys and $idMap.
237
     *
238
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult
239
     * @param array $expectedKeys
240
     * @param array $idMap
241
     */
242
    public function assertFulltextSearch(SearchResult $searchResult, array $expectedKeys, array $idMap)
243
    {
244
        $this->assertEquals(
245
            array_reduce(
246
                $expectedKeys,
247
                function ($carry, $item) {
248
                    $carry += count((array)$item);
249
250
                    return $carry;
251
                },
252
                0
253
            ),
254
            $searchResult->totalCount
255
        );
256
257
        $expectedIds = $this->mapKeysToIds($expectedKeys, $idMap);
258
        $actualIds = $this->mapSearchResultToIds($searchResult);
259
260
        $this->assertEquals($expectedIds, $actualIds);
261
    }
262
263
    /**
264
     * Map given array of $expectedKeys to Content IDs, using $idMap.
265
     *
266
     * @param array $expectedKeys
267
     * @param array $idMap
268
     *
269
     * @return array
270
     */
271
    private function mapKeysToIds(array $expectedKeys, array $idMap)
272
    {
273
        $expectedIds = [];
274
275
        foreach ($expectedKeys as $keyGroup) {
276
            if (is_array($keyGroup)) {
277
                $idGroup = [];
278
279
                /** @var array $keyGroup */
280
                foreach ($keyGroup as $key) {
281
                    $idGroup[] = $idMap[$key];
282
                }
283
284
                sort($idGroup);
285
                $expectedIds[] = $idGroup;
286
287
                continue;
288
            }
289
290
            $key = $keyGroup;
291
            $expectedIds[] = $idMap[$key];
292
        }
293
294
        return $expectedIds;
295
    }
296
297
    /**
298
     * Map given $searchResult to an array of Content IDs, ordered and grouped by relevancy score.
299
     *
300
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult
301
     *
302
     * @return array
303
     */
304
    private function mapSearchResultToIds(SearchResult $searchResult)
305
    {
306
        $scoreGroupedIds = [];
307
308
        foreach ($searchResult->searchHits as $index => $searchHit) {
309
            if ($searchHit->valueObject instanceof Content || $searchHit->valueObject instanceof Location) {
310
                $contentInfo = $searchHit->valueObject->contentInfo;
311
            } elseif ($searchHit->valueObject instanceof ContentInfo) {
312
                $contentInfo = $searchHit->valueObject;
313
            } else {
314
                throw new RuntimeException('Unknown search hit value');
315
            }
316
317
            $scoreGroupedIds[(string)$searchHit->score][] = $contentInfo->id;
318
        }
319
320
        return array_map(
321
            function (array $idGroup) {
322
                if (count($idGroup) === 1) {
323
                    return reset($idGroup);
324
                }
325
326
                sort($idGroup);
327
328
                return $idGroup;
329
            },
330
            array_values($scoreGroupedIds)
331
        );
332
    }
333
334
    /**
335
     * Checks if Solr version is in the given range.
336
     *
337
     * @param string $minVersion
338
     * @param string $maxVersion
339
     *
340
     * @return bool
341
     */
342
    private function isSolrMajorVersionInRange(string $minVersion, string $maxVersion): bool
343
    {
344
        $version = getenv('SOLR_VERSION');
345
        if (is_string($version) && !empty($version)) {
346
            return version_compare($version, $minVersion, '>=') && version_compare($version, $maxVersion, '<');
347
        }
348
349
        return false;
350
    }
351
}
352