Completed
Push — master ( e0c8fe...ceaa4d )
by André
110:02 queued 96:02
created

SearchServiceFulltextTest::mapSearchResultToIds()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 17
nc 4
nop 1
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
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\Values\Content\Content;
10
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
11
use eZ\Publish\API\Repository\Values\Content\Location;
12
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
13
use eZ\Publish\API\Repository\Values\Content\Query;
14
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
15
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
16
use RuntimeException;
17
18
/**
19
 * Test case for full text search in the SearchService.
20
 *
21
 * @see \eZ\Publish\API\Repository\SearchService
22
 * @group integration
23
 * @group search
24
 * @group fulltext
25
 */
26
class SearchServiceFulltextTest extends BaseTest
27
{
28
    /**
29
     * Create test Content and return Content ID map for subsequent testing.
30
     */
31
    public function testPrepareContent()
32
    {
33
        $repository = $this->getRepository();
34
        $dataMap = [
35
            1 => 'quick',
36
            2 => 'brown',
37
            3 => 'fox',
38
            4 => 'news',
39
            5 => 'quick brown',
40
            6 => 'quick fox',
41
            7 => 'quick news',
42
            8 => 'brown fox',
43
            9 => 'brown news',
44
            10 => 'fox news',
45
            11 => 'quick brown fox',
46
            12 => 'quick brown news',
47
            13 => 'quick fox news',
48
            14 => 'brown fox news',
49
            15 => 'quick brown fox news',
50
        ];
51
52
        $contentService = $repository->getContentService();
53
        $contentTypeService = $repository->getContentTypeService();
54
        $locationService = $repository->getLocationService();
55
56
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
57
58
        $idMap = [];
59
60
        foreach ($dataMap as $key => $string) {
61
            $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
62
            $contentCreateStruct->setField('name', $string);
63
64
            $content = $contentService->publishVersion(
65
                $contentService->createContent(
66
                    $contentCreateStruct,
67
                    [$locationService->newLocationCreateStruct(2)]
68
                )->versionInfo
69
            );
70
71
            $idMap[$key] = $content->id;
72
        }
73
74
        $this->refreshSearch($repository);
75
76
        return $idMap;
77
    }
78
79
    /**
80
     * Return pairs of arguments:
81
     *  - search string for testing
82
     *  - an array of corresponding Content keys as defined in testPrepareContent() method,
83
     *    ordered and grouped by relevancy.
84
     *
85
     * @see testPrepareContent
86
     */
87
    public function providerForTestFulltextSearch()
88
    {
89
        return [
90
            [
91
                'fox',
92
                [3, [6, 8, 10], [11, 13, 14, 15]],
93
            ],
94
            [
95
                'quick fox',
96
                $quickOrFox = [6, [11, 13, 15], [1, 3], [5, 7, 8, 10], [12, 14]],
97
            ],
98
            [
99
                'quick OR fox',
100
                $quickOrFox,
101
            ],
102
            [
103
                'quick AND () OR AND fox',
104
                $quickOrFox,
105
            ],
106
            [
107
                '+quick +fox',
108
                $quickAndFox = [6, [11, 13, 15]],
109
            ],
110
            [
111
                'quick AND fox',
112
                $quickAndFox,
113
            ],
114
            [
115
                'brown +fox -news',
116
                [8, 11, 3, 6],
117
            ],
118
            [
119
                'quick +fox -news',
120
                [6, 11, 3, 8],
121
            ],
122
            [
123
                'quick brown +fox -news',
124
                $notNewsFox = [11, [6, 8], 3],
125
            ],
126
            [
127
                '((quick AND fox) OR (brown AND fox) OR fox) AND NOT news',
128
                $notNewsFox,
129
            ],
130
            [
131
                '"quick brown"',
132
                [5, [11, 12, 15]],
133
            ],
134
            [
135
                '"quick brown" AND fox',
136
                [[11, 15]],
137
            ],
138
            [
139
                'quick OR brown AND fox AND NOT news',
140
                [11, 8],
141
            ],
142
            [
143
                '(quick OR brown) AND fox AND NOT news',
144
                [11, [6, 8]],
145
            ],
146
            [
147
                '"fox brown"',
148
                [],
149
            ],
150
            [
151
                'qui*',
152
                [[1, 5, 6, 7, 11, 12, 13, 15]],
153
            ],
154
            [
155
                '+qui* +fox',
156
                [6, [11, 13, 15]],
157
            ],
158
        ];
159
    }
160
161
    /**
162
     * Test for the findContent() method.
163
     *
164
     * @param $searchString
165
     * @param array $expectedKeys
166
     * @param array $idMap
167
     *
168
     * @depends testPrepareContent
169
     * @dataProvider providerForTestFulltextSearch
170
     */
171 View Code Duplication
    public function testFulltextContentSearch($searchString, array $expectedKeys, array $idMap)
172
    {
173
        $repository = $this->getRepository(false);
174
        $searchService = $repository->getSearchService();
175
176
        $query = new Query(['query' => new Criterion\FullText($searchString)]);
177
        $searchResult = $searchService->findContent($query);
178
179
        $this->assertFulltextSearch($searchResult, $expectedKeys, $idMap);
180
    }
181
182
    /**
183
     * Test for the findLocations() method.
184
     *
185
     * @param $searchString
186
     * @param array $expectedKeys
187
     * @param array $idMap
188
     *
189
     * @depends testPrepareContent
190
     * @dataProvider providerForTestFulltextSearch
191
     */
192 View Code Duplication
    public function testFulltextLocationSearch($searchString, array $expectedKeys, array $idMap)
193
    {
194
        $repository = $this->getRepository(false);
195
        $searchService = $repository->getSearchService();
196
197
        $query = new LocationQuery(['query' => new Criterion\FullText($searchString)]);
198
        $searchResult = $searchService->findLocations($query);
199
200
        $this->assertFulltextSearch($searchResult, $expectedKeys, $idMap);
201
    }
202
203
    /**
204
     * Assert given $searchResult using $expectedKeys and $idMap.
205
     *
206
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult
207
     * @param array $expectedKeys
208
     * @param array $idMap
209
     */
210
    public function assertFulltextSearch(SearchResult $searchResult, array $expectedKeys, array $idMap)
211
    {
212
        $this->assertEquals(
213
            array_reduce(
214
                $expectedKeys,
215
                function ($carry, $item) {
216
                    $carry += count((array)$item);
217
218
                    return $carry;
219
                },
220
                0
221
            ),
222
            $searchResult->totalCount
223
        );
224
225
        $expectedIds = $this->mapKeysToIds($expectedKeys, $idMap);
226
        $actualIds = $this->mapSearchResultToIds($searchResult);
227
228
        $this->assertEquals($expectedIds, $actualIds);
229
    }
230
231
    /**
232
     * Map given array of $expectedKeys to Content IDs, using $idMap.
233
     *
234
     * @param array $expectedKeys
235
     * @param array $idMap
236
     *
237
     * @return array
238
     */
239
    private function mapKeysToIds(array $expectedKeys, array $idMap)
240
    {
241
        $expectedIds = [];
242
243
        foreach ($expectedKeys as $keyGroup) {
244
            if (is_array($keyGroup)) {
245
                $idGroup = [];
246
247
                /** @var array $keyGroup */
248
                foreach ($keyGroup as $key) {
249
                    $idGroup[] = $idMap[$key];
250
                }
251
252
                sort($idGroup);
253
                $expectedIds[] = $idGroup;
254
255
                continue;
256
            }
257
258
            $key = $keyGroup;
259
            $expectedIds[] = $idMap[$key];
260
        }
261
262
        return $expectedIds;
263
    }
264
265
    /**
266
     * Map given $searchResult to an array of Content IDs, ordered and grouped by relevancy score.
267
     *
268
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult
269
     *
270
     * @return array
271
     */
272
    private function mapSearchResultToIds(SearchResult $searchResult)
273
    {
274
        $scoreGroupedIds = [];
275
276
        foreach ($searchResult->searchHits as $index => $searchHit) {
277
            if ($searchHit->valueObject instanceof Content || $searchHit->valueObject instanceof Location) {
278
                $contentInfo = $searchHit->valueObject->contentInfo;
279
            } elseif ($searchHit->valueObject instanceof ContentInfo) {
280
                $contentInfo = $searchHit->valueObject;
281
            } else {
282
                throw new RuntimeException('Unknown search hit value');
283
            }
284
285
            $scoreGroupedIds[(string)$searchHit->score][] = $contentInfo->id;
286
        }
287
288
        return array_map(
289
            function (array $idGroup) {
290
                if (count($idGroup) === 1) {
291
                    return reset($idGroup);
292
                }
293
294
                sort($idGroup);
295
296
                return $idGroup;
297
            },
298
            array_values($scoreGroupedIds)
299
        );
300
    }
301
}
302