Completed
Push — ezp-30646 ( 66adba...cee0f6 )
by
unknown
16:53
created

SearchServiceTest::assertQueryFixture()   F

Complexity

Conditions 14
Paths 576

Size

Total Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
nc 576
nop 6
dl 0
loc 80
rs 2.5874
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 * File containing the SearchServiceTest class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\API\Repository\Tests;
10
11
use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory;
12
use eZ\Publish\API\Repository\Values\Content\Content;
13
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
14
use eZ\Publish\API\Repository\Values\Content\Query;
15
use eZ\Publish\API\Repository\Values\Content\Location;
16
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
17
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
18
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
19
use eZ\Publish\API\Repository\Values\Content\Search\SearchResult;
20
use eZ\Publish\API\Repository\Values\Content\Search\SearchHit;
21
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
22
23
/**
24
 * Test case for operations in the SearchService.
25
 *
26
 * @see eZ\Publish\API\Repository\SearchService
27
 * @group integration
28
 * @group search
29
 */
30
class SearchServiceTest extends BaseTest
31
{
32
    const QUERY_CLASS = Query::class;
33
34
    use Common\FacetedSearchProvider;
35
36
    public function getFilterContentSearches()
37
    {
38
        $fixtureDir = $this->getFixtureDir();
39
40
        return [
41
            0 => [
42
                [
43
                    'filter' => new Criterion\ContentId(
44
                        [1, 4, 10]
45
                    ),
46
                    'sortClauses' => [new SortClause\ContentId()],
47
                ],
48
                $fixtureDir . 'ContentId.php',
49
            ],
50
            1 => [
51
                [
52
                    'filter' => new Criterion\LogicalAnd(
53
                        [
54
                            new Criterion\ContentId(
55
                                [1, 4, 10]
56
                            ),
57
                            new Criterion\ContentId(
58
                                [4, 12]
59
                            ),
60
                        ]
61
                    ),
62
                    'sortClauses' => [new SortClause\ContentId()],
63
                ],
64
                $fixtureDir . 'LogicalAnd.php',
65
            ],
66
            2 => [
67
                [
68
                    'filter' => new Criterion\LogicalOr(
69
                        [
70
                            new Criterion\ContentId(
71
                                [1, 4, 10]
72
                            ),
73
                            new Criterion\ContentId(
74
                                [4, 12]
75
                            ),
76
                        ]
77
                    ),
78
                    'sortClauses' => [new SortClause\ContentId()],
79
                ],
80
                $fixtureDir . 'LogicalOr.php',
81
            ],
82
            3 => [
83
                [
84
                    'filter' => new Criterion\LogicalAnd(
85
                        [
86
                            new Criterion\ContentId(
87
                                [1, 4, 10]
88
                            ),
89
                            new Criterion\LogicalNot(
90
                                new Criterion\ContentId(
91
                                    [10, 12]
92
                                )
93
                            ),
94
                        ]
95
                    ),
96
                    'sortClauses' => [new SortClause\ContentId()],
97
                ],
98
                $fixtureDir . 'LogicalNot.php',
99
            ],
100
            4 => [
101
                [
102
                    'filter' => new Criterion\LogicalAnd(
103
                        [
104
                            new Criterion\ContentId(
105
                                [1, 4, 10]
106
                            ),
107
                            new Criterion\LogicalAnd(
108
                                [
109
                                    new Criterion\LogicalNot(
110
                                        new Criterion\ContentId(
111
                                            [10, 12]
112
                                        )
113
                                    ),
114
                                ]
115
                            ),
116
                        ]
117
                    ),
118
                    'sortClauses' => [new SortClause\ContentId()],
119
                ],
120
                $fixtureDir . 'LogicalNot.php',
121
            ],
122
            5 => [
123
                [
124
                    'filter' => new Criterion\ContentTypeId(
125
                        4
126
                    ),
127
                    'sortClauses' => [new SortClause\ContentId()],
128
                ],
129
                $fixtureDir . 'ContentTypeId.php',
130
            ],
131
            6 => [
132
                [
133
                    'filter' => new Criterion\ContentTypeIdentifier(
134
                        'user'
135
                    ),
136
                    'sortClauses' => [new SortClause\ContentId()],
137
                ],
138
                $fixtureDir . 'ContentTypeId.php',
139
            ],
140
            7 => [
141
                [
142
                    'filter' => new Criterion\MatchNone(),
143
                    'sortClauses' => [new SortClause\ContentId()],
144
                ],
145
                $fixtureDir . 'MatchNone.php',
146
            ],
147
            8 => [
148
                [
149
                    'filter' => new Criterion\ContentTypeGroupId(
150
                        2
151
                    ),
152
                    'sortClauses' => [new SortClause\ContentId()],
153
                ],
154
                $fixtureDir . 'ContentTypeGroupId.php',
155
            ],
156
            9 => [
157
                [
158
                    'filter' => new Criterion\DateMetadata(
159
                        Criterion\DateMetadata::MODIFIED,
160
                        Criterion\Operator::GT,
161
                        1343140540
162
                    ),
163
                    'sortClauses' => [new SortClause\ContentId()],
164
                ],
165
                $fixtureDir . 'DateMetadataGt.php',
166
            ],
167
            10 => [
168
                [
169
                    'filter' => new Criterion\DateMetadata(
170
                        Criterion\DateMetadata::MODIFIED,
171
                        Criterion\Operator::GTE,
172
                        1311154215
173
                    ),
174
                    'sortClauses' => [new SortClause\ContentId()],
175
                ],
176
                $fixtureDir . 'DateMetadataGte.php',
177
            ],
178
            11 => [
179
                [
180
                    'filter' => new Criterion\DateMetadata(
181
                        Criterion\DateMetadata::MODIFIED,
182
                        Criterion\Operator::LTE,
183
                        1311154215
184
                    ),
185
                    'limit' => 10,
186
                    'sortClauses' => [new SortClause\ContentId()],
187
                ],
188
                $fixtureDir . 'DateMetadataLte.php',
189
            ],
190
            12 => [
191
                [
192
                    'filter' => new Criterion\DateMetadata(
193
                        Criterion\DateMetadata::MODIFIED,
194
                        Criterion\Operator::IN,
195
                        [1033920794, 1060695457, 1343140540]
196
                    ),
197
                    'sortClauses' => [new SortClause\ContentId()],
198
                ],
199
                $fixtureDir . 'DateMetadataIn.php',
200
            ],
201
            13 => [
202
                [
203
                    'filter' => new Criterion\DateMetadata(
204
                        Criterion\DateMetadata::MODIFIED,
205
                        Criterion\Operator::BETWEEN,
206
                        [1033920776, 1072180276]
207
                    ),
208
                    'sortClauses' => [new SortClause\ContentId()],
209
                ],
210
                $fixtureDir . 'DateMetadataBetween.php',
211
            ],
212
            14 => [
213
                [
214
                    'filter' => new Criterion\DateMetadata(
215
                        Criterion\DateMetadata::CREATED,
216
                        Criterion\Operator::BETWEEN,
217
                        [1033920776, 1072180278]
218
                    ),
219
                    'sortClauses' => [new SortClause\ContentId()],
220
                ],
221
                $fixtureDir . 'DateMetadataCreated.php',
222
            ],
223
            15 => [
224
                [
225
                    'filter' => new Criterion\CustomField(
226
                        'user_group_name_value_s',
227
                        Criterion\Operator::EQ,
228
                        'Members'
229
                    ),
230
                    'sortClauses' => [new SortClause\ContentId()],
231
                ],
232
                $fixtureDir . 'Field.php',
233
            ],
234
            16 => [
235
                [
236
                    'filter' => new Criterion\CustomField(
237
                        'user_group_name_value_s',
238
                        Criterion\Operator::CONTAINS,
239
                        'Members'
240
                    ),
241
                    'sortClauses' => [new SortClause\ContentId()],
242
                ],
243
                $fixtureDir . 'Field.php',
244
            ],
245
            17 => [
246
                [
247
                    'filter' => new Criterion\CustomField(
248
                        'user_group_name_value_s',
249
                        Criterion\Operator::LT,
250
                        'Members'
251
                    ),
252
                    'sortClauses' => [new SortClause\ContentId()],
253
                ],
254
                $fixtureDir . 'CustomFieldLt.php',
255
            ],
256
            18 => [
257
                [
258
                    'filter' => new Criterion\CustomField(
259
                        'user_group_name_value_s',
260
                        Criterion\Operator::LTE,
261
                        'Members'
262
                    ),
263
                    'sortClauses' => [new SortClause\ContentId()],
264
                ],
265
                $fixtureDir . 'CustomFieldLte.php',
266
            ],
267
            19 => [
268
                [
269
                    'filter' => new Criterion\CustomField(
270
                        'user_group_name_value_s',
271
                        Criterion\Operator::GT,
272
                        'Members'
273
                    ),
274
                    'sortClauses' => [new SortClause\ContentId()],
275
                ],
276
                $fixtureDir . 'CustomFieldGt.php',
277
            ],
278
            20 => [
279
                [
280
                    'filter' => new Criterion\CustomField(
281
                        'user_group_name_value_s',
282
                        Criterion\Operator::GTE,
283
                        'Members'
284
                    ),
285
                    'sortClauses' => [new SortClause\ContentId()],
286
                ],
287
                $fixtureDir . 'CustomFieldGte.php',
288
            ],
289
            21 => [
290
                [
291
                    'filter' => new Criterion\CustomField(
292
                        'user_group_name_value_s',
293
                        Criterion\Operator::BETWEEN,
294
                        ['Administrator users', 'Members']
295
                    ),
296
                    'sortClauses' => [new SortClause\ContentId()],
297
                ],
298
                $fixtureDir . 'CustomFieldBetween.php',
299
            ],
300
            22 => [
301
                [
302
                    'filter' => new Criterion\RemoteId(
303
                        ['f5c88a2209584891056f987fd965b0ba', 'faaeb9be3bd98ed09f606fc16d144eca']
304
                    ),
305
                    'sortClauses' => [new SortClause\ContentId()],
306
                ],
307
                $fixtureDir . 'RemoteId.php',
308
            ],
309
            23 => [
310
                [
311
                    'filter' => new Criterion\SectionId(
312
                        [2]
313
                    ),
314
                    'sortClauses' => [new SortClause\ContentId()],
315
                ],
316
                $fixtureDir . 'SectionId.php',
317
            ],
318
            24 => [
319
                [
320
                    'filter' => new Criterion\Field(
321
                        'name',
322
                        Criterion\Operator::EQ,
323
                        'Members'
324
                    ),
325
                    'sortClauses' => [new SortClause\ContentId()],
326
                ],
327
                $fixtureDir . 'Field.php',
328
            ],
329
            25 => [
330
                [
331
                    'filter' => new Criterion\Field(
332
                        'name',
333
                        Criterion\Operator::IN,
334
                        ['Members', 'Anonymous Users']
335
                    ),
336
                    'sortClauses' => [new SortClause\ContentId()],
337
                ],
338
                $fixtureDir . 'FieldIn.php',
339
            ],
340
            26 => [
341
                [
342
                    'filter' => new Criterion\DateMetadata(
343
                        Criterion\DateMetadata::MODIFIED,
344
                        Criterion\Operator::BETWEEN,
345
                        [1033920275, 1033920794]
346
                    ),
347
                    'sortClauses' => [new SortClause\ContentId()],
348
                ],
349
                $fixtureDir . 'FieldBetween.php',
350
            ],
351
            27 => [
352
                [
353
                    'filter' => new Criterion\LogicalOr(
354
                        [
355
                            new Criterion\Field(
356
                                'name',
357
                                Criterion\Operator::EQ,
358
                                'Members'
359
                            ),
360
                            new Criterion\DateMetadata(
361
                                Criterion\DateMetadata::MODIFIED,
362
                                Criterion\Operator::BETWEEN,
363
                                [1033920275, 1033920794]
364
                            ),
365
                        ]
366
                    ),
367
                    'sortClauses' => [new SortClause\ContentId()],
368
                ],
369
                $fixtureDir . 'FieldOr.php',
370
            ],
371
            28 => [
372
                [
373
                    'filter' => new Criterion\Subtree(
374
                        '/1/5/'
375
                    ),
376
                    'sortClauses' => [new SortClause\ContentId()],
377
                ],
378
                $fixtureDir . 'Subtree.php',
379
            ],
380
            29 => [
381
                [
382
                    'filter' => new Criterion\LocationId(
383
                        [1, 2, 5]
384
                    ),
385
                    'sortClauses' => [new SortClause\ContentId()],
386
                ],
387
                $fixtureDir . 'LocationId.php',
388
            ],
389
            30 => [
390
                [
391
                    'filter' => new Criterion\ParentLocationId(
392
                        [1]
393
                    ),
394
                    'sortClauses' => [new SortClause\ContentId()],
395
                ],
396
                $fixtureDir . 'ParentLocationId.php',
397
            ],
398
            31 => [
399
                [
400
                    'filter' => new Criterion\LocationRemoteId(
401
                        ['3f6d92f8044aed134f32153517850f5a', 'f3e90596361e31d496d4026eb624c983']
402
                    ),
403
                    'sortClauses' => [new SortClause\ContentId()],
404
                ],
405
                $fixtureDir . 'LocationRemoteId.php',
406
            ],
407
            32 => [
408
                [
409
                    // There is no Status Criterion anymore, this should match all published as well
410
                    'filter' => new Criterion\Subtree(
411
                        '/1/'
412
                    ),
413
                    'sortClauses' => [new SortClause\ContentId()],
414
                    'limit' => 50,
415
                ],
416
                $fixtureDir . 'Status.php',
417
                // Result having the same sort level should be sorted between them to be system independent
418
                function (&$data) {
419
                    usort(
420
                        $data->searchHits,
421
                        function ($a, $b) {
422
                            if ($a->score == $b->score) {
423
                                if ($a->valueObject['id'] == $b->valueObject['id']) {
424
                                    return 0;
425
                                }
426
427
                                // Order by ascending ID
428
                                return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
429
                            }
430
431
                            // Order by descending score
432
                            return ($a->score > $b->score) ? -1 : 1;
433
                        }
434
                    );
435
                },
436
            ],
437
            33 => [
438
                [
439
                    'filter' => new Criterion\UserMetadata(
440
                        Criterion\UserMetadata::MODIFIER,
441
                        Criterion\Operator::EQ,
442
                        14
443
                    ),
444
                    'sortClauses' => [
445
                        new SortClause\ContentId(),
446
                    ],
447
                    'limit' => 50,
448
                ],
449
                $fixtureDir . 'UserMetadata.php',
450
            ],
451
            34 => [
452
                [
453
                    'filter' => new Criterion\UserMetadata(
454
                        Criterion\UserMetadata::MODIFIER,
455
                        Criterion\Operator::IN,
456
                        [14]
457
                    ),
458
                    'sortClauses' => [
459
                        new SortClause\ContentId(),
460
                    ],
461
                    'limit' => 50,
462
                ],
463
                $fixtureDir . 'UserMetadata.php',
464
            ],
465
            35 => [
466
                [
467
                    'filter' => new Criterion\UserMetadata(
468
                        Criterion\UserMetadata::OWNER,
469
                        Criterion\Operator::EQ,
470
                        14
471
                    ),
472
                    'sortClauses' => [
473
                        new SortClause\ContentId(),
474
                    ],
475
                    'limit' => 50,
476
                ],
477
                $fixtureDir . 'UserMetadata.php',
478
            ],
479
            36 => [
480
                [
481
                    'filter' => new Criterion\UserMetadata(
482
                        Criterion\UserMetadata::OWNER,
483
                        Criterion\Operator::IN,
484
                        [14]
485
                    ),
486
                    'sortClauses' => [
487
                        new SortClause\ContentId(),
488
                    ],
489
                    'limit' => 50,
490
                ],
491
                $fixtureDir . 'UserMetadata.php',
492
            ],
493
            37 => [
494
                [
495
                    'filter' => new Criterion\UserMetadata(
496
                        Criterion\UserMetadata::GROUP,
497
                        Criterion\Operator::EQ,
498
                        12
499
                    ),
500
                    'sortClauses' => [
501
                        new SortClause\ContentId(),
502
                    ],
503
                    'limit' => 50,
504
                ],
505
                $fixtureDir . 'UserMetadata.php',
506
            ],
507
            38 => [
508
                [
509
                    'filter' => new Criterion\UserMetadata(
510
                        Criterion\UserMetadata::GROUP,
511
                        Criterion\Operator::IN,
512
                        [12]
513
                    ),
514
                    'sortClauses' => [
515
                        new SortClause\ContentId(),
516
                    ],
517
                    'limit' => 50,
518
                ],
519
                $fixtureDir . 'UserMetadata.php',
520
            ],
521
            39 => [
522
                [
523
                    'filter' => new Criterion\UserMetadata(
524
                        Criterion\UserMetadata::GROUP,
525
                        Criterion\Operator::EQ,
526
                        4
527
                    ),
528
                    'sortClauses' => [
529
                        new SortClause\ContentId(),
530
                    ],
531
                    'limit' => 50,
532
                ],
533
                $fixtureDir . 'UserMetadata.php',
534
            ],
535
            40 => [
536
                [
537
                    'filter' => new Criterion\UserMetadata(
538
                        Criterion\UserMetadata::GROUP,
539
                        Criterion\Operator::IN,
540
                        [4]
541
                    ),
542
                    'sortClauses' => [
543
                        new SortClause\ContentId(),
544
                    ],
545
                    'limit' => 50,
546
                ],
547
                $fixtureDir . 'UserMetadata.php',
548
            ],
549
            41 => [
550
                [
551
                    'filter' => new Criterion\Ancestor(
552
                        [
553
                            '/1/5/44/',
554
                            '/1/5/44/45/',
555
                        ]
556
                    ),
557
                    'sortClauses' => [
558
                        new SortClause\ContentId(),
559
                    ],
560
                    'limit' => 50,
561
                ],
562
                $fixtureDir . 'AncestorContent.php',
563
            ],
564
        ];
565
    }
566
567
    public function getContentQuerySearches()
568
    {
569
        $fixtureDir = $this->getFixtureDir();
570
571
        return [
572
            [
573
                [
574
                    'filter' => new Criterion\ContentId(
575
                        [58, 10]
576
                    ),
577
                    'query' => new Criterion\FullText('contact'),
578
                    'sortClauses' => [new SortClause\ContentId()],
579
                ],
580
                $fixtureDir . 'FullTextFiltered.php',
581
            ],
582
            [
583
                [
584
                    'query' => new Criterion\FullText(
585
                        'contact',
586
                        [
587
                            'boost' => [
588
                                'title' => 2,
589
                            ],
590
                            'fuzziness' => .5,
591
                        ]
592
                    ),
593
                    'sortClauses' => [new SortClause\ContentId()],
594
                ],
595
                $fixtureDir . 'FullText.php',
596
            ],
597
            [
598
                [
599
                    'query' => new Criterion\FullText(
600
                        'Contact*'
601
                    ),
602
                    'sortClauses' => [new SortClause\ContentId()],
603
                ],
604
                $fixtureDir . 'FullTextWildcard.php',
605
            ],
606
            [
607
                [
608
                    'query' => new Criterion\LanguageCode('eng-GB', false),
609
                    'sortClauses' => [new SortClause\ContentId()],
610
                ],
611
                $fixtureDir . 'LanguageCode.php',
612
            ],
613
            [
614
                [
615
                    'query' => new Criterion\LanguageCode(['eng-US', 'eng-GB']),
616
                    'offset' => 10,
617
                    'sortClauses' => [new SortClause\ContentId()],
618
                ],
619
                $fixtureDir . 'LanguageCodeIn.php',
620
            ],
621
            [
622
                [
623
                    'query' => new Criterion\LanguageCode('eng-GB'),
624
                    'offset' => 10,
625
                    'sortClauses' => [new SortClause\ContentId()],
626
                ],
627
                $fixtureDir . 'LanguageCodeAlwaysAvailable.php',
628
            ],
629
            [
630
                [
631
                    'query' => new Criterion\Visibility(
632
                        Criterion\Visibility::VISIBLE
633
                    ),
634
                    'sortClauses' => [new SortClause\ContentId()],
635
                    'limit' => 50,
636
                ],
637
                $fixtureDir . 'Visibility.php',
638
            ],
639
        ];
640
    }
641
642
    public function getLocationQuerySearches()
643
    {
644
        $fixtureDir = $this->getFixtureDir();
645
646
        return [
647
            [
648
                [
649
                    'query' => new Criterion\Location\Depth(Criterion\Operator::EQ, 1),
650
                    'sortClauses' => [new SortClause\ContentId()],
651
                ],
652
                $fixtureDir . 'Depth.php',
653
            ],
654
            [
655
                [
656
                    'query' => new Criterion\Location\Depth(Criterion\Operator::IN, [1, 3]),
657
                    'sortClauses' => [new SortClause\ContentId()],
658
                ],
659
                $fixtureDir . 'DepthIn.php',
660
            ],
661
            [
662
                [
663
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GT, 2),
664
                    'sortClauses' => [new SortClause\ContentId()],
665
                ],
666
                $fixtureDir . 'DepthGt.php',
667
            ],
668
            [
669
                [
670
                    'query' => new Criterion\Location\Depth(Criterion\Operator::GTE, 2),
671
                    'sortClauses' => [new SortClause\ContentId()],
672
                    'limit' => 50,
673
                ],
674
                $fixtureDir . 'DepthGte.php',
675
            ],
676
            [
677
                [
678
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LT, 2),
679
                    'sortClauses' => [new SortClause\ContentId()],
680
                ],
681
                $fixtureDir . 'Depth.php',
682
            ],
683
            [
684
                [
685
                    'query' => new Criterion\Location\Depth(Criterion\Operator::LTE, 2),
686
                    'sortClauses' => [new SortClause\ContentId()],
687
                    'limit' => 50,
688
                ],
689
                $fixtureDir . 'DepthLte.php',
690
            ],
691
            [
692
                [
693
                    'query' => new Criterion\Location\Depth(Criterion\Operator::BETWEEN, [1, 2]),
694
                    'sortClauses' => [new SortClause\ContentId()],
695
                    'limit' => 50,
696
                ],
697
                $fixtureDir . 'DepthLte.php',
698
            ],
699
            [
700
                [
701
                    'filter' => new Criterion\Ancestor('/1/5/44/45/'),
702
                    'sortClauses' => [
703
                        new SortClause\Location\Depth(),
704
                    ],
705
                    'limit' => 50,
706
                ],
707
                $fixtureDir . 'AncestorLocation.php',
708
            ],
709
        ];
710
    }
711
712
    /**
713
     * Test for the findContent() method.
714
     *
715
     * @dataProvider getFilterContentSearches
716
     *
717
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
718
     */
719
    public function testFindContentFiltered($queryData, $fixture, $closure = null)
720
    {
721
        $query = new Query($queryData);
722
        $this->assertQueryFixture($query, $fixture, $closure);
723
    }
724
725
    /**
726
     * Test for the findContentInfo() method.
727
     *
728
     * @dataProvider getFilterContentSearches
729
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
730
     */
731
    public function testFindContentInfoFiltered($queryData, $fixture, $closure = null)
732
    {
733
        $query = new Query($queryData);
734
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
735
    }
736
737
    /**
738
     * Test for the findLocations() method.
739
     *
740
     * @dataProvider getFilterContentSearches
741
     *
742
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
743
     */
744
    public function testFindLocationsContentFiltered($queryData, $fixture, $closure = null)
745
    {
746
        $query = new LocationQuery($queryData);
747
        $this->assertQueryFixture($query, $fixture, $closure);
748
    }
749
750
    /**
751
     * Test for deprecated $criterion property on query object.
752
     *
753
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
754
     * @deprecated
755
     */
756
    public function testDeprecatedCriteriaProperty()
757
    {
758
        $this->assertQueryFixture(
759
            new Query(
760
                [
761
                    'query' => new Criterion\ContentId(
762
                        [1, 4, 10]
763
                    ),
764
                    'sortClauses' => [new SortClause\ContentId()],
765
                ]
766
            ),
767
            $this->getFixtureDir() . 'DeprecatedContentIdQuery.php'
768
        );
769
    }
770
771
    /**
772
     * Test for the findContent() method.
773
     *
774
     * @dataProvider getContentQuerySearches
775
     *
776
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
777
     */
778
    public function testQueryContent($queryData, $fixture, $closure = null)
779
    {
780
        $query = new Query($queryData);
781
        $this->assertQueryFixture($query, $fixture, $closure);
782
    }
783
784
    /**
785
     * Test for the findContentInfo() method.
786
     *
787
     * @dataProvider getContentQuerySearches
788
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
789
     */
790
    public function testQueryContentInfo($queryData, $fixture, $closure = null)
791
    {
792
        $query = new Query($queryData);
793
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
794
    }
795
796
    /**
797
     * Test for the findLocations() method.
798
     *
799
     * @dataProvider getContentQuerySearches
800
     *
801
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
802
     */
803
    public function testQueryContentLocations($queryData, $fixture, $closure = null)
804
    {
805
        $query = new LocationQuery($queryData);
806
        $this->assertQueryFixture($query, $fixture, $closure);
807
    }
808
809
    /**
810
     * Test for the findLocations() method.
811
     *
812
     * @dataProvider getLocationQuerySearches
813
     *
814
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
815
     */
816
    public function testQueryLocations($queryData, $fixture, $closure = null)
817
    {
818
        $query = new LocationQuery($queryData);
819
        $this->assertQueryFixture($query, $fixture, $closure);
820
    }
821
822
    public function getCaseInsensitiveSearches()
823
    {
824
        return [
825
            [
826
                [
827
                    'filter' => new Criterion\Field(
828
                        'name',
829
                        Criterion\Operator::EQ,
830
                        'Members'
831
                    ),
832
                    'sortClauses' => [new SortClause\ContentId()],
833
                ],
834
            ],
835
            [
836
                [
837
                    'filter' => new Criterion\Field(
838
                        'name',
839
                        Criterion\Operator::EQ,
840
                        'members'
841
                    ),
842
                    'sortClauses' => [new SortClause\ContentId()],
843
                ],
844
            ],
845
            [
846
                [
847
                    'filter' => new Criterion\Field(
848
                        'name',
849
                        Criterion\Operator::EQ,
850
                        'MEMBERS'
851
                    ),
852
                    'sortClauses' => [new SortClause\ContentId()],
853
                ],
854
            ],
855
        ];
856
    }
857
858
    /**
859
     * Test for the findContent() method.
860
     *
861
     * @dataProvider getCaseInsensitiveSearches
862
     *
863
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
864
     */
865
    public function testFindContentFieldFiltersCaseSensitivity($queryData)
866
    {
867
        $query = new Query($queryData);
868
        $this->assertQueryFixture(
869
            $query,
870
            $this->getFixtureDir() . 'Field.php'
871
        );
872
    }
873
874
    /**
875
     * Test for the findLocations() method.
876
     *
877
     * @dataProvider getCaseInsensitiveSearches
878
     *
879
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
880
     */
881
    public function testFindLocationsFieldFiltersCaseSensitivity($queryData)
882
    {
883
        $query = new LocationQuery($queryData);
884
        $this->assertQueryFixture(
885
            $query,
886
            $this->getFixtureDir() . 'Field.php'
887
        );
888
    }
889
890
    public function getRelationFieldFilterSearches()
891
    {
892
        $fixtureDir = $this->getFixtureDir();
893
894
        return [
895
            0 => [
896
                [
897
                    'filter' => new Criterion\FieldRelation(
898
                        'image',
899
                        Criterion\Operator::IN,
900
                        [1, 4, 10]
901
                    ),
902
                    'sortClauses' => [new SortClause\ContentId()],
903
                ],
904
                $fixtureDir . 'FieldRelation.php',
905
            ],
906
            1 => [
907
                [
908
                    'filter' => new Criterion\FieldRelation(
909
                        'image',
910
                        Criterion\Operator::IN,
911
                        [4, 49]
912
                    ),
913
                    'sortClauses' => [new SortClause\ContentId()],
914
                ],
915
                $fixtureDir . 'FieldRelationAll.php',
916
            ],
917
            2 => [
918
                [
919
                    'filter' => new Criterion\FieldRelation(
920
                        'image',
921
                        Criterion\Operator::IN,
922
                        [4]
923
                    ),
924
                    'sortClauses' => [new SortClause\ContentId()],
925
                ],
926
                $fixtureDir . 'FieldRelation.php',
927
            ],
928
            3 => [
929
                [
930
                    'filter' => new Criterion\FieldRelation(
931
                        'image',
932
                        Criterion\Operator::CONTAINS,
933
                        [1, 4, 10]
934
                    ),
935
                    'sortClauses' => [new SortClause\ContentId()],
936
                ],
937
                $fixtureDir . 'MatchNone.php',
938
            ],
939
            4 => [
940
                [
941
                    'filter' => new Criterion\FieldRelation(
942
                        'image',
943
                        Criterion\Operator::CONTAINS,
944
                        [4, 49]
945
                    ),
946
                    'sortClauses' => [new SortClause\ContentId()],
947
                ],
948
                $fixtureDir . 'MatchNone.php',
949
            ],
950
            5 => [
951
                [
952
                    'filter' => new Criterion\FieldRelation(
953
                        'image',
954
                        Criterion\Operator::CONTAINS,
955
                        [4]
956
                    ),
957
                    'sortClauses' => [new SortClause\ContentId()],
958
                ],
959
                $fixtureDir . 'FieldRelation.php',
960
            ],
961
        ];
962
    }
963
964
    /**
965
     * Purely for creating relation data needed for testFindRelationFieldContentInfoFiltered()
966
     * and testFindRelationFieldLocationsFiltered().
967
     */
968
    public function testRelationContentCreation()
969
    {
970
        $repository = $this->getRepository();
971
        $galleryType = $repository->getContentTypeService()->loadContentTypeByIdentifier('gallery');
972
        $contentService = $repository->getContentService();
973
        $locationService = $repository->getLocationService();
974
975
        $locationCreateStruct = $locationService->newLocationCreateStruct(2); // Home
976
977
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
978
        $createStruct->setField('name', 'Image gallery');
979
        $createStruct->setField('image', 49); // Images folder
980
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
981
        $contentService->publishVersion($draft->getVersionInfo());
982
983
        $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB');
984
        $createStruct->setField('name', 'User gallery');
985
        $createStruct->setField('image', 4); // User folder
986
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
987
        $contentService->publishVersion($draft->getVersionInfo());
988
989
        $this->refreshSearch($repository);
990
    }
991
992
    /**
993
     * Test for FieldRelation using findContentInfo() method.
994
     *
995
     * @dataProvider getRelationFieldFilterSearches
996
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
997
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
998
     */
999
    public function testFindRelationFieldContentInfoFiltered($queryData, $fixture)
1000
    {
1001
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1002
        $query = new Query($queryData);
1003
        $this->assertQueryFixture($query, $fixture, null, true, true, false);
1004
    }
1005
1006
    /**
1007
     * Test for FieldRelation using findLocations() method.
1008
     *
1009
     * @dataProvider getRelationFieldFilterSearches
1010
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
1011
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation
1012
     */
1013
    public function testFindRelationFieldLocationsFiltered($queryData, $fixture)
1014
    {
1015
        $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches
1016
        $query = new LocationQuery($queryData);
1017
        $this->assertQueryFixture($query, $fixture, null, true, false, false);
1018
    }
1019
1020
    public function testFindSingle()
1021
    {
1022
        $repository = $this->getRepository();
1023
        $searchService = $repository->getSearchService();
1024
1025
        $content = $searchService->findSingle(
1026
            new Criterion\ContentId(
1027
                [4]
1028
            )
1029
        );
1030
1031
        $this->assertEquals(
1032
            4,
1033
            $content->id
1034
        );
1035
    }
1036
1037
    public function testFindNoPerformCount()
1038
    {
1039
        $repository = $this->getRepository();
1040
        $searchService = $repository->getSearchService();
1041
1042
        $query = new Query();
1043
        $query->performCount = false;
1044
        $query->query = new Criterion\ContentTypeId(
1045
            [4]
1046
        );
1047
1048
        $searchHit = $searchService->findContent($query);
1049
1050
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1051
            $this->assertNull(
1052
                $searchHit->totalCount
1053
            );
1054
        } else {
1055
            $this->assertEquals(
1056
                2,
1057
                $searchHit->totalCount
1058
            );
1059
        }
1060
    }
1061
1062
    public function testFindNoPerformCountException()
1063
    {
1064
        $this->expectException(\RuntimeException::class);
1065
1066
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1067
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1068
        }
1069
1070
        $repository = $this->getRepository();
1071
        $searchService = $repository->getSearchService();
1072
1073
        $query = new Query();
1074
        $query->performCount = false;
1075
        $query->limit = 0;
1076
        $query->query = new Criterion\ContentTypeId(
1077
            [4]
1078
        );
1079
1080
        $searchService->findContent($query);
1081
    }
1082
1083
    public function testFindLocationsNoPerformCount()
1084
    {
1085
        $repository = $this->getRepository();
1086
        $searchService = $repository->getSearchService();
1087
1088
        $query = new LocationQuery();
1089
        $query->performCount = false;
1090
        $query->query = new Criterion\ContentTypeId(
1091
            [4]
1092
        );
1093
1094
        $searchHit = $searchService->findLocations($query);
1095
1096
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1097
            $this->assertNull(
1098
                $searchHit->totalCount
1099
            );
1100
        } else {
1101
            $this->assertEquals(
1102
                2,
1103
                $searchHit->totalCount
1104
            );
1105
        }
1106
    }
1107
1108
    public function testFindLocationsNoPerformCountException()
1109
    {
1110
        $this->expectException(\RuntimeException::class);
1111
1112
        if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
1113
            $this->markTestSkipped('Only applicable to Legacy/DB based search');
1114
        }
1115
1116
        $repository = $this->getRepository();
1117
        $searchService = $repository->getSearchService();
1118
1119
        $query = new LocationQuery();
1120
        $query->performCount = false;
1121
        $query->limit = 0;
1122
        $query->query = new Criterion\ContentTypeId(
1123
            [4]
1124
        );
1125
1126
        $searchService->findLocations($query);
1127
    }
1128
1129
    /**
1130
     * Create test Content with ezcountry field having multiple countries selected.
1131
     *
1132
     * @return Content
1133
     */
1134
    protected function createMultipleCountriesContent()
1135
    {
1136
        $repository = $this->getRepository();
1137
        $contentTypeService = $repository->getContentTypeService();
1138
        $contentService = $repository->getContentService();
1139
1140
        $createStruct = $contentTypeService->newContentTypeCreateStruct('countries-multiple');
1141
        $createStruct->mainLanguageCode = 'eng-GB';
1142
        $createStruct->remoteId = 'countries-multiple-123';
1143
        $createStruct->names = ['eng-GB' => 'Multiple countries'];
1144
        $createStruct->creatorId = 14;
1145
        $createStruct->creationDate = new \DateTime();
1146
1147
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('countries', 'ezcountry');
1148
        $fieldCreate->names = ['eng-GB' => 'Countries'];
1149
        $fieldCreate->fieldGroup = 'main';
1150
        $fieldCreate->position = 1;
1151
        $fieldCreate->isTranslatable = false;
1152
        $fieldCreate->isSearchable = true;
1153
        $fieldCreate->fieldSettings = ['isMultiple' => true];
1154
1155
        $createStruct->addFieldDefinition($fieldCreate);
1156
1157
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1158
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1159
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1160
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1161
1162
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1163
        $createStruct->remoteId = 'countries-multiple-456';
1164
        $createStruct->alwaysAvailable = false;
1165
        $createStruct->setField(
1166
            'countries',
1167
            ['BE', 'DE', 'FR', 'HR', 'NO', 'PT', 'RU']
1168
        );
1169
1170
        $draft = $contentService->createContent($createStruct);
1171
        $content = $contentService->publishVersion($draft->getVersionInfo());
1172
1173
        $this->refreshSearch($repository);
1174
1175
        return $content;
1176
    }
1177
1178
    /**
1179
     * Test for the findContent() method.
1180
     *
1181
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1182
     */
1183
    public function testFieldCollectionContains()
1184
    {
1185
        $testContent = $this->createMultipleCountriesContent();
1186
1187
        $query = new Query(
1188
            [
1189
                'query' => new Criterion\Field(
1190
                    'countries',
1191
                    Criterion\Operator::CONTAINS,
1192
                    'Belgium'
1193
                ),
1194
            ]
1195
        );
1196
1197
        $repository = $this->getRepository();
1198
        $searchService = $repository->getSearchService();
1199
        $result = $searchService->findContent($query);
1200
1201
        $this->assertEquals(1, $result->totalCount);
1202
        $this->assertEquals(
1203
            $testContent->id,
1204
            $result->searchHits[0]->valueObject->id
1205
        );
1206
    }
1207
1208
    /**
1209
     * Test for the findContent() method.
1210
     *
1211
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
1212
     * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFieldCollectionContains
1213
     */
1214
    public function testFieldCollectionContainsNoMatch()
1215
    {
1216
        $this->createMultipleCountriesContent();
1217
        $query = new Query(
1218
            [
1219
                'query' => new Criterion\Field(
1220
                    'countries',
1221
                    Criterion\Operator::CONTAINS,
1222
                    'Netherlands Antilles'
1223
                ),
1224
            ]
1225
        );
1226
1227
        $repository = $this->getRepository();
1228
        $searchService = $repository->getSearchService();
1229
        $result = $searchService->findContent($query);
1230
1231
        $this->assertEquals(0, $result->totalCount);
1232
    }
1233
1234
    public function testInvalidFieldIdentifierRange()
1235
    {
1236
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1237
        $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable fields found for the given criterion target \'some_hopefully_unknown_field\'');
1238
1239
        $repository = $this->getRepository();
1240
        $searchService = $repository->getSearchService();
1241
1242
        $searchService->findContent(
1243
            new Query(
1244
                [
1245
                    'filter' => new Criterion\Field(
1246
                        'some_hopefully_unknown_field',
1247
                        Criterion\Operator::BETWEEN,
1248
                        [10, 1000]
1249
                    ),
1250
                    'sortClauses' => [new SortClause\ContentId()],
1251
                ]
1252
            )
1253
        );
1254
    }
1255
1256
    public function testInvalidFieldIdentifierIn()
1257
    {
1258
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1259
        $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable fields found for the given criterion target \'some_hopefully_unknown_field\'');
1260
1261
        $repository = $this->getRepository();
1262
        $searchService = $repository->getSearchService();
1263
1264
        $searchService->findContent(
1265
            new Query(
1266
                [
1267
                    'filter' => new Criterion\Field(
1268
                        'some_hopefully_unknown_field',
1269
                        Criterion\Operator::EQ,
1270
                        1000
1271
                    ),
1272
                    'sortClauses' => [new SortClause\ContentId()],
1273
                ]
1274
            )
1275
        );
1276
    }
1277
1278
    public function testFindContentWithNonSearchableField()
1279
    {
1280
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1281
        $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable fields found for the given criterion target \'tag_cloud_url\'');
1282
1283
        $repository = $this->getRepository();
1284
        $searchService = $repository->getSearchService();
1285
1286
        $searchService->findContent(
1287
            new Query(
1288
                [
1289
                    'filter' => new Criterion\Field(
1290
                        'tag_cloud_url',
1291
                        Criterion\Operator::EQ,
1292
                        'http://nimbus.com'
1293
                    ),
1294
                    'sortClauses' => [new SortClause\ContentId()],
1295
                ]
1296
            )
1297
        );
1298
    }
1299
1300
    public function testSortFieldWithNonSearchableField()
1301
    {
1302
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1303
        $this->expectExceptionMessage('Argument \'$sortClause->targetData\' is invalid: No searchable fields found for the given sort clause target \'title\' on \'template_look\'');
1304
1305
        $repository = $this->getRepository();
1306
        $searchService = $repository->getSearchService();
1307
1308
        $searchService->findContent(
1309
            new Query(
1310
                [
1311
                    'sortClauses' => [new SortClause\Field('template_look', 'title')],
1312
                ]
1313
            )
1314
        );
1315
    }
1316
1317
    public function testSortMapLocationDistanceWithNonSearchableField()
1318
    {
1319
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1320
        $this->expectExceptionMessage('Argument \'$sortClause->targetData\' is invalid: No searchable fields found for the given sort clause target \'title\' on \'template_look\'');
1321
1322
        $repository = $this->getRepository();
1323
        $searchService = $repository->getSearchService();
1324
1325
        $searchService->findContent(
1326
            new Query(
1327
                [
1328
                    'sortClauses' => [
1329
                        new SortClause\MapLocationDistance(
1330
                            'template_look',
1331
                            'title',
1332
                            1,
1333
                            2
1334
                        ),
1335
                    ],
1336
                ]
1337
            )
1338
        );
1339
    }
1340
1341
    public function testFindSingleFailMultiple()
1342
    {
1343
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1344
1345
        $repository = $this->getRepository();
1346
        $searchService = $repository->getSearchService();
1347
1348
        $searchService->findSingle(
1349
            new Criterion\ContentId(
1350
                [4, 10]
1351
            )
1352
        );
1353
    }
1354
1355
    public function testFindSingleWithNonSearchableField()
1356
    {
1357
        $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class);
1358
1359
        $repository = $this->getRepository();
1360
        $searchService = $repository->getSearchService();
1361
1362
        $searchService->findSingle(
1363
            new Criterion\Field(
1364
                'tag_cloud_url',
1365
                Criterion\Operator::EQ,
1366
                'http://nimbus.com'
1367
            )
1368
        );
1369
    }
1370
1371
    public function getSortedContentSearches()
1372
    {
1373
        $fixtureDir = $this->getFixtureDir();
1374
1375
        return [
1376
            0 => [
1377
                [
1378
                    'filter' => new Criterion\SectionId([2]),
1379
                    'offset' => 0,
1380
                    'limit' => 10,
1381
                    'sortClauses' => [],
1382
                ],
1383
                $fixtureDir . 'SortNone.php',
1384
                // Result having the same sort level should be sorted between them to be system independent
1385
                function (&$data) {
1386
                    usort(
1387
                        $data->searchHits,
1388
                        function ($a, $b) {
1389
                            return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1;
1390
                        }
1391
                    );
1392
                },
1393
            ],
1394
            1 => [
1395
                [
1396
                    'filter' => new Criterion\SectionId([2]),
1397
                    'offset' => 0,
1398
                    'limit' => 10,
1399
                    'sortClauses' => [
1400
                        new SortClause\DatePublished(),
1401
                        new SortClause\ContentId(),
1402
                    ],
1403
                ],
1404
                $fixtureDir . 'SortDatePublished.php',
1405
            ],
1406
            2 => [
1407
                [
1408
                    'filter' => new Criterion\SectionId([2]),
1409
                    'offset' => 0,
1410
                    'limit' => 50,
1411
                    'sortClauses' => [
1412
                        new SortClause\DateModified(),
1413
                        new SortClause\ContentId(),
1414
                    ],
1415
                ],
1416
                $fixtureDir . 'SortDateModified.php',
1417
            ],
1418
            3 => [
1419
                [
1420
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1421
                    'offset' => 0,
1422
                    'limit' => 50,
1423
                    'sortClauses' => [
1424
                        new SortClause\SectionIdentifier(),
1425
                        new SortClause\ContentId(),
1426
                    ],
1427
                ],
1428
                $fixtureDir . 'SortSectionIdentifier.php',
1429
            ],
1430
            4 => [
1431
                [
1432
                    'filter' => new Criterion\SectionId([4, 2, 6, 3]),
1433
                    'offset' => 0,
1434
                    'limit' => 50,
1435
                    'sortClauses' => [
1436
                        new SortClause\SectionName(),
1437
                        new SortClause\ContentId(),
1438
                    ],
1439
                ],
1440
                $fixtureDir . 'SortSectionName.php',
1441
            ],
1442
            5 => [
1443
                [
1444
                    'filter' => new Criterion\SectionId([2, 3]),
1445
                    'offset' => 0,
1446
                    'limit' => 50,
1447
                    'sortClauses' => [
1448
                        new SortClause\ContentName(),
1449
                        new SortClause\ContentId(),
1450
                    ],
1451
                ],
1452
                $fixtureDir . 'SortContentName.php',
1453
            ],
1454
            6 => [
1455
                [
1456
                    'filter' => new Criterion\ContentTypeId(1),
1457
                    'offset' => 0,
1458
                    'limit' => 50,
1459
                    'sortClauses' => [
1460
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1461
                        new SortClause\ContentId(),
1462
                    ],
1463
                ],
1464
                $fixtureDir . 'SortFolderName.php',
1465
            ],
1466
            7 => [
1467
                [
1468
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1469
                    'offset' => 0,
1470
                    'limit' => 50,
1471
                    'sortClauses' => [
1472
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1473
                        new SortClause\ContentId(),
1474
                    ],
1475
                ],
1476
                $fixtureDir . 'SortFieldMultipleTypes.php',
1477
            ],
1478
            8 => [
1479
                [
1480
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1481
                    'offset' => 0,
1482
                    'limit' => 50,
1483
                    'sortClauses' => [
1484
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1485
                        new SortClause\ContentId(),
1486
                    ],
1487
                ],
1488
                $fixtureDir . 'SortFieldMultipleTypesReverse.php',
1489
            ],
1490
            9 => [
1491
                [
1492
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1493
                    'offset' => 3,
1494
                    'limit' => 5,
1495
                    'sortClauses' => [
1496
                        new SortClause\Field('folder', 'name', Query::SORT_ASC),
1497
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1498
                        new SortClause\ContentId(),
1499
                    ],
1500
                ],
1501
                $fixtureDir . 'SortFieldMultipleTypesSlice.php',
1502
            ],
1503
            10 => [
1504
                [
1505
                    'filter' => new Criterion\ContentTypeId([1, 3]),
1506
                    'offset' => 3,
1507
                    'limit' => 5,
1508
                    'sortClauses' => [
1509
                        new SortClause\Field('folder', 'name', Query::SORT_DESC),
1510
                        new SortClause\Field('user', 'first_name', Query::SORT_ASC),
1511
                        new SortClause\ContentId(),
1512
                    ],
1513
                ],
1514
                $fixtureDir . 'SortFieldMultipleTypesSliceReverse.php',
1515
            ],
1516
        ];
1517
    }
1518
1519
    public function getSortedLocationSearches()
1520
    {
1521
        $fixtureDir = $this->getFixtureDir();
1522
1523
        return [
1524
            [
1525
                [
1526
                    'filter' => new Criterion\SectionId([2]),
1527
                    'offset' => 0,
1528
                    'limit' => 10,
1529
                    'sortClauses' => [new SortClause\Location\Path(Query::SORT_DESC)],
1530
                ],
1531
                $fixtureDir . 'SortPathString.php',
1532
            ],
1533
            [
1534
                [
1535
                    'filter' => new Criterion\SectionId([2]),
1536
                    'offset' => 0,
1537
                    'limit' => 10,
1538
                    'sortClauses' => [new SortClause\Location\Depth(Query::SORT_ASC)],
1539
                ],
1540
                $fixtureDir . 'SortLocationDepth.php',
1541
                // Result having the same sort level should be sorted between them to be system independent
1542
                function (&$data) {
1543
                    // Result with ids:
1544
                    //     4 has depth = 1
1545
                    //     11, 12, 13, 42, 59 have depth = 2
1546
                    //     10, 14 have depth = 3
1547
                    $map = [
1548
                        4 => 0,
1549
                        11 => 1,
1550
                        12 => 2,
1551
                        13 => 3,
1552
                        42 => 4,
1553
                        59 => 5,
1554
                        10 => 6,
1555
                        14 => 7,
1556
                    ];
1557
                    usort(
1558
                        $data->searchHits,
1559
                        function ($a, $b) use ($map) {
1560
                            return ($map[$a->valueObject['id']] < $map[$b->valueObject['id']]) ? -1 : 1;
1561
                        }
1562
                    );
1563
                },
1564
            ],
1565
            [
1566
                [
1567
                    'filter' => new Criterion\SectionId([3]),
1568
                    'offset' => 0,
1569
                    'limit' => 10,
1570
                    'sortClauses' => [
1571
                        new SortClause\Location\Path(Query::SORT_DESC),
1572
                        new SortClause\ContentName(Query::SORT_ASC),
1573
                    ],
1574
                ],
1575
                $fixtureDir . 'SortMultiple.php',
1576
            ],
1577
            [
1578
                [
1579
                    'filter' => new Criterion\SectionId([2]),
1580
                    'offset' => 0,
1581
                    'limit' => 10,
1582
                    'sortClauses' => [
1583
                        new SortClause\Location\Priority(Query::SORT_DESC),
1584
                        new SortClause\ContentId(),
1585
                    ],
1586
                ],
1587
                $fixtureDir . 'SortDesc.php',
1588
            ],
1589
        ];
1590
    }
1591
1592
    /**
1593
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
1594
     */
1595
    protected function createTestContentType()
1596
    {
1597
        $repository = $this->getRepository();
1598
        $contentTypeService = $repository->getContentTypeService();
1599
1600
        $createStruct = $contentTypeService->newContentTypeCreateStruct('test-type');
1601
        $createStruct->mainLanguageCode = 'eng-GB';
1602
        $createStruct->names = ['eng-GB' => 'Test type'];
1603
        $createStruct->creatorId = 14;
1604
        $createStruct->creationDate = new \DateTime();
1605
1606
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer', 'ezinteger');
1607
        $translatableFieldCreate->names = ['eng-GB' => 'Simple translatable integer field'];
1608
        $translatableFieldCreate->fieldGroup = 'main';
1609
        $translatableFieldCreate->position = 1;
1610
        $translatableFieldCreate->isTranslatable = true;
1611
        $translatableFieldCreate->isSearchable = true;
1612
1613
        $createStruct->addFieldDefinition($translatableFieldCreate);
1614
1615
        $nonTranslatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer2', 'ezinteger');
1616
        $nonTranslatableFieldCreate->names = ['eng-GB' => 'Simple non-translatable integer field'];
1617
        $nonTranslatableFieldCreate->fieldGroup = 'main';
1618
        $nonTranslatableFieldCreate->position = 2;
1619
        $nonTranslatableFieldCreate->isTranslatable = false;
1620
        $nonTranslatableFieldCreate->isSearchable = true;
1621
1622
        $createStruct->addFieldDefinition($nonTranslatableFieldCreate);
1623
1624
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
1625
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
1626
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
1627
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
1628
1629
        return $contentType;
1630
    }
1631
1632
    /**
1633
     * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType
1634
     * @param int $fieldValue11 Value for translatable field in first language
1635
     * @param int $fieldValue12 Value for translatable field in second language
1636
     * @param int $fieldValue2 Value for non translatable field
1637
     * @param string $mainLanguageCode
1638
     * @param bool $alwaysAvailable
1639
     *
1640
     * @return Content
1641
     */
1642
    protected function createMultilingualContent(
1643
        $contentType,
1644
        $fieldValue11 = null,
1645
        $fieldValue12 = null,
1646
        $fieldValue2 = null,
1647
        $mainLanguageCode = 'eng-GB',
1648
        $alwaysAvailable = false
1649
    ) {
1650
        $repository = $this->getRepository();
1651
        $contentService = $repository->getContentService();
1652
1653
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
1654
        $createStruct->alwaysAvailable = $alwaysAvailable;
1655
        $createStruct->mainLanguageCode = $mainLanguageCode;
1656
        if ($fieldValue11) {
1657
            $createStruct->setField('integer', $fieldValue11, 'eng-GB');
1658
        }
1659
        if ($fieldValue12) {
1660
            $createStruct->setField('integer', $fieldValue12, 'ger-DE');
1661
        }
1662
        $createStruct->setField('integer2', $fieldValue2, $mainLanguageCode);
1663
1664
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
1665
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
1666
        $content = $contentService->publishVersion($draft->getVersionInfo());
1667
1668
        $this->refreshSearch($repository);
1669
1670
        return $content;
1671
    }
1672
1673
    public function providerForTestMultilingualFieldSort()
1674
    {
1675
        return [
1676
            0 => [
1677
                [
1678
                    1 => [1, 2, 1],
1679
                    2 => [2, 1, 2],
1680
                    3 => [2, 1, 3],
1681
                    4 => [1, 2, 4],
1682
                ],
1683
                [
1684
                    'languages' => [
1685
                        'eng-GB',
1686
                        'ger-DE',
1687
                    ],
1688
                ],
1689
                [
1690
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1691
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1692
                ],
1693
                /**
1694
                 * Expected order, Value eng-GB, Value ger-DE.
1695
                 *
1696
                 * Content 4, 1, 2, 4
1697
                 * Content 1, 1, 2, 1
1698
                 * Content 3, 2, 1, 3
1699
                 * Content 2, 2, 1, 2
1700
                 */
1701
                [4, 1, 3, 2],
1702
            ],
1703
            1 => [
1704
                [
1705
                    1 => [1, 2, 1],
1706
                    2 => [2, 1, 2],
1707
                    3 => [2, 1, 3],
1708
                    4 => [1, 2, 4],
1709
                ],
1710
                [
1711
                    'languages' => [
1712
                        'ger-DE',
1713
                        'eng-GB',
1714
                    ],
1715
                ],
1716
                [
1717
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1718
                    new SortClause\Field('test-type', 'integer2', Query::SORT_DESC),
1719
                ],
1720
                /**
1721
                 * Expected order, Value eng-GB, Value ger-DE.
1722
                 *
1723
                 * Content 3, 2, 1, 3
1724
                 * Content 2, 2, 1, 2
1725
                 * Content 4, 1, 2, 4
1726
                 * Content 1, 1, 2, 1
1727
                 */
1728
                [3, 2, 4, 1],
1729
            ],
1730
            2 => [
1731
                [
1732
                    1 => [null, 2, null, 'ger-DE'],
1733
                    2 => [3, null, null, 'eng-GB'],
1734
                    3 => [4, null, null, 'eng-GB'],
1735
                    4 => [null, 1, null, 'ger-DE'],
1736
                ],
1737
                [
1738
                    'languages' => [
1739
                        'eng-GB',
1740
                    ],
1741
                ],
1742
                [
1743
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1744
                ],
1745
                /**
1746
                 * Expected order, Value eng-GB, Value ger-DE.
1747
                 *
1748
                 * Content 3, 4, -
1749
                 * Content 2, 3, -
1750
                 */
1751
                [3, 2],
1752
            ],
1753
            3 => [
1754
                [
1755
                    1 => [null, 2, null, 'ger-DE'],
1756
                    2 => [3, null, null, 'eng-GB'],
1757
                    3 => [4, null, null, 'eng-GB'],
1758
                    4 => [null, 1, null, 'ger-DE'],
1759
                ],
1760
                [
1761
                    'languages' => [
1762
                        'ger-DE',
1763
                    ],
1764
                ],
1765
                [
1766
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1767
                ],
1768
                /**
1769
                 * Expected order, Value eng-GB, Value ger-DE.
1770
                 *
1771
                 * Content 1, -, 2
1772
                 * Content 4, -, 1
1773
                 */
1774
                [1, 4],
1775
            ],
1776
            4 => [
1777
                [
1778
                    1 => [null, 2, null, 'ger-DE'],
1779
                    2 => [3, null, null, 'eng-GB'],
1780
                    3 => [4, null, null, 'eng-GB'],
1781
                    4 => [null, 1, null, 'ger-DE'],
1782
                ],
1783
                [
1784
                    'languages' => [
1785
                        'eng-GB',
1786
                        'ger-DE',
1787
                    ],
1788
                ],
1789
                [
1790
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1791
                ],
1792
                /**
1793
                 * Expected order, Value eng-GB, Value ger-DE.
1794
                 *
1795
                 * Content 3, 4, -
1796
                 * Content 2, 3, -
1797
                 * Content 1, -, 2
1798
                 * Content 4, -, 1
1799
                 */
1800
                [3, 2, 1, 4],
1801
            ],
1802
            5 => [
1803
                [
1804
                    1 => [null, 2, null, 'ger-DE'],
1805
                    2 => [3, null, null, 'eng-GB'],
1806
                    3 => [4, null, null, 'eng-GB'],
1807
                    4 => [null, 1, null, 'ger-DE'],
1808
                ],
1809
                [
1810
                    'languages' => [
1811
                        'ger-DE',
1812
                        'eng-GB',
1813
                    ],
1814
                ],
1815
                [
1816
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1817
                ],
1818
                /**
1819
                 * Expected order, Value eng-GB, Value ger-DE.
1820
                 *
1821
                 * Content 3, 4, -
1822
                 * Content 2, 3, -
1823
                 * Content 1, -, 2
1824
                 * Content 4, -, 1
1825
                 */
1826
                [3, 2, 1, 4],
1827
            ],
1828
            6 => [
1829
                [
1830
                    1 => [null, 2, null, 'ger-DE'],
1831
                    2 => [3, 4, null, 'eng-GB'],
1832
                    3 => [4, 3, null, 'eng-GB'],
1833
                    4 => [null, 1, null, 'ger-DE'],
1834
                ],
1835
                [
1836
                    'languages' => [
1837
                        'eng-GB',
1838
                        'ger-DE',
1839
                    ],
1840
                ],
1841
                [
1842
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1843
                ],
1844
                /**
1845
                 * Expected order, Value eng-GB, Value ger-DE.
1846
                 *
1847
                 * Content 3, 4, 3
1848
                 * Content 2, 3, 4
1849
                 * Content 1, -, 2
1850
                 * Content 4, -, 1
1851
                 */
1852
                [3, 2, 1, 4],
1853
            ],
1854
            7 => [
1855
                [
1856
                    1 => [null, 2, null, 'ger-DE'],
1857
                    2 => [3, 4, null, 'eng-GB'],
1858
                    3 => [4, 3, null, 'eng-GB'],
1859
                    4 => [null, 1, null, 'ger-DE'],
1860
                ],
1861
                [
1862
                    'languages' => [
1863
                        'ger-DE',
1864
                        'eng-GB',
1865
                    ],
1866
                ],
1867
                [
1868
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1869
                ],
1870
                /**
1871
                 * Expected order, Value eng-GB, Value ger-DE.
1872
                 *
1873
                 * Content 2, 3, 4
1874
                 * Content 3, 4, 3
1875
                 * Content 1, -, 2
1876
                 * Content 4, -, 1
1877
                 */
1878
                [2, 3, 1, 4],
1879
            ],
1880
            8 => [
1881
                [
1882
                    1 => [null, 1, null, 'ger-DE', true],
1883
                    2 => [4, null, null, 'eng-GB', true],
1884
                    3 => [3, null, null, 'eng-GB', false],
1885
                    4 => [null, 2, null, 'ger-DE', false],
1886
                ],
1887
                [
1888
                    'languages' => [
1889
                        'eng-GB',
1890
                    ],
1891
                ],
1892
                [
1893
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1894
                ],
1895
                /**
1896
                 * Expected order, Value eng-GB, Value ger-DE.
1897
                 *
1898
                 * Content 1, -, 1
1899
                 * Content 3, 3, -
1900
                 * Content 2, 4, -
1901
                 */
1902
                [1, 3, 2],
1903
            ],
1904
            9 => [
1905
                [
1906
                    1 => [null, 1, null, 'ger-DE', true],
1907
                    2 => [4, null, null, 'eng-GB', true],
1908
                    3 => [3, null, null, 'eng-GB', false],
1909
                    4 => [null, 2, null, 'ger-DE', false],
1910
                ],
1911
                [
1912
                    'languages' => [
1913
                        'ger-DE',
1914
                    ],
1915
                ],
1916
                [
1917
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1918
                ],
1919
                /**
1920
                 * Expected order, Value eng-GB, Value ger-DE.
1921
                 *
1922
                 * Content 2, 4, -
1923
                 * Content 4, -, 2
1924
                 * Content 1, -, 1
1925
                 */
1926
                [2, 4, 1],
1927
            ],
1928
            10 => [
1929
                [
1930
                    1 => [null, 1, null, 'ger-DE', true],
1931
                    2 => [4, null, null, 'eng-GB', true],
1932
                    3 => [3, null, null, 'eng-GB', false],
1933
                    4 => [null, 2, null, 'ger-DE', false],
1934
                ],
1935
                [
1936
                    'languages' => [
1937
                        'eng-GB',
1938
                    ],
1939
                    'useAlwaysAvailable' => false,
1940
                ],
1941
                [
1942
                    new SortClause\Field('test-type', 'integer', Query::SORT_ASC),
1943
                ],
1944
                /**
1945
                 * Expected order, Value eng-GB, Value ger-DE.
1946
                 *
1947
                 * Content 3, 3, -
1948
                 * Content 2, 4, -
1949
                 */
1950
                [3, 2],
1951
            ],
1952
            11 => [
1953
                [
1954
                    1 => [null, 1, null, 'ger-DE', true],
1955
                    2 => [4, null, null, 'eng-GB', true],
1956
                    3 => [3, null, null, 'eng-GB', false],
1957
                    4 => [null, 2, null, 'ger-DE', false],
1958
                ],
1959
                [
1960
                    'languages' => [
1961
                        'ger-DE',
1962
                    ],
1963
                    'useAlwaysAvailable' => false,
1964
                ],
1965
                [
1966
                    new SortClause\Field('test-type', 'integer', Query::SORT_DESC),
1967
                ],
1968
                /**
1969
                 * Expected order, Value eng-GB, Value ger-DE.
1970
                 *
1971
                 * Content 4, -, 2
1972
                 * Content 1, -, 1
1973
                 */
1974
                [4, 1],
1975
            ],
1976
        ];
1977
    }
1978
1979
    /**
1980
     * Test for the findContent() method.
1981
     *
1982
     * @group rrr
1983
     * @dataProvider providerForTestMultilingualFieldSort
1984
     *
1985
     * @param array $contentDataList
1986
     * @param array $languageSettings
1987
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
1988
     * @param array $expected
1989
     */
1990
    public function testMultilingualFieldSortContent(
1991
        array $contentDataList,
1992
        $languageSettings,
1993
        array $sortClauses,
1994
        $expected
1995
    ) {
1996
        $this->assertMultilingualFieldSort(
1997
            $contentDataList,
1998
            $languageSettings,
1999
            $sortClauses,
2000
            $expected
2001
        );
2002
    }
2003
2004
    /**
2005
     * Test for the findLocations() method.
2006
     *
2007
     * @group rrr
2008
     * @dataProvider providerForTestMultilingualFieldSort
2009
     *
2010
     * @param array $contentDataList
2011
     * @param array $languageSettings
2012
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2013
     * @param array $expected
2014
     */
2015
    public function testMultilingualFieldSortLocation(
2016
        array $contentDataList,
2017
        $languageSettings,
2018
        array $sortClauses,
2019
        $expected
2020
    ) {
2021
        $this->assertMultilingualFieldSort(
2022
            $contentDataList,
2023
            $languageSettings,
2024
            $sortClauses,
2025
            $expected,
2026
            false
2027
        );
2028
    }
2029
2030
    /**
2031
     * @param array $contentDataList
2032
     * @param array $languageSettings
2033
     * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses
2034
     * @param array $expected
2035
     * @param bool $contentSearch
2036
     */
2037
    protected function assertMultilingualFieldSort(
2038
        array $contentDataList,
2039
        $languageSettings,
2040
        array $sortClauses,
2041
        $expected,
2042
        $contentSearch = true
2043
    ) {
2044
        $contentType = $this->createTestContentType();
2045
2046
        // Create a draft to account for behaviour with ContentType in different states
2047
        $repository = $this->getRepository();
2048
        $contentTypeService = $repository->getContentTypeService();
2049
        $contentTypeService->createContentTypeDraft($contentType);
2050
2051
        $defaults = [null, null, null, 'eng-GB', false];
2052
        $contentIdList = [];
2053
        foreach ($contentDataList as $key => $contentData) {
2054
            $contentData = $contentData + $defaults;
2055
            list(
2056
                $fieldValue11,
2057
                $fieldValue12,
2058
                $fieldValue2,
2059
                $mainLanguageCode,
2060
                $alwaysAvailable
2061
            ) = $contentData;
2062
2063
            $contentIdList[$key] = $this->createMultilingualContent(
2064
                $contentType,
2065
                $fieldValue11,
2066
                $fieldValue12,
2067
                $fieldValue2,
2068
                $mainLanguageCode,
2069
                $alwaysAvailable
2070
            )->id;
2071
        }
2072
2073
        // "article" type Content is not matched, this ensures that non-matched
2074
        // field does not affect sort
2075
        $dummySortClause = new SortClause\Field('article', 'title', Query::SORT_ASC);
2076
        array_unshift($sortClauses, $dummySortClause);
2077
        $sortClauses[] = $dummySortClause;
2078
2079
        $searchService = $repository->getSearchService();
2080
        if ($contentSearch) {
2081
            $query = new Query(
2082
                [
2083
                    'query' => new Criterion\ContentTypeId($contentType->id),
2084
                    'sortClauses' => $sortClauses,
2085
                ]
2086
            );
2087
            $result = $searchService->findContent($query, $languageSettings);
2088
        } else {
2089
            $query = new LocationQuery(
2090
                [
2091
                    'query' => new Criterion\ContentTypeId($contentType->id),
2092
                    'sortClauses' => $sortClauses,
2093
                ]
2094
            );
2095
            $result = $searchService->findLocations($query, $languageSettings);
2096
        }
2097
2098
        $this->assertEquals(count($expected), $result->totalCount);
2099
2100
        $expectedIdList = [];
2101
        foreach ($expected as $contentNumber) {
2102
            $expectedIdList[] = $contentIdList[$contentNumber];
2103
        }
2104
2105
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2106
    }
2107
2108
    public function providerForTestMultilingualFieldFilter()
2109
    {
2110
        return [
2111
            0 => [
2112
                $fixture = [
2113
                    1 => [null, 1, null, 'ger-DE', true],
2114
                    2 => [4, null, null, 'eng-GB', true],
2115
                    3 => [3, null, null, 'eng-GB', false],
2116
                    4 => [null, 2, null, 'ger-DE', false],
2117
                    5 => [5, null, null, 'eng-GB', true],
2118
                ],
2119
                $languageSettings = [
2120
                    'languages' => [
2121
                        'ger-DE',
2122
                    ],
2123
                ],
2124
                new Criterion\Field('integer', Criterion\Operator::LT, 5),
2125
                /**
2126
                 * Expected order, Value eng-GB, Value ger-DE.
2127
                 *
2128
                 * Content 2, 4, -
2129
                 * Content 4, -, 2
2130
                 * Content 1, -, 1
2131
                 */
2132
                [2, 4, 1],
2133
            ],
2134
            1 => [
2135
                $fixture,
2136
                [
2137
                    'languages' => [
2138
                        'ger-DE',
2139
                    ],
2140
                    'useAlwaysAvailable' => false,
2141
                ],
2142
                new Criterion\Field('integer', Criterion\Operator::LT, 2),
2143
                /**
2144
                 * Expected order, Value eng-GB, Value ger-DE.
2145
                 *
2146
                 * Content 1, -, 1
2147
                 */
2148
                [1],
2149
            ],
2150
            2 => [
2151
                $fixture,
2152
                [
2153
                    'languages' => [
2154
                        'eng-GB',
2155
                    ],
2156
                ],
2157
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2158
                /**
2159
                 * Expected order, Value eng-GB, Value ger-DE.
2160
                 *
2161
                 * Content 5, 5, -
2162
                 * Content 2, 4, -
2163
                 * Content 3, 3, -
2164
                 * Content 1, -, 1
2165
                 */
2166
                [2, 3, 1],
2167
            ],
2168
            3 => [
2169
                $fixture,
2170
                [
2171
                    'languages' => [
2172
                        'eng-GB',
2173
                    ],
2174
                    'useAlwaysAvailable' => false,
2175
                ],
2176
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2177
                /**
2178
                 * Expected order, Value eng-GB, Value ger-DE.
2179
                 *
2180
                 * Content 2, 4, -
2181
                 * Content 3, 3, -
2182
                 */
2183
                [2, 3],
2184
            ],
2185
            4 => [
2186
                $fixture,
2187
                $languageSettings,
2188
                new Criterion\Field('integer', Criterion\Operator::LTE, 4),
2189
                /**
2190
                 * Expected order, Value eng-GB, Value ger-DE.
2191
                 *
2192
                 * Content 2, 4, -
2193
                 * Content 4, -, 2
2194
                 * Content 1, -, 1
2195
                 */
2196
                [2, 4, 1],
2197
            ],
2198
            5 => [
2199
                $fixture,
2200
                $languageSettings,
2201
                new Criterion\Field('integer', Criterion\Operator::GT, 1),
2202
                /**
2203
                 * Expected order, Value eng-GB, Value ger-DE.
2204
                 *
2205
                 * Content 5, 5, -
2206
                 * Content 2, 4, -
2207
                 * Content 4, -, 2
2208
                 */
2209
                [5, 2, 4],
2210
            ],
2211
            6 => [
2212
                $fixture,
2213
                $languageSettings,
2214
                new Criterion\Field('integer', Criterion\Operator::GTE, 2),
2215
                /**
2216
                 * Expected order, Value eng-GB, Value ger-DE.
2217
                 *
2218
                 * Content 5, 5, -
2219
                 * Content 2, 4, -
2220
                 * Content 4, -, 2
2221
                 */
2222
                [5, 2, 4],
2223
            ],
2224
            7 => [
2225
                $fixture,
2226
                $languageSettings,
2227
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [2, 4]),
2228
                /**
2229
                 * Expected order, Value eng-GB, Value ger-DE.
2230
                 *
2231
                 * Content 2, 4, -
2232
                 * Content 4, -, 2
2233
                 */
2234
                [2, 4],
2235
            ],
2236
            8 => [
2237
                $fixture,
2238
                $languageSettings,
2239
                new Criterion\Field('integer', Criterion\Operator::BETWEEN, [4, 2]),
2240
                [],
2241
            ],
2242
            9 => [
2243
                $fixture,
2244
                $languageSettings,
2245
                new Criterion\Field('integer', Criterion\Operator::EQ, 4),
2246
                /**
2247
                 * Expected order, Value eng-GB, Value ger-DE.
2248
                 *
2249
                 * Content 4, -, 2
2250
                 */
2251
                [2],
2252
            ],
2253
            10 => [
2254
                $fixture,
2255
                $languageSettings,
2256
                new Criterion\Field('integer', Criterion\Operator::EQ, 2),
2257
                /**
2258
                 * Expected order, Value eng-GB, Value ger-DE.
2259
                 *
2260
                 * Content 2, 4, -
2261
                 */
2262
                [4],
2263
            ],
2264
        ];
2265
    }
2266
2267
    /**
2268
     * Test for the findContent() method.
2269
     *
2270
     * @group ttt
2271
     * @dataProvider providerForTestMultilingualFieldFilter
2272
     *
2273
     * @param array $contentDataList
2274
     * @param array $languageSettings
2275
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2276
     * @param array $expected
2277
     */
2278
    public function testMultilingualFieldFilterContent(
2279
        array $contentDataList,
2280
        $languageSettings,
2281
        Criterion $criterion,
2282
        $expected
2283
    ) {
2284
        $this->assertMultilingualFieldFilter(
2285
            $contentDataList,
2286
            $languageSettings,
2287
            $criterion,
2288
            $expected
2289
        );
2290
    }
2291
2292
    /**
2293
     * Test for the findLocations() method.
2294
     *
2295
     * @group ttt
2296
     * @dataProvider providerForTestMultilingualFieldFilter
2297
     *
2298
     * @param array $contentDataList
2299
     * @param array $languageSettings
2300
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2301
     * @param array $expected
2302
     */
2303
    public function testMultilingualFieldFilterLocation(
2304
        array $contentDataList,
2305
        $languageSettings,
2306
        Criterion $criterion,
2307
        $expected
2308
    ) {
2309
        $this->assertMultilingualFieldFilter(
2310
            $contentDataList,
2311
            $languageSettings,
2312
            $criterion,
2313
            $expected,
2314
            false
2315
        );
2316
    }
2317
2318
    /**
2319
     * @param array $contentDataList
2320
     * @param array $languageSettings
2321
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion
2322
     * @param array $expected
2323
     * @param bool $contentSearch
2324
     */
2325
    protected function assertMultilingualFieldFilter(
2326
        array $contentDataList,
2327
        $languageSettings,
2328
        Criterion $criterion,
2329
        $expected,
2330
        $contentSearch = true
2331
    ) {
2332
        $contentType = $this->createTestContentType();
2333
2334
        // Create a draft to account for behaviour with ContentType in different states
2335
        $repository = $this->getRepository();
2336
        $contentTypeService = $repository->getContentTypeService();
2337
        $contentTypeService->createContentTypeDraft($contentType);
2338
2339
        $defaults = [null, null, null, 'eng-GB', false];
2340
        $contentIdList = [];
2341
        foreach ($contentDataList as $key => $contentData) {
2342
            $contentData = $contentData + $defaults;
2343
            list(
2344
                $fieldValue11,
2345
                $fieldValue12,
2346
                $fieldValue2,
2347
                $mainLanguageCode,
2348
                $alwaysAvailable
2349
            ) = $contentData;
2350
2351
            $contentIdList[$key] = $this->createMultilingualContent(
2352
                $contentType,
2353
                $fieldValue11,
2354
                $fieldValue12,
2355
                $fieldValue2,
2356
                $mainLanguageCode,
2357
                $alwaysAvailable
2358
            )->id;
2359
        }
2360
2361
        $sortClause = new SortClause\Field('test-type', 'integer', Query::SORT_DESC);
2362
        $searchService = $repository->getSearchService();
2363
        if ($contentSearch) {
2364
            $query = new Query(
2365
                [
2366
                    'query' => new Criterion\LogicalAnd(
2367
                        [
2368
                            new Criterion\ContentTypeId($contentType->id),
2369
                            $criterion,
2370
                        ]
2371
                    ),
2372
                    'sortClauses' => [$sortClause],
2373
                ]
2374
            );
2375
            $result = $searchService->findContent($query, $languageSettings);
2376
        } else {
2377
            $query = new LocationQuery(
2378
                [
2379
                    'query' => new Criterion\LogicalAnd(
2380
                        [
2381
                            new Criterion\ContentTypeId($contentType->id),
2382
                            $criterion,
2383
                        ]
2384
                    ),
2385
                    'sortClauses' => [$sortClause],
2386
                ]
2387
            );
2388
            $result = $searchService->findLocations($query, $languageSettings);
2389
        }
2390
2391
        $this->assertEquals(count($expected), $result->totalCount);
2392
2393
        $expectedIdList = [];
2394
        foreach ($expected as $contentNumber) {
2395
            $expectedIdList[] = $contentIdList[$contentNumber];
2396
        }
2397
2398
        $this->assertEquals($expectedIdList, $this->mapResultContentIds($result));
2399
    }
2400
2401
    /**
2402
     * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result
2403
     *
2404
     * @return array
2405
     */
2406
    protected function mapResultContentIds(SearchResult $result)
2407
    {
2408
        return array_map(
2409
            function (SearchHit $searchHit) {
2410
                if ($searchHit->valueObject instanceof Location) {
2411
                    return $searchHit->valueObject->contentInfo->id;
2412
                }
2413
2414
                return $searchHit->valueObject->id;
2415
            },
2416
            $result->searchHits
2417
        );
2418
    }
2419
2420
    /**
2421
     * Test for the findContent() method.
2422
     *
2423
     * @dataProvider getSortedContentSearches
2424
     *
2425
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2426
     */
2427
    public function testFindAndSortContent($queryData, $fixture, $closure = null)
2428
    {
2429
        $query = new Query($queryData);
2430
        $this->assertQueryFixture($query, $fixture, $closure);
2431
    }
2432
2433
    /**
2434
     * Test for the findContentInfo() method.
2435
     *
2436
     * @dataProvider getSortedContentSearches
2437
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2438
     */
2439
    public function testFindAndSortContentInfo($queryData, $fixture, $closure = null)
2440
    {
2441
        $query = new Query($queryData);
2442
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true);
2443
    }
2444
2445
    /**
2446
     * Test for the findLocations() method.
2447
     *
2448
     * @dataProvider getSortedContentSearches
2449
     *
2450
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2451
     */
2452
    public function testFindAndSortContentLocations($queryData, $fixture, $closure = null)
2453
    {
2454
        $query = new LocationQuery($queryData);
2455
        $this->assertQueryFixture($query, $fixture, $closure);
2456
    }
2457
2458
    /**
2459
     * Test for the findLocations() method.
2460
     *
2461
     * @dataProvider getSortedLocationSearches
2462
     *
2463
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
2464
     */
2465
    public function testFindAndSortLocations($queryData, $fixture, $closure = null)
2466
    {
2467
        $query = new LocationQuery($queryData);
2468
        $this->assertQueryFixture($query, $fixture, $closure);
2469
    }
2470
2471
    /**
2472
     * Test for the findContent() method.
2473
     *
2474
     * @dataProvider getFacetedSearches
2475
     *
2476
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2477
     */
2478
    public function testFindFacetedContent(Query $query, $fixture)
2479
    {
2480
        $this->assertQueryFixture($query, $fixture);
2481
    }
2482
2483
    /**
2484
     * Test for the findContentInfo() method.
2485
     *
2486
     * @dataProvider getFacetedSearches
2487
     * @see \eZ\Publish\API\Repository\SearchService::findContentInfo()
2488
     */
2489
    public function testFindFacetedContentInfo(Query $query, $fixture)
2490
    {
2491
        $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure(), true);
2492
    }
2493
2494
    /**
2495
     * Test for the findContent() method.
2496
     *
2497
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2498
     */
2499
    public function testQueryCustomField()
2500
    {
2501
        $query = new Query(
2502
            [
2503
                'query' => new Criterion\CustomField(
2504
                    'custom_field',
2505
                    Criterion\Operator::EQ,
2506
                    'AdMiNiStRaToR'
2507
                ),
2508
                'offset' => 0,
2509
                'limit' => 10,
2510
                'sortClauses' => [new SortClause\ContentId()],
2511
            ]
2512
        );
2513
        $this->assertQueryFixture(
2514
            $query,
2515
            $this->getFixtureDir() . '/QueryCustomField.php'
2516
        );
2517
    }
2518
2519
    /**
2520
     * Test for the findContent() method.
2521
     *
2522
     * This tests explicitely queries the first_name while user is contained in
2523
     * the last_name of admin and anonymous. This is done to show the custom
2524
     * copy field working.
2525
     *
2526
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2527
     */
2528
    public function testQueryModifiedField()
2529
    {
2530
        // Check using get_class since the others extend SetupFactory\Legacy
2531
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2532
            $this->markTestIncomplete(
2533
                'Custom fields not supported by LegacySE ' .
2534
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2535
            );
2536
        }
2537
2538
        $query = new Query(
2539
            [
2540
                'query' => new Criterion\Field(
2541
                    'first_name',
2542
                    Criterion\Operator::EQ,
2543
                    'User'
2544
                ),
2545
                'offset' => 0,
2546
                'limit' => 10,
2547
                'sortClauses' => [new SortClause\ContentId()],
2548
            ]
2549
        );
2550
        $query->query->setCustomField('user', 'first_name', 'custom_field');
2551
2552
        $this->assertQueryFixture(
2553
            $query,
2554
            $this->getFixtureDir() . '/QueryModifiedField.php'
2555
        );
2556
    }
2557
2558
    /**
2559
     * Test for the findContent() method.
2560
     *
2561
     * This tests first explicitly creates sort clause on the 'short_name' which is empty
2562
     * for all Content instances of 'folder' ContentType. Custom sort field is then set
2563
     * to the index storage name of folder's 'name' field, in order to show the custom
2564
     * sort field working.
2565
     *
2566
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2567
     */
2568
    public function testSortModifiedField()
2569
    {
2570
        // Check using get_class since the others extend SetupFactory\Legacy
2571
        if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
2572
            $this->markTestIncomplete(
2573
                'Custom field sort not supported by LegacySE ' .
2574
                '(@todo: Legacy should fallback to just querying normal field so this should be tested here)'
2575
            );
2576
        }
2577
2578
        $sortClause = new SortClause\Field('folder', 'short_name', Query::SORT_ASC);
2579
        $sortClause->setCustomField('folder', 'short_name', 'folder_name_value_s');
2580
2581
        $query = new Query(
2582
            [
2583
                'filter' => new Criterion\ContentTypeId(1),
2584
                'offset' => 0,
2585
                'limit' => 10,
2586
                'sortClauses' => [
2587
                    $sortClause,
2588
                    new SortClause\ContentId(),
2589
                ],
2590
            ]
2591
        );
2592
2593
        $this->assertQueryFixture(
2594
            $query,
2595
            $this->getFixtureDir() . '/SortFolderName.php'
2596
        );
2597
    }
2598
2599
    /**
2600
     * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType
2601
     */
2602
    protected function createTestPlaceContentType()
2603
    {
2604
        $repository = $this->getRepository();
2605
        $contentTypeService = $repository->getContentTypeService();
2606
2607
        $createStruct = $contentTypeService->newContentTypeCreateStruct('testtype');
2608
        $createStruct->mainLanguageCode = 'eng-GB';
2609
        $createStruct->names = ['eng-GB' => 'Test type'];
2610
        $createStruct->creatorId = 14;
2611
        $createStruct->creationDate = new \DateTime();
2612
2613
        $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('maplocation', 'ezgmaplocation');
2614
        $translatableFieldCreate->names = ['eng-GB' => 'Map location field'];
2615
        $translatableFieldCreate->fieldGroup = 'main';
2616
        $translatableFieldCreate->position = 1;
2617
        $translatableFieldCreate->isTranslatable = false;
2618
        $translatableFieldCreate->isSearchable = true;
2619
2620
        $createStruct->addFieldDefinition($translatableFieldCreate);
2621
2622
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
2623
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
2624
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
2625
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
2626
2627
        return $contentType;
2628
    }
2629
2630
    /**
2631
     * Test for the findContent() method.
2632
     *
2633
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2634
     * @group maplocation
2635
     */
2636
    public function testMapLocationDistanceLessThanOrEqual()
2637
    {
2638
        $contentType = $this->createTestPlaceContentType();
2639
2640
        // Create a draft to account for behaviour with ContentType in different states
2641
        $repository = $this->getRepository();
2642
        $contentTypeService = $repository->getContentTypeService();
2643
        $contentService = $repository->getContentService();
2644
        $contentTypeService->createContentTypeDraft($contentType);
2645
2646
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2647
        $createStruct->alwaysAvailable = false;
2648
        $createStruct->mainLanguageCode = 'eng-GB';
2649
        $createStruct->setField(
2650
            'maplocation',
2651
            [
2652
                'latitude' => 45.894877,
2653
                'longitude' => 15.972699,
2654
                'address' => 'Here be wild boars',
2655
            ],
2656
            'eng-GB'
2657
        );
2658
2659
        $draft = $contentService->createContent($createStruct);
2660
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2661
2662
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2663
        $createStruct->alwaysAvailable = false;
2664
        $createStruct->mainLanguageCode = 'eng-GB';
2665
        $createStruct->setField(
2666
            'maplocation',
2667
            [
2668
                'latitude' => 45.927334,
2669
                'longitude' => 15.934847,
2670
                'address' => 'A lone tree',
2671
            ],
2672
            'eng-GB'
2673
        );
2674
2675
        $draft = $contentService->createContent($createStruct);
2676
        $tree = $contentService->publishVersion($draft->getVersionInfo());
2677
2678
        $this->refreshSearch($repository);
2679
2680
        $query = new Query(
2681
            [
2682
                'filter' => new Criterion\LogicalAnd(
2683
                    [
2684
                        new Criterion\ContentTypeId($contentType->id),
2685
                        new Criterion\MapLocationDistance(
2686
                            'maplocation',
2687
                            Criterion\Operator::LTE,
2688
                            240,
2689
                            43.756825,
2690
                            15.775074
2691
                        ),
2692
                    ]
2693
                ),
2694
                'offset' => 0,
2695
                'limit' => 10,
2696
                'sortClauses' => [],
2697
            ]
2698
        );
2699
2700
        $searchService = $repository->getSearchService();
2701
        $result = $searchService->findContent($query);
2702
2703
        $this->assertEquals(1, $result->totalCount);
2704
        $this->assertEquals(
2705
            $wildBoars->id,
2706
            $result->searchHits[0]->valueObject->id
2707
        );
2708
    }
2709
2710
    /**
2711
     * Test for the findContent() method.
2712
     *
2713
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2714
     * @group maplocation
2715
     */
2716
    public function testMapLocationDistanceGreaterThanOrEqual()
2717
    {
2718
        $contentType = $this->createTestPlaceContentType();
2719
2720
        // Create a draft to account for behaviour with ContentType in different states
2721
        $repository = $this->getRepository();
2722
        $contentTypeService = $repository->getContentTypeService();
2723
        $contentService = $repository->getContentService();
2724
        $contentTypeService->createContentTypeDraft($contentType);
2725
2726
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2727
        $createStruct->alwaysAvailable = false;
2728
        $createStruct->mainLanguageCode = 'eng-GB';
2729
        $createStruct->setField(
2730
            'maplocation',
2731
            [
2732
                'latitude' => 45.894877,
2733
                'longitude' => 15.972699,
2734
                'address' => 'Here be wild boars',
2735
            ],
2736
            'eng-GB'
2737
        );
2738
2739
        $draft = $contentService->createContent($createStruct);
2740
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2741
2742
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2743
        $createStruct->alwaysAvailable = false;
2744
        $createStruct->mainLanguageCode = 'eng-GB';
2745
        $createStruct->setField(
2746
            'maplocation',
2747
            [
2748
                'latitude' => 45.927334,
2749
                'longitude' => 15.934847,
2750
                'address' => 'A lone tree',
2751
            ],
2752
            'eng-GB'
2753
        );
2754
2755
        $draft = $contentService->createContent($createStruct);
2756
        $tree = $contentService->publishVersion($draft->getVersionInfo());
2757
2758
        $this->refreshSearch($repository);
2759
2760
        $query = new Query(
2761
            [
2762
                'filter' => new Criterion\LogicalAnd(
2763
                    [
2764
                        new Criterion\ContentTypeId($contentType->id),
2765
                        new Criterion\MapLocationDistance(
2766
                            'maplocation',
2767
                            Criterion\Operator::GTE,
2768
                            240,
2769
                            43.756825,
2770
                            15.775074
2771
                        ),
2772
                    ]
2773
                ),
2774
                'offset' => 0,
2775
                'limit' => 10,
2776
                'sortClauses' => [],
2777
            ]
2778
        );
2779
2780
        $searchService = $repository->getSearchService();
2781
        $result = $searchService->findContent($query);
2782
2783
        $this->assertEquals(1, $result->totalCount);
2784
        $this->assertEquals(
2785
            $tree->id,
2786
            $result->searchHits[0]->valueObject->id
2787
        );
2788
    }
2789
2790
    /**
2791
     * Test for the findContent() method.
2792
     *
2793
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2794
     * @group maplocation
2795
     */
2796
    public function testMapLocationDistanceBetween()
2797
    {
2798
        $contentType = $this->createTestPlaceContentType();
2799
2800
        // Create a draft to account for behaviour with ContentType in different states
2801
        $repository = $this->getRepository();
2802
        $contentTypeService = $repository->getContentTypeService();
2803
        $contentService = $repository->getContentService();
2804
        $contentTypeService->createContentTypeDraft($contentType);
2805
2806
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2807
        $createStruct->alwaysAvailable = false;
2808
        $createStruct->mainLanguageCode = 'eng-GB';
2809
        $createStruct->setField(
2810
            'maplocation',
2811
            [
2812
                'latitude' => 45.894877,
2813
                'longitude' => 15.972699,
2814
                'address' => 'Here be wild boars',
2815
            ],
2816
            'eng-GB'
2817
        );
2818
2819
        $draft = $contentService->createContent($createStruct);
2820
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2821
2822
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2823
        $createStruct->alwaysAvailable = false;
2824
        $createStruct->mainLanguageCode = 'eng-GB';
2825
        $createStruct->setField(
2826
            'maplocation',
2827
            [
2828
                'latitude' => 45.927334,
2829
                'longitude' => 15.934847,
2830
                'address' => 'A lone tree',
2831
            ],
2832
            'eng-GB'
2833
        );
2834
2835
        $draft = $contentService->createContent($createStruct);
2836
        $tree = $contentService->publishVersion($draft->getVersionInfo());
2837
2838
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2839
        $createStruct->alwaysAvailable = false;
2840
        $createStruct->mainLanguageCode = 'eng-GB';
2841
        $createStruct->setField(
2842
            'maplocation',
2843
            [
2844
                'latitude' => 45.903777,
2845
                'longitude' => 15.958788,
2846
                'address' => 'Meadow with mushrooms',
2847
            ],
2848
            'eng-GB'
2849
        );
2850
2851
        $draft = $contentService->createContent($createStruct);
2852
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
2853
2854
        $this->refreshSearch($repository);
2855
2856
        $query = new Query(
2857
            [
2858
                'filter' => new Criterion\LogicalAnd(
2859
                    [
2860
                        new Criterion\ContentTypeId($contentType->id),
2861
                        new Criterion\MapLocationDistance(
2862
                            'maplocation',
2863
                            Criterion\Operator::BETWEEN,
2864
                            [239, 241],
2865
                            43.756825,
2866
                            15.775074
2867
                        ),
2868
                    ]
2869
                ),
2870
                'offset' => 0,
2871
                'limit' => 10,
2872
                'sortClauses' => [],
2873
            ]
2874
        );
2875
2876
        $searchService = $repository->getSearchService();
2877
        $result = $searchService->findContent($query);
2878
2879
        $this->assertEquals(1, $result->totalCount);
2880
        $this->assertEquals(
2881
            $mushrooms->id,
2882
            $result->searchHits[0]->valueObject->id
2883
        );
2884
    }
2885
2886
    /**
2887
     * Test for the findContent() method.
2888
     *
2889
     * This tests the distance over the pole. The tests intentionally uses large range,
2890
     * as the flat Earth model used in Legacy Storage Search is not precise for the use case.
2891
     * What is tested here is that outer bounding box is correctly calculated, so that
2892
     * location is not excluded.
2893
     *
2894
     * Range between 222km and 350km shows the magnitude of error between great-circle
2895
     * (always very precise) and flat Earth (very imprecise for this use case) models.
2896
     *
2897
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2898
     * @group maplocation
2899
     */
2900
    public function testMapLocationDistanceBetweenPolar()
2901
    {
2902
        $contentType = $this->createTestPlaceContentType();
2903
2904
        // Create a draft to account for behaviour with ContentType in different states
2905
        $repository = $this->getRepository();
2906
        $contentTypeService = $repository->getContentTypeService();
2907
        $contentService = $repository->getContentService();
2908
        $contentTypeService->createContentTypeDraft($contentType);
2909
2910
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2911
        $createStruct->alwaysAvailable = false;
2912
        $createStruct->mainLanguageCode = 'eng-GB';
2913
        $createStruct->setField(
2914
            'maplocation',
2915
            [
2916
                'latitude' => 89,
2917
                'longitude' => -164,
2918
                'address' => 'Polar bear media tower',
2919
            ],
2920
            'eng-GB'
2921
        );
2922
2923
        $draft = $contentService->createContent($createStruct);
2924
        $polarBear = $contentService->publishVersion($draft->getVersionInfo());
2925
2926
        $this->refreshSearch($repository);
2927
2928
        $query = new Query(
2929
            [
2930
                'filter' => new Criterion\LogicalAnd(
2931
                    [
2932
                        new Criterion\ContentTypeId($contentType->id),
2933
                        new Criterion\MapLocationDistance(
2934
                            'maplocation',
2935
                            Criterion\Operator::BETWEEN,
2936
                            [221, 350],
2937
                            89,
2938
                            16
2939
                        ),
2940
                    ]
2941
                ),
2942
                'offset' => 0,
2943
                'limit' => 10,
2944
                'sortClauses' => [],
2945
            ]
2946
        );
2947
2948
        $searchService = $repository->getSearchService();
2949
        $result = $searchService->findContent($query);
2950
2951
        $this->assertEquals(1, $result->totalCount);
2952
        $this->assertEquals(
2953
            $polarBear->id,
2954
            $result->searchHits[0]->valueObject->id
2955
        );
2956
    }
2957
2958
    /**
2959
     * Test for the findContent() method.
2960
     *
2961
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
2962
     * @group maplocation
2963
     */
2964
    public function testMapLocationDistanceSortAscending()
2965
    {
2966
        $contentType = $this->createTestPlaceContentType();
2967
2968
        // Create a draft to account for behaviour with ContentType in different states
2969
        $repository = $this->getRepository();
2970
        $contentTypeService = $repository->getContentTypeService();
2971
        $contentService = $repository->getContentService();
2972
        $contentTypeService->createContentTypeDraft($contentType);
2973
2974
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2975
        $createStruct->alwaysAvailable = false;
2976
        $createStruct->mainLanguageCode = 'eng-GB';
2977
        $createStruct->setField(
2978
            'maplocation',
2979
            [
2980
                'latitude' => 45.894877,
2981
                'longitude' => 15.972699,
2982
                'address' => 'Here be wild boars',
2983
            ],
2984
            'eng-GB'
2985
        );
2986
2987
        $draft = $contentService->createContent($createStruct);
2988
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
2989
2990
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
2991
        $createStruct->alwaysAvailable = false;
2992
        $createStruct->mainLanguageCode = 'eng-GB';
2993
        $createStruct->setField(
2994
            'maplocation',
2995
            [
2996
                'latitude' => 45.927334,
2997
                'longitude' => 15.934847,
2998
                'address' => 'A lone tree',
2999
            ],
3000
            'eng-GB'
3001
        );
3002
3003
        $draft = $contentService->createContent($createStruct);
3004
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3005
3006
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3007
        $createStruct->alwaysAvailable = false;
3008
        $createStruct->mainLanguageCode = 'eng-GB';
3009
        $createStruct->setField(
3010
            'maplocation',
3011
            [
3012
                'latitude' => 45.903777,
3013
                'longitude' => 15.958788,
3014
                'address' => 'Meadow with mushrooms',
3015
            ],
3016
            'eng-GB'
3017
        );
3018
3019
        $draft = $contentService->createContent($createStruct);
3020
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3021
3022
        $this->refreshSearch($repository);
3023
3024
        $wellInVodice = [
3025
            'latitude' => 43.756825,
3026
            'longitude' => 15.775074,
3027
        ];
3028
3029
        $query = new Query(
3030
            [
3031
                'filter' => new Criterion\LogicalAnd(
3032
                    [
3033
                        new Criterion\ContentTypeId($contentType->id),
3034
                        new Criterion\MapLocationDistance(
3035
                            'maplocation',
3036
                            Criterion\Operator::GTE,
3037
                            235,
3038
                            $wellInVodice['latitude'],
3039
                            $wellInVodice['longitude']
3040
                        ),
3041
                    ]
3042
                ),
3043
                'offset' => 0,
3044
                'limit' => 10,
3045
                'sortClauses' => [
3046
                    new SortClause\MapLocationDistance(
3047
                        'testtype',
3048
                        'maplocation',
3049
                        $wellInVodice['latitude'],
3050
                        $wellInVodice['longitude'],
3051
                        Query::SORT_ASC
3052
                    ),
3053
                ],
3054
            ]
3055
        );
3056
3057
        $searchService = $repository->getSearchService();
3058
        $result = $searchService->findContent($query);
3059
3060
        $this->assertEquals(3, $result->totalCount);
3061
        $this->assertEquals(
3062
            $wildBoars->id,
3063
            $result->searchHits[0]->valueObject->id
3064
        );
3065
        $this->assertEquals(
3066
            $mushrooms->id,
3067
            $result->searchHits[1]->valueObject->id
3068
        );
3069
        $this->assertEquals(
3070
            $tree->id,
3071
            $result->searchHits[2]->valueObject->id
3072
        );
3073
    }
3074
3075
    /**
3076
     * Test for the findContent() method.
3077
     *
3078
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3079
     * @group maplocation
3080
     */
3081
    public function testMapLocationDistanceSortDescending()
3082
    {
3083
        $contentType = $this->createTestPlaceContentType();
3084
3085
        // Create a draft to account for behaviour with ContentType in different states
3086
        $repository = $this->getRepository();
3087
        $contentTypeService = $repository->getContentTypeService();
3088
        $contentService = $repository->getContentService();
3089
        $contentTypeService->createContentTypeDraft($contentType);
3090
3091
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3092
        $createStruct->alwaysAvailable = false;
3093
        $createStruct->mainLanguageCode = 'eng-GB';
3094
        $createStruct->setField(
3095
            'maplocation',
3096
            [
3097
                'latitude' => 45.894877,
3098
                'longitude' => 15.972699,
3099
                'address' => 'Here be wild boars',
3100
            ],
3101
            'eng-GB'
3102
        );
3103
3104
        $draft = $contentService->createContent($createStruct);
3105
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3106
3107
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3108
        $createStruct->alwaysAvailable = false;
3109
        $createStruct->mainLanguageCode = 'eng-GB';
3110
        $createStruct->setField(
3111
            'maplocation',
3112
            [
3113
                'latitude' => 45.927334,
3114
                'longitude' => 15.934847,
3115
                'address' => 'A lone tree',
3116
            ],
3117
            'eng-GB'
3118
        );
3119
3120
        $draft = $contentService->createContent($createStruct);
3121
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3122
3123
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3124
        $createStruct->alwaysAvailable = false;
3125
        $createStruct->mainLanguageCode = 'eng-GB';
3126
        $createStruct->setField(
3127
            'maplocation',
3128
            [
3129
                'latitude' => 45.903777,
3130
                'longitude' => 15.958788,
3131
                'address' => 'Meadow with mushrooms',
3132
            ],
3133
            'eng-GB'
3134
        );
3135
3136
        $draft = $contentService->createContent($createStruct);
3137
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3138
3139
        $this->refreshSearch($repository);
3140
3141
        $well = [
3142
            'latitude' => 43.756825,
3143
            'longitude' => 15.775074,
3144
        ];
3145
3146
        $query = new Query(
3147
            [
3148
                'filter' => new Criterion\LogicalAnd(
3149
                    [
3150
                        new Criterion\ContentTypeId($contentType->id),
3151
                        new Criterion\MapLocationDistance(
3152
                            'maplocation',
3153
                            Criterion\Operator::GTE,
3154
                            235,
3155
                            $well['latitude'],
3156
                            $well['longitude']
3157
                        ),
3158
                    ]
3159
                ),
3160
                'offset' => 0,
3161
                'limit' => 10,
3162
                'sortClauses' => [
3163
                    new SortClause\MapLocationDistance(
3164
                        'testtype',
3165
                        'maplocation',
3166
                        $well['latitude'],
3167
                        $well['longitude'],
3168
                        Query::SORT_DESC
3169
                    ),
3170
                ],
3171
            ]
3172
        );
3173
3174
        $searchService = $repository->getSearchService();
3175
        $result = $searchService->findContent($query);
3176
3177
        $this->assertEquals(3, $result->totalCount);
3178
        $this->assertEquals(
3179
            $wildBoars->id,
3180
            $result->searchHits[2]->valueObject->id
3181
        );
3182
        $this->assertEquals(
3183
            $mushrooms->id,
3184
            $result->searchHits[1]->valueObject->id
3185
        );
3186
        $this->assertEquals(
3187
            $tree->id,
3188
            $result->searchHits[0]->valueObject->id
3189
        );
3190
    }
3191
3192
    /**
3193
     * Test for the findContent() method.
3194
     *
3195
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3196
     * @group maplocation
3197
     */
3198
    public function testMapLocationDistanceWithCustomField()
3199
    {
3200
        $contentType = $this->createTestPlaceContentType();
3201
3202
        // Create a draft to account for behaviour with ContentType in different states
3203
        $repository = $this->getRepository();
3204
        $contentTypeService = $repository->getContentTypeService();
3205
        $contentService = $repository->getContentService();
3206
        $contentTypeService->createContentTypeDraft($contentType);
3207
3208
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3209
        $createStruct->alwaysAvailable = false;
3210
        $createStruct->mainLanguageCode = 'eng-GB';
3211
        $createStruct->setField(
3212
            'maplocation',
3213
            [
3214
                'latitude' => 45.894877,
3215
                'longitude' => 15.972699,
3216
                'address' => 'Here be wild boars',
3217
            ],
3218
            'eng-GB'
3219
        );
3220
3221
        $draft = $contentService->createContent($createStruct);
3222
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3223
3224
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3225
        $createStruct->alwaysAvailable = false;
3226
        $createStruct->mainLanguageCode = 'eng-GB';
3227
        $createStruct->setField(
3228
            'maplocation',
3229
            [
3230
                'latitude' => 45.927334,
3231
                'longitude' => 15.934847,
3232
                'address' => 'A lone tree',
3233
            ],
3234
            'eng-GB'
3235
        );
3236
3237
        $draft = $contentService->createContent($createStruct);
3238
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3239
3240
        $this->refreshSearch($repository);
3241
3242
        $distanceCriterion = new Criterion\MapLocationDistance(
3243
            'maplocation',
3244
            Criterion\Operator::LTE,
3245
            240,
3246
            43.756825,
3247
            15.775074
3248
        );
3249
        $distanceCriterion->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3250
3251
        $query = new Query(
3252
            [
3253
                'filter' => new Criterion\LogicalAnd(
3254
                    [
3255
                        new Criterion\ContentTypeId($contentType->id),
3256
                        $distanceCriterion,
3257
                    ]
3258
                ),
3259
                'offset' => 0,
3260
                'limit' => 10,
3261
                'sortClauses' => [],
3262
            ]
3263
        );
3264
3265
        $searchService = $repository->getSearchService();
3266
        $result = $searchService->findContent($query);
3267
3268
        $this->assertEquals(1, $result->totalCount);
3269
        $this->assertEquals(
3270
            $wildBoars->id,
3271
            $result->searchHits[0]->valueObject->id
3272
        );
3273
    }
3274
3275
    /**
3276
     * Test for the findContent() method.
3277
     *
3278
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3279
     * @group maplocation
3280
     */
3281
    public function testMapLocationDistanceWithCustomFieldSort()
3282
    {
3283
        $contentType = $this->createTestPlaceContentType();
3284
3285
        // Create a draft to account for behaviour with ContentType in different states
3286
        $repository = $this->getRepository();
3287
        $contentTypeService = $repository->getContentTypeService();
3288
        $contentService = $repository->getContentService();
3289
        $contentTypeService->createContentTypeDraft($contentType);
3290
3291
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3292
        $createStruct->alwaysAvailable = false;
3293
        $createStruct->mainLanguageCode = 'eng-GB';
3294
        $createStruct->setField(
3295
            'maplocation',
3296
            [
3297
                'latitude' => 45.894877,
3298
                'longitude' => 15.972699,
3299
                'address' => 'Here be wild boars',
3300
            ],
3301
            'eng-GB'
3302
        );
3303
3304
        $draft = $contentService->createContent($createStruct);
3305
        $wildBoars = $contentService->publishVersion($draft->getVersionInfo());
3306
3307
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3308
        $createStruct->alwaysAvailable = false;
3309
        $createStruct->mainLanguageCode = 'eng-GB';
3310
        $createStruct->setField(
3311
            'maplocation',
3312
            [
3313
                'latitude' => 45.927334,
3314
                'longitude' => 15.934847,
3315
                'address' => 'A lone tree',
3316
            ],
3317
            'eng-GB'
3318
        );
3319
3320
        $draft = $contentService->createContent($createStruct);
3321
        $tree = $contentService->publishVersion($draft->getVersionInfo());
3322
3323
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3324
        $createStruct->alwaysAvailable = false;
3325
        $createStruct->mainLanguageCode = 'eng-GB';
3326
        $createStruct->setField(
3327
            'maplocation',
3328
            [
3329
                'latitude' => 45.903777,
3330
                'longitude' => 15.958788,
3331
                'address' => 'Meadow with mushrooms',
3332
            ],
3333
            'eng-GB'
3334
        );
3335
3336
        $draft = $contentService->createContent($createStruct);
3337
        $mushrooms = $contentService->publishVersion($draft->getVersionInfo());
3338
3339
        $this->refreshSearch($repository);
3340
3341
        $well = [
3342
            'latitude' => 43.756825,
3343
            'longitude' => 15.775074,
3344
        ];
3345
3346
        $sortClause = new SortClause\MapLocationDistance(
3347
            'testtype',
3348
            'maplocation',
3349
            $well['latitude'],
3350
            $well['longitude'],
3351
            Query::SORT_DESC
3352
        );
3353
        $sortClause->setCustomField('testtype', 'maplocation', 'custom_geolocation_field');
3354
3355
        $query = new Query(
3356
            [
3357
                'filter' => new Criterion\LogicalAnd(
3358
                    [
3359
                        new Criterion\ContentTypeId($contentType->id),
3360
                        new Criterion\MapLocationDistance(
3361
                            'maplocation',
3362
                            Criterion\Operator::GTE,
3363
                            235,
3364
                            $well['latitude'],
3365
                            $well['longitude']
3366
                        ),
3367
                    ]
3368
                ),
3369
                'offset' => 0,
3370
                'limit' => 10,
3371
                'sortClauses' => [
3372
                    $sortClause,
3373
                ],
3374
            ]
3375
        );
3376
3377
        $searchService = $repository->getSearchService();
3378
        $result = $searchService->findContent($query);
3379
3380
        $this->assertEquals(3, $result->totalCount);
3381
        $this->assertEquals(
3382
            $wildBoars->id,
3383
            $result->searchHits[2]->valueObject->id
3384
        );
3385
        $this->assertEquals(
3386
            $mushrooms->id,
3387
            $result->searchHits[1]->valueObject->id
3388
        );
3389
        $this->assertEquals(
3390
            $tree->id,
3391
            $result->searchHits[0]->valueObject->id
3392
        );
3393
    }
3394
3395
    /**
3396
     * Test for the findLocations() method.
3397
     *
3398
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3399
     */
3400
    public function testFindMainLocation()
3401
    {
3402
        $plainSiteLocationId = 56;
3403
        $designLocationId = 58;
3404
        $partnersContentId = 59;
3405
        $repository = $this->getRepository();
3406
        $locationService = $repository->getLocationService();
3407
        $contentService = $repository->getContentService();
3408
3409
        // Add secondary Location for "Partners" user group, under "Design" page
3410
        $locationService->createLocation(
3411
            $contentService->loadContentInfo($partnersContentId),
3412
            $locationService->newLocationCreateStruct($designLocationId)
3413
        );
3414
3415
        $this->refreshSearch($repository);
3416
3417
        $query = new LocationQuery(
3418
            [
3419
                'filter' => new Criterion\LogicalAnd(
3420
                    [
3421
                        new Criterion\ParentLocationId($designLocationId),
3422
                        new Criterion\Location\IsMainLocation(
3423
                            Criterion\Location\IsMainLocation::MAIN
3424
                        ),
3425
                    ]
3426
                ),
3427
                'offset' => 0,
3428
                'limit' => 10,
3429
                'sortClauses' => [],
3430
            ]
3431
        );
3432
3433
        $searchService = $repository->getSearchService();
3434
        $result = $searchService->findLocations($query);
3435
3436
        $this->assertEquals(1, $result->totalCount);
3437
        $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id);
3438
    }
3439
3440
    /**
3441
     * Test for the findLocations() method.
3442
     *
3443
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3444
     */
3445
    public function testFindNonMainLocation()
3446
    {
3447
        $designLocationId = 58;
3448
        $partnersContentId = 59;
3449
        $repository = $this->getRepository();
3450
        $locationService = $repository->getLocationService();
3451
        $contentService = $repository->getContentService();
3452
3453
        // Add secondary Location for "Partners" user group, under "Design" page
3454
        $newLocation = $locationService->createLocation(
3455
            $contentService->loadContentInfo($partnersContentId),
3456
            $locationService->newLocationCreateStruct($designLocationId)
3457
        );
3458
3459
        $this->refreshSearch($repository);
3460
3461
        $query = new LocationQuery(
3462
            [
3463
                'filter' => new Criterion\LogicalAnd(
3464
                    [
3465
                        new Criterion\ParentLocationId($designLocationId),
3466
                        new Criterion\Location\IsMainLocation(
3467
                            Criterion\Location\IsMainLocation::NOT_MAIN
3468
                        ),
3469
                    ]
3470
                ),
3471
                'offset' => 0,
3472
                'limit' => 10,
3473
                'sortClauses' => [],
3474
            ]
3475
        );
3476
3477
        $searchService = $repository->getSearchService();
3478
        $result = $searchService->findLocations($query);
3479
3480
        $this->assertEquals(1, $result->totalCount);
3481
        $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id);
3482
    }
3483
3484
    /**
3485
     * Test for the findLocations() method.
3486
     *
3487
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3488
     */
3489
    public function testSortMainLocationAscending()
3490
    {
3491
        $plainSiteLocationId = 56;
3492
        $designLocationId = 58;
3493
        $partnersContentId = 59;
3494
        $repository = $this->getRepository();
3495
        $locationService = $repository->getLocationService();
3496
        $contentService = $repository->getContentService();
3497
3498
        // Add secondary Location for "Partners" user group, under "Design" page
3499
        $newLocation = $locationService->createLocation(
3500
            $contentService->loadContentInfo($partnersContentId),
3501
            $locationService->newLocationCreateStruct($designLocationId)
3502
        );
3503
3504
        $this->refreshSearch($repository);
3505
3506
        $query = new LocationQuery(
3507
            [
3508
                'filter' => new Criterion\ParentLocationId($designLocationId),
3509
                'offset' => 0,
3510
                'limit' => 10,
3511
                'sortClauses' => [
3512
                    new SortClause\Location\IsMainLocation(
3513
                        LocationQuery::SORT_ASC
3514
                    ),
3515
                ],
3516
            ]
3517
        );
3518
3519
        $searchService = $repository->getSearchService();
3520
        $result = $searchService->findLocations($query);
3521
3522
        $this->assertEquals(2, $result->totalCount);
3523
        $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id);
3524
        $this->assertEquals($plainSiteLocationId, $result->searchHits[1]->valueObject->id);
3525
    }
3526
3527
    /**
3528
     * Test for the findLocations() method.
3529
     *
3530
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3531
     */
3532
    public function testSortMainLocationDescending()
3533
    {
3534
        $plainSiteLocationId = 56;
3535
        $designLocationId = 58;
3536
        $partnersContentId = 59;
3537
        $repository = $this->getRepository();
3538
        $locationService = $repository->getLocationService();
3539
        $contentService = $repository->getContentService();
3540
3541
        // Add secondary Location for "Partners" user group, under "Design" page
3542
        $newLocation = $locationService->createLocation(
3543
            $contentService->loadContentInfo($partnersContentId),
3544
            $locationService->newLocationCreateStruct($designLocationId)
3545
        );
3546
3547
        $this->refreshSearch($repository);
3548
3549
        $query = new LocationQuery(
3550
            [
3551
                'filter' => new Criterion\ParentLocationId($designLocationId),
3552
                'offset' => 0,
3553
                'limit' => 10,
3554
                'sortClauses' => [
3555
                    new SortClause\Location\IsMainLocation(
3556
                        LocationQuery::SORT_DESC
3557
                    ),
3558
                ],
3559
            ]
3560
        );
3561
3562
        $searchService = $repository->getSearchService();
3563
        $result = $searchService->findLocations($query);
3564
3565
        $this->assertEquals(2, $result->totalCount);
3566
        $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id);
3567
        $this->assertEquals($newLocation->id, $result->searchHits[1]->valueObject->id);
3568
    }
3569
3570
    /**
3571
     * Test for the findLocations() method.
3572
     *
3573
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3574
     */
3575
    public function testContentWithMultipleLocations()
3576
    {
3577
        $repository = $this->getRepository();
3578
        $contentService = $repository->getContentService();
3579
        $contentTypeService = $repository->getContentTypeService();
3580
        $locationService = $repository->getLocationService();
3581
3582
        $forumType = $contentTypeService->loadContentTypeByIdentifier('forum');
3583
3584
        $createStruct = $contentService->newContentCreateStruct($forumType, 'eng-GB');
3585
        $createStruct->alwaysAvailable = false;
3586
        $createStruct->setField('name', 'An awesome duplicate forum');
3587
3588
        $draft = $contentService->createContent($createStruct);
3589
        $content = $contentService->publishVersion($draft->getVersionInfo());
3590
3591
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2);
3592
        $location1 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3593
        $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(5);
3594
        $location2 = $locationService->createLocation($content->contentInfo, $locationCreateStruct);
3595
3596
        $this->refreshSearch($repository);
3597
3598
        $query = new LocationQuery(
3599
            [
3600
                'filter' => new Criterion\ContentId($content->id),
3601
                'sortClauses' => [
3602
                    new SortClause\Location\Id(LocationQuery::SORT_ASC),
3603
                ],
3604
            ]
3605
        );
3606
3607
        $searchService = $repository->getSearchService();
3608
        $result = $searchService->findLocations($query);
3609
3610
        $this->assertEquals(2, $result->totalCount);
3611
        $this->assertEquals(
3612
            $location1->id,
3613
            $result->searchHits[0]->valueObject->id
3614
        );
3615
        $this->assertEquals(
3616
            $location2->id,
3617
            $result->searchHits[1]->valueObject->id
3618
        );
3619
    }
3620
3621
    protected function createContentForTestUserMetadataGroupHorizontal()
3622
    {
3623
        $repository = $this->getRepository();
3624
        $contentService = $repository->getContentService();
3625
        $contentTypeService = $repository->getContentTypeService();
3626
        $locationService = $repository->getLocationService();
3627
        $userService = $repository->getUserService();
3628
        $administratorUser = $repository->getCurrentUser();
3629
        // ID of the "Administrators" user group in an eZ Publish demo installation
3630
        $administratorsUserGroupId = 12;
3631
        // ID of the "Editors" user group in an eZ Publish demo installation
3632
        $editorsUserGroupId = 13;
3633
3634
        $administratorsUserGroup = $userService->loadUserGroup($administratorsUserGroupId);
3635
        $editorsUserGroup = $userService->loadUserGroup($editorsUserGroupId);
3636
3637
        // Add additional Location for Administrators UserGroup under Editors UserGroup Location
3638
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3639
            $editorsUserGroup->contentInfo->mainLocationId
3640
        );
3641
        $newAdministratorsUserGroupLocation = $locationService->createLocation(
3642
            $administratorsUserGroup->contentInfo,
3643
            $locationCreateStruct
3644
        );
3645
3646
        // Add additional Location for administrator user under newly created UserGroup Location
3647
        $locationCreateStruct = $locationService->newLocationCreateStruct(
3648
            $newAdministratorsUserGroupLocation->id
3649
        );
3650
        $locationService->createLocation(
3651
            $administratorUser->contentInfo,
3652
            $locationCreateStruct
3653
        );
3654
3655
        // Create a Content to be found through Editors UserGroup id.
3656
        // This ensures data is indexed, it could also be done by updating metadata of
3657
        // an existing Content, but slot would need to reindex Content and that should
3658
        // be tested elsewhere (dedicated indexing integration tests, missing ATM).
3659
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
3660
3661
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
3662
        $createStruct->setField('name', 'test');
3663
3664
        $locationCreateStruct = $locationService->newLocationCreateStruct(2);
3665
        $draft = $contentService->createContent($createStruct, [$locationCreateStruct]);
3666
        $content = $contentService->publishVersion($draft->getVersionInfo());
3667
        $contentTypeService->createContentTypeDraft($contentType);
3668
3669
        $this->refreshSearch($repository);
3670
3671
        return $content;
3672
    }
3673
3674
    /**
3675
     * Test for the findContent() method.
3676
     *
3677
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3678
     */
3679
    public function testUserMetadataGroupHorizontalFilterContent($queryType = null)
3680
    {
3681
        if ($queryType === null) {
3682
            $queryType = 'filter';
3683
        }
3684
3685
        $repository = $this->getRepository();
3686
        $searchService = $repository->getSearchService();
3687
        $editorsUserGroupId = 13;
3688
3689
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3690
3691
        $criteria = [];
3692
        $setupFactory = $this->getSetupFactory();
3693
3694
        // Do not limit for LSE, as it does not not require reindexing.
3695
        // See explanation below.
3696
        if ($setupFactory instanceof LegacySolrSetupFactory) {
3697
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3698
        }
3699
3700
        $criteria[] = new Criterion\UserMetadata(
3701
            Criterion\UserMetadata::GROUP,
3702
            Criterion\Operator::EQ,
3703
            $editorsUserGroupId
3704
        );
3705
3706
        $query = new Query(
3707
            [
3708
                $queryType => new Criterion\LogicalAnd($criteria),
3709
                'sortClauses' => [
3710
                    new SortClause\ContentId(),
3711
                ],
3712
                'limit' => 50,
3713
            ]
3714
        );
3715
3716
        if ($setupFactory instanceof LegacySolrSetupFactory) {
3717
            $result = $searchService->findContent($query);
3718
3719
            // Administrator User is owned by itself, when additional Locations are added
3720
            // it should be reindexed and its UserGroups will updated, which means it should
3721
            // also be found as a Content of Editors UserGroup. However we do not handle this
3722
            // in slots yet, and also miss SPI methods to do it without using Search (also
3723
            // needed to decouple services), because as indexing is asynchronous Search
3724
            // should not eat its own dog food for reindexing.
3725
            $this->assertEquals(1, $result->totalCount);
3726
3727
            $this->assertEquals(
3728
                $content->id,
3729
                $result->searchHits[0]->valueObject->id
3730
            );
3731
        } else {
3732
            // This is how it should eventually work for all search engines,
3733
            // with required reindexing slots properly implemented.
3734
3735
            $result = $searchService->findContent($query);
3736
3737
            // Assert last hit manually, as id will change because it is created in test
3738
            // and not present it base fixture.
3739
            $foundContent1 = array_pop($result->searchHits);
3740
            $result->totalCount = $result->totalCount - 1;
3741
            $this->assertEquals($content->id, $foundContent1->valueObject->id);
3742
3743
            $this->simplifySearchResult($result);
3744
            $this->assertEqualsWithDelta(
3745
                include $this->getFixtureDir() . '/UserMetadata.php',
3746
                $result,
3747
                .1, // Be quite generous regarding delay -- most important for scores
3748
                'Search results do not match.',
3749
            );
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected ')'
Loading history...
3750
        }
3751
    }
3752
3753
    /**
3754
     * Test for the findContent() method.
3755
     *
3756
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3757
     */
3758
    public function testUserMetadataGroupHorizontalQueryContent()
3759
    {
3760
        $this->testUserMetadataGroupHorizontalFilterContent('query');
3761
    }
3762
3763
    /**
3764
     * Test for the findLocations() method.
3765
     *
3766
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3767
     */
3768
    public function testUserMetadataGroupHorizontalFilterLocation($queryType = null)
3769
    {
3770
        if ($queryType === null) {
3771
            $queryType = 'filter';
3772
        }
3773
3774
        $repository = $this->getRepository();
3775
        $searchService = $repository->getSearchService();
3776
        $editorsUserGroupId = 13;
3777
3778
        $content = $this->createContentForTestUserMetadataGroupHorizontal();
3779
3780
        $criteria = [];
3781
        $setupFactory = $this->getSetupFactory();
3782
3783
        // Do not limit for LSE, as it does not not require reindexing.
3784
        // See explanation below.
3785
        if ($setupFactory instanceof LegacySolrSetupFactory) {
3786
            $criteria[] = new Criterion\ContentTypeIdentifier('folder');
3787
        }
3788
3789
        $criteria[] = new Criterion\UserMetadata(
3790
            Criterion\UserMetadata::GROUP,
3791
            Criterion\Operator::EQ,
3792
            $editorsUserGroupId
3793
        );
3794
3795
        $query = new LocationQuery(
3796
            [
3797
                $queryType => new Criterion\LogicalAnd($criteria),
3798
                'sortClauses' => [
3799
                    new SortClause\Location\Id(),
3800
                ],
3801
                'limit' => 50,
3802
            ]
3803
        );
3804
3805
        if ($setupFactory instanceof LegacySolrSetupFactory) {
3806
            $result = $searchService->findLocations($query);
3807
3808
            // Administrator User is owned by itself, when additional Locations are added
3809
            // it should be reindexed and its UserGroups will updated, which means it should
3810
            // also be found as a Content of Editors UserGroup. However we do not handle this
3811
            // in slots yet, and also miss SPI methods to do it without using Search (also
3812
            // needed to decouple services), because as indexing is asynchronous Search
3813
            // should not eat its own dog food for reindexing.
3814
            $this->assertEquals(1, $result->totalCount);
3815
3816
            $this->assertEquals(
3817
                $content->contentInfo->mainLocationId,
3818
                $result->searchHits[0]->valueObject->id
3819
            );
3820
        } else {
3821
            // This is how it should eventually work for all search engines,
3822
            // with required reindexing slots properly implemented.
3823
3824
            $result = $searchService->findLocations($query);
3825
3826
            // Assert last two hits manually, as ids will change because they are created
3827
            // in test and not present in base fixture.
3828
            $foundLocation1 = array_pop($result->searchHits);
3829
            $foundLocation2 = array_pop($result->searchHits);
3830
            // Remove additional Administrators UserGroup Location
3831
            array_pop($result->searchHits);
3832
            $result->totalCount = $result->totalCount - 2;
3833
            $this->assertEquals(
3834
                $content->versionInfo->contentInfo->mainLocationId,
3835
                $foundLocation1->valueObject->id
3836
            );
3837
            $this->assertEquals(
3838
                $repository->getCurrentUser()->id,
3839
                $foundLocation2->valueObject->contentId
3840
            );
3841
3842
            $this->simplifySearchResult($result);
3843
            $this->assertEqualsWithDelta(
3844
                include $this->getFixtureDir() . '/UserMetadataLocation.php',
3845
                $result,
3846
                .1, // Be quite generous regarding delay -- most important for scores
3847
                'Search results do not match.',
3848
            );
3849
        }
3850
    }
3851
3852
    /**
3853
     * Test for the findLocations() method.
3854
     *
3855
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
3856
     */
3857
    public function testUserMetadataGroupHorizontalQueryLocation()
3858
    {
3859
        $this->testUserMetadataGroupHorizontalFilterLocation('query');
3860
    }
3861
3862
    /**
3863
     * Test for FullText on the findContent() method.
3864
     *
3865
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3866
     */
3867
    public function testFullTextOnNewContent()
3868
    {
3869
        $repository = $this->getRepository();
3870
        $contentService = $repository->getContentService();
3871
        $contentTypeService = $repository->getContentTypeService();
3872
        $locationService = $repository->getLocationService();
3873
        $searchService = $repository->getSearchService();
3874
3875
        $contentCreateStruct = $contentService->newContentCreateStruct(
3876
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3877
            'eng-GB'
3878
        );
3879
3880
        $contentCreateStruct->setField('name', 'foxes');
3881
3882
        $englishContent = $contentService->publishVersion(
3883
            $contentService->createContent(
3884
                $contentCreateStruct,
3885
                [$locationService->newLocationCreateStruct(2)]
3886
            )->versionInfo
3887
        );
3888
3889
        $this->refreshSearch($repository);
3890
3891
        $query = new Query(
3892
            [
3893
                'query' => new Criterion\FullText('foxes'),
3894
            ]
3895
        );
3896
3897
        $searchResult = $searchService->findContentInfo($query);
3898
3899
        $this->assertEquals(1, $searchResult->totalCount);
3900
        $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id);
3901
    }
3902
3903
    /**
3904
     * Test for the findContent() method.
3905
     *
3906
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3907
     */
3908
    public function testLanguageAnalysisSeparateContent()
3909
    {
3910
        $this->markTestSkipped('Language analysis is currently not supported');
3911
3912
        $repository = $this->getRepository();
3913
        $contentService = $repository->getContentService();
3914
        $contentTypeService = $repository->getContentTypeService();
3915
        $locationService = $repository->getLocationService();
3916
        $searchService = $repository->getSearchService();
3917
        $languageService = $repository->getContentLanguageService();
3918
3919
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
3920
        $languageCreateStruct->languageCode = 'rus-RU';
3921
        $languageCreateStruct->name = 'Russian';
3922
3923
        $languageService->createLanguage($languageCreateStruct);
3924
3925
        $contentCreateStruct = $contentService->newContentCreateStruct(
3926
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3927
            'eng-GB'
3928
        );
3929
3930
        $contentCreateStruct->setField('name', 'foxes');
3931
3932
        $englishContent = $contentService->publishVersion(
3933
            $contentService->createContent(
3934
                $contentCreateStruct,
3935
                [$locationService->newLocationCreateStruct(2)]
3936
            )->versionInfo
3937
        );
3938
3939
        $contentCreateStruct = $contentService->newContentCreateStruct(
3940
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3941
            'rus-RU'
3942
        );
3943
3944
        $contentCreateStruct->setField('name', 'foxes');
3945
3946
        $russianContent = $contentService->publishVersion(
3947
            $contentService->createContent(
3948
                $contentCreateStruct,
3949
                [$locationService->newLocationCreateStruct(2)]
3950
            )->versionInfo
3951
        );
3952
3953
        // Only Content in English should be found, because Content in Russian
3954
        // will not be correctly stemmed
3955
        $query = new Query(
3956
            [
3957
                'query' => new Criterion\FullText('foxing'),
3958
            ]
3959
        );
3960
3961
        $searchResult = $searchService->findContent($query);
3962
3963
        $this->assertEquals(1, $searchResult->totalCount);
3964
        $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id);
3965
    }
3966
3967
    /**
3968
     * Test for the findContent() method.
3969
     *
3970
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
3971
     */
3972
    public function testLanguageAnalysisSameContent()
3973
    {
3974
        $this->markTestSkipped('Language analysis is currently not supported');
3975
3976
        $repository = $this->getRepository();
3977
        $contentService = $repository->getContentService();
3978
        $contentTypeService = $repository->getContentTypeService();
3979
        $locationService = $repository->getLocationService();
3980
        $searchService = $repository->getSearchService();
3981
        $languageService = $repository->getContentLanguageService();
3982
3983
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
3984
        $languageCreateStruct->languageCode = 'rus-RU';
3985
        $languageCreateStruct->name = 'Russian';
3986
3987
        $languageService->createLanguage($languageCreateStruct);
3988
3989
        $contentCreateStruct = $contentService->newContentCreateStruct(
3990
            $contentTypeService->loadContentTypeByIdentifier('folder'),
3991
            'eng-GB'
3992
        );
3993
3994
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
3995
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
3996
3997
        $mixedContent = $contentService->publishVersion(
3998
            $contentService->createContent(
3999
                $contentCreateStruct,
4000
                [$locationService->newLocationCreateStruct(2)]
4001
            )->versionInfo
4002
        );
4003
4004
        // Content will be found because translation in Russian will be correctly stemmed
4005
        $query = new Query(
4006
            [
4007
                'query' => new Criterion\FullText('важнее'),
4008
            ]
4009
        );
4010
4011
        $searchResult = $searchService->findContent($query);
4012
4013
        $this->assertEquals(1, $searchResult->totalCount);
4014
        $this->assertEquals($mixedContent->id, $searchResult->searchHits[0]->valueObject->id);
4015
    }
4016
4017
    /**
4018
     * Test for the findContent() method.
4019
     *
4020
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4021
     */
4022
    public function testLanguageAnalysisSameContentNotFound()
4023
    {
4024
        $this->markTestSkipped('Language analysis is currently not supported');
4025
4026
        $repository = $this->getRepository();
4027
        $contentService = $repository->getContentService();
4028
        $contentTypeService = $repository->getContentTypeService();
4029
        $locationService = $repository->getLocationService();
4030
        $searchService = $repository->getSearchService();
4031
        $languageService = $repository->getContentLanguageService();
4032
4033
        $languageCreateStruct = $languageService->newLanguageCreateStruct();
4034
        $languageCreateStruct->languageCode = 'rus-RU';
4035
        $languageCreateStruct->name = 'Russian';
4036
4037
        $languageService->createLanguage($languageCreateStruct);
4038
4039
        $contentCreateStruct = $contentService->newContentCreateStruct(
4040
            $contentTypeService->loadContentTypeByIdentifier('folder'),
4041
            'eng-GB'
4042
        );
4043
4044
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB');
4045
        $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU');
4046
4047
        $mixedContent = $contentService->publishVersion(
4048
            $contentService->createContent(
4049
                $contentCreateStruct,
4050
                [$locationService->newLocationCreateStruct(2)]
4051
            )->versionInfo
4052
        );
4053
4054
        // Content should be found because translation in Russian will be correctly stemmed
4055
        $query = new Query(
4056
            [
4057
                'query' => new Criterion\FullText('важнее'),
4058
            ]
4059
        );
4060
4061
        // Filtering fields for only English will cause no match because the term will
4062
        // not be correctly stemmed
4063
        $searchResult = $searchService->findContent($query, ['languages' => ['eng-GB']]);
4064
4065
        $this->assertEquals(0, $searchResult->totalCount);
4066
    }
4067
4068
    /**
4069
     * Test for the findContent() method searching for content filtered by languages.
4070
     *
4071
     * @covers \eZ\Publish\Core\Repository\SearchService::findContent
4072
     */
4073
    public function testFindContentWithLanguageFilter()
4074
    {
4075
        $repository = $this->getRepository();
4076
        $searchService = $repository->getSearchService();
4077
4078
        $query = new Query(
4079
            [
4080
                'filter' => new Criterion\ContentId([4]),
4081
                'offset' => 0,
4082
            ]
4083
        );
4084
        $searchResult = $searchService->findContent(
4085
            $query,
4086
            ['languages' => ['eng-US']],
4087
            false
4088
        );
4089
        /* END: Use Case */
4090
4091
        $this->assertInstanceOf(
4092
            SearchResult::class,
4093
            $searchResult
4094
        );
4095
4096
        $this->assertEquals(1, $searchResult->totalCount);
4097
        $this->assertCount($searchResult->totalCount, $searchResult->searchHits);
4098
        foreach ($searchResult->searchHits as $searchHit) {
4099
            $this->assertInstanceOf(
4100
                SearchHit::class,
4101
                $searchHit
4102
            );
4103
        }
4104
    }
4105
4106
    /**
4107
     * This test prepares data for other tests.
4108
     *
4109
     * @see testFulltextContentSearchComplex
4110
     * @see testFulltextLocationSearchComplex
4111
     *
4112
     * @return array
4113
     */
4114
    public function testFulltextComplex()
4115
    {
4116
        $repository = $this->getRepository();
4117
        $contentService = $repository->getContentService();
4118
        $contentTypeService = $repository->getContentTypeService();
4119
        $locationService = $repository->getLocationService();
4120
4121
        $contentType = $contentTypeService->loadContentTypeByIdentifier('folder');
4122
        $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4123
4124
        $contentCreateStruct->setField('name', 'red');
4125
        $contentCreateStruct->setField('short_name', 'red apple');
4126
        $content1 = $contentService->publishVersion(
4127
            $contentService->createContent(
4128
                $contentCreateStruct,
4129
                [$locationService->newLocationCreateStruct(2)]
4130
            )->versionInfo
4131
        );
4132
4133
        $contentCreateStruct->setField('name', 'apple');
4134
        $contentCreateStruct->setField('short_name', 'two');
4135
        $content2 = $contentService->publishVersion(
4136
            $contentService->createContent(
4137
                $contentCreateStruct,
4138
                [$locationService->newLocationCreateStruct(2)]
4139
            )->versionInfo
4140
        );
4141
4142
        $contentCreateStruct->setField('name', 'red apple');
4143
        $contentCreateStruct->setField('short_name', 'three');
4144
        $content3 = $contentService->publishVersion(
4145
            $contentService->createContent(
4146
                $contentCreateStruct,
4147
                [$locationService->newLocationCreateStruct(2)]
4148
            )->versionInfo
4149
        );
4150
4151
        $this->refreshSearch($repository);
4152
4153
        $criterion = new Criterion\FullText(
4154
            'red apple',
4155
            [
4156
                'boost' => [
4157
                    'short_name' => 2,
4158
                ],
4159
                'fuzziness' => .1,
4160
            ]
4161
        );
4162
4163
        return [$criterion, $content1, $content2, $content3];
4164
    }
4165
4166
    /**
4167
     * Test for the findContent() method.
4168
     *
4169
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4170
     * @depends testFulltextComplex
4171
     *
4172
     * @param array $data
4173
     */
4174
    public function testFulltextContentSearchComplex(array $data)
4175
    {
4176
        // Do not initialize from scratch
4177
        $repository = $this->getRepository(false);
4178
        $searchService = $repository->getSearchService();
4179
        list($criterion, $content1, $content2, $content3) = $data;
4180
4181
        $searchResult = $searchService->findContent(
4182
            new Query(['query' => $criterion]),
4183
            ['languages' => ['eng-GB']]
4184
        );
4185
        $searchHits = $searchResult->searchHits;
4186
4187
        $this->assertEquals(3, $searchResult->totalCount);
4188
4189
        // Legacy search engine does have scoring, sorting the results by ID in that case
4190
        $setupFactory = $this->getSetupFactory();
4191
        if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
4192
            usort(
4193
                $searchHits,
4194
                function ($a, $b) {
4195
                    return ($a->valueObject->id < $b->valueObject->id) ? -1 : 1;
4196
                }
4197
            );
4198
4199
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->id);
4200
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->id);
4201
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->id);
4202
4203
            return;
4204
        }
4205
4206
        // Assert scores are descending
4207
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4208
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4209
4210
        // Assert order
4211
        $this->assertEquals($content1->id, $searchHits[0]->valueObject->id);
4212
        $this->assertEquals($content3->id, $searchHits[1]->valueObject->id);
4213
        $this->assertEquals($content2->id, $searchHits[2]->valueObject->id);
4214
    }
4215
4216
    /**
4217
     * Test for the findLocations() method.
4218
     *
4219
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4220
     * @depends testFulltextComplex
4221
     *
4222
     * @param array $data
4223
     */
4224
    public function testFulltextLocationSearchComplex(array $data)
4225
    {
4226
        $setupFactory = $this->getSetupFactory();
4227
        if ($setupFactory instanceof LegacySolrSetupFactory && getenv('SOLR_VERSION') === '4.10.4') {
4228
            $this->markTestSkipped('Skipping location search score test on Solr 4.10, you need Solr 6 for this!');
4229
        }
4230
4231
        // Do not initialize from scratch
4232
        $repository = $this->getRepository(false);
4233
        list($criterion, $content1, $content2, $content3) = $data;
4234
        $searchService = $repository->getSearchService();
4235
4236
        $searchResult = $searchService->findLocations(
4237
            new LocationQuery(['query' => $criterion]),
4238
            ['languages' => ['eng-GB']]
4239
        );
4240
        $searchHits = $searchResult->searchHits;
4241
4242
        $this->assertEquals(3, $searchResult->totalCount);
4243
4244
        // Legacy search engine does have scoring, sorting the results by ID in that case
4245
        $setupFactory = $this->getSetupFactory();
4246
        if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') {
4247
            usort(
4248
                $searchHits,
4249
                function ($a, $b) {
4250
                    return ($a->valueObject->id < $b->valueObject->id) ? -1 : 1;
4251
                }
4252
            );
4253
4254
            $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId);
4255
            $this->assertEquals($content2->id, $searchHits[1]->valueObject->contentId);
4256
            $this->assertEquals($content3->id, $searchHits[2]->valueObject->contentId);
4257
4258
            return;
4259
        }
4260
4261
        // Assert scores are descending
4262
        $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score);
4263
        $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score);
4264
4265
        // Assert order
4266
        $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId);
4267
        $this->assertEquals($content3->id, $searchHits[1]->valueObject->contentId);
4268
        $this->assertEquals($content2->id, $searchHits[2]->valueObject->contentId);
4269
    }
4270
4271
    /**
4272
     * Assert that query result matches the given fixture.
4273
     *
4274
     * @param Query $query
4275
     * @param string $fixture
4276
     * @param null|callable $closure
4277
     * @param bool $info
4278
     * @param bool $id
4279
     */
4280
    protected function assertQueryFixture(Query $query, $fixture, $closure = null, $ignoreScore = true, $info = false, $id = true)
4281
    {
4282
        $repository = $this->getRepository();
4283
        $searchService = $repository->getSearchService();
4284
4285
        try {
4286
            if ($query instanceof LocationQuery) {
4287
                $result = $searchService->findLocations($query);
4288
            } elseif ($query instanceof Query) {
4289
                if ($info) {
4290
                    $result = $searchService->findContentInfo($query);
4291
                } else {
4292
                    $result = $searchService->findContent($query);
4293
                }
4294
            } else {
4295
                $this->fail('Expected instance of LocationQuery or Query, got: ' . gettype($query));
4296
            }
4297
            $this->simplifySearchResult($result);
4298
        } catch (NotImplementedException $e) {
4299
            $this->markTestSkipped(
4300
                'This feature is not supported by the current search backend: ' . $e->getMessage()
4301
            );
4302
        }
4303
4304
        if (!is_file($fixture)) {
4305
            if (isset($_ENV['ez_tests_record'])) {
4306
                file_put_contents(
4307
                    $record = $fixture . '.recording',
4308
                    "<?php\n\nreturn " . var_export($result, true) . ";\n\n"
4309
                );
4310
                $this->markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result));
4311
            } else {
4312
                $this->markTestIncomplete("No fixture available. Set \$_ENV['ez_tests_record'] to generate:\n " . $fixture);
4313
            }
4314
        }
4315
4316
        $fixture = include $fixture;
4317
4318
        if ($closure !== null) {
4319
            $closure($fixture);
4320
            $closure($result);
4321
        }
4322
4323
        if ($ignoreScore) {
4324
            foreach ([$fixture, $result] as $set) {
4325
                $property = new \ReflectionProperty(get_class($set), 'maxScore');
4326
                $property->setAccessible(true);
4327
                $property->setValue($set, 0.0);
4328
4329
                foreach ($set->searchHits as $hit) {
4330
                    $property = new \ReflectionProperty(get_class($hit), 'score');
4331
                    $property->setAccessible(true);
4332
                    $property->setValue($hit, 0.0);
4333
                }
4334
            }
4335
        }
4336
4337
        foreach ([$fixture, $result] as $set) {
4338
            foreach ($set->searchHits as $hit) {
4339
                $property = new \ReflectionProperty(get_class($hit), 'index');
4340
                $property->setAccessible(true);
4341
                $property->setValue($hit, null);
4342
4343
                $property = new \ReflectionProperty(get_class($hit), 'matchedTranslation');
4344
                $property->setAccessible(true);
4345
                $property->setValue($hit, null);
4346
4347
                if (!$id) {
4348
                    $hit->valueObject['id'] = null;
4349
                }
4350
            }
4351
        }
4352
4353
        $this->assertEqualsWithDelta(
4354
            $fixture,
4355
            $result,
4356
            .99, // Be quite generous regarding delay -- most important for scores
4357
            'Search results do not match.',
4358
        );
4359
    }
4360
4361
    /**
4362
     * Show a simplified view of the search result for manual introspection.
4363
     *
4364
     * @param SearchResult $result
4365
     *
4366
     * @return string
4367
     */
4368
    protected function printResult(SearchResult $result)
4369
    {
4370
        $printed = '';
4371
        foreach ($result->searchHits as $hit) {
4372
            $printed .= sprintf(" - %s (%s)\n", $hit->valueObject['title'], $hit->valueObject['id']);
4373
        }
4374
4375
        return $printed;
4376
    }
4377
4378
    /**
4379
     * Simplify search result.
4380
     *
4381
     * This leads to saner comparisons of results, since we do not get the full
4382
     * content objects every time.
4383
     *
4384
     * @param SearchResult $result
4385
     */
4386
    protected function simplifySearchResult(SearchResult $result)
4387
    {
4388
        $result->time = 1;
4389
4390
        foreach ($result->searchHits as $hit) {
4391
            switch (true) {
4392
                case $hit->valueObject instanceof Content:
4393
                case $hit->valueObject instanceof Location:
4394
                    $hit->valueObject = [
4395
                        'id' => $hit->valueObject->contentInfo->id,
4396
                        'title' => $hit->valueObject->contentInfo->name,
4397
                    ];
4398
                    break;
4399
4400
                case $hit->valueObject instanceof ContentInfo:
4401
                    $hit->valueObject = [
4402
                        'id' => $hit->valueObject->id,
4403
                        'title' => $hit->valueObject->name,
4404
                    ];
4405
                    break;
4406
4407
                default:
4408
                    throw new \RuntimeException('Unknown search result hit type: ' . get_class($hit->valueObject));
4409
            }
4410
        }
4411
    }
4412
4413
    /**
4414
     * Get fixture directory.
4415
     *
4416
     * @return string
4417
     */
4418
    protected function getFixtureDir()
4419
    {
4420
        return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/';
4421
    }
4422
4423
    /**
4424
     * For findContentInfo tests, to reuse fixtures for findContent tests.
4425
     *
4426
     * @param null|callable $closure
4427
     *
4428
     * @return callable
4429
     */
4430
    private function getContentInfoFixtureClosure($closure = null)
4431
    {
4432
        /** @var $data \eZ\Publish\API\Repository\Values\Content\Search\SearchResult */
4433
        return function (&$data) use ($closure) {
4434
            foreach ($data->searchHits as $searchHit) {
4435
                if ($searchHit->valueObject instanceof Content) {
4436
                    $searchHit->valueObject = $searchHit->valueObject->getVersionInfo()->getContentInfo();
4437
                }
4438
            }
4439
4440
            if (isset($closure)) {
4441
                $closure($data);
4442
            }
4443
        };
4444
    }
4445
4446
    /**
4447
     * Test searching using Field Criterion where the given Field Identifier exists in
4448
     * both searchable and non-searchable Fields.
4449
     * Number of returned results depends on used storage.
4450
     *
4451
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4452
     */
4453
    public function testFieldCriterionForContentsWithIdenticalFieldIdentifiers()
4454
    {
4455
        $this->createContentWithFieldType(
4456
            'url',
4457
            'title',
4458
            'foo'
4459
        );
4460
        $this->createContentWithFieldType(
4461
            'string',
4462
            'title',
4463
            'foo'
4464
        );
4465
        $query = new Query(
4466
            [
4467
                'query' => new Criterion\Field(
4468
                    'title',
4469
                    Criterion\Operator::EQ,
4470
                    'foo'
4471
                ),
4472
            ]
4473
        );
4474
4475
        $repository = $this->getRepository();
4476
        $searchService = $repository->getSearchService();
4477
        $result = $searchService->findContent($query);
4478
4479
        $this->assertTrue(($result->totalCount === 1 || $result->totalCount === 2));
4480
    }
4481
4482
    private function createContentWithFieldType(
4483
        string $fieldType,
4484
        string $fieldName,
4485
        string $fieldValue
4486
    ) {
4487
        $repository = $this->getRepository();
4488
        $contentTypeService = $repository->getContentTypeService();
4489
        $contentService = $repository->getContentService();
4490
4491
        $createStruct = $contentTypeService->newContentTypeCreateStruct($fieldType . uniqid());
4492
        $createStruct->mainLanguageCode = 'eng-GB';
4493
        $createStruct->remoteId = $fieldType . '-123';
4494
        $createStruct->names = ['eng-GB' => $fieldType];
4495
        $createStruct->creatorId = 14;
4496
        $createStruct->creationDate = new \DateTime();
4497
4498
        $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct($fieldName, 'ez' . $fieldType);
4499
        $fieldCreate->names = ['eng-GB' => $fieldName];
4500
        $fieldCreate->fieldGroup = 'main';
4501
        $fieldCreate->position = 1;
4502
4503
        $createStruct->addFieldDefinition($fieldCreate);
4504
4505
        $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content');
4506
        $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]);
4507
        $contentTypeService->publishContentTypeDraft($contentTypeDraft);
4508
        $contentType = $contentTypeService->loadContentType($contentTypeDraft->id);
4509
4510
        $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB');
4511
        $createStruct->remoteId = $fieldType . '-456';
4512
        $createStruct->alwaysAvailable = false;
4513
        $createStruct->setField(
4514
            $fieldName,
4515
            $fieldValue
4516
        );
4517
4518
        $draft = $contentService->createContent($createStruct);
4519
        $content = $contentService->publishVersion($draft->getVersionInfo());
4520
4521
        $this->refreshSearch($repository);
4522
4523
        return $content;
4524
    }
4525
4526
    /**
4527
     * Test for the findContent() method with random sort clause.
4528
     *
4529
     * There is a slight chance when this test could fail, if by some reason,
4530
     * we got to same _random_ results, or mt_rand() provides same seed for seed-supported DB implementation.
4531
     *
4532
     * @see \eZ\Publish\API\Repository\SearchService::findContent()
4533
     *
4534
     * @dataProvider getSeedsForRandomSortClause
4535
     */
4536
    public function testRandomSortContent(?int $firstSeed, ?int $secondSeed)
4537
    {
4538
        if ($firstSeed || $secondSeed) {
4539
            $this->skipIfSeedNotImplemented();
4540
        }
4541
4542
        $firstQuery = new Query([
4543
            'sortClauses' => [
4544
                new SortClause\Random($firstSeed),
4545
            ],
4546
        ]);
4547
4548
        $secondQuery = new Query([
4549
            'sortClauses' => [
4550
                new SortClause\Random($secondSeed),
4551
            ],
4552
        ]);
4553
4554
        $repository = $this->getRepository();
4555
        $searchService = $repository->getSearchService();
4556
4557
        $method = 'assertNotEquals';
4558
4559
        if ($firstSeed !== null && $secondSeed !== null && ($firstSeed === $secondSeed)) {
4560
            $method = 'assertEquals';
4561
        }
4562
4563
        try {
4564
            $this->$method(
4565
                $searchService->findContent($firstQuery)->searchHits,
4566
                $searchService->findContent($secondQuery)->searchHits
4567
            );
4568
        } catch (NotImplementedException $e) {
4569
            $this->markTestSkipped(
4570
                'This feature is not supported by the current search backend: ' . $e->getMessage()
4571
            );
4572
        }
4573
    }
4574
4575
    /**
4576
     * Test for the findLocations() method.
4577
     *
4578
     * @see \eZ\Publish\API\Repository\SearchService::findLocations()
4579
     *
4580
     * @dataProvider getSeedsForRandomSortClause
4581
     */
4582
    public function testRandomSortLocation(?int $firstSeed, ?int $secondSeed)
4583
    {
4584
        if ($firstSeed || $secondSeed) {
4585
            $this->skipIfSeedNotImplemented();
4586
        }
4587
4588
        $firstQuery = new LocationQuery([
4589
            'sortClauses' => [
4590
                new SortClause\Random($firstSeed),
4591
            ],
4592
        ]);
4593
4594
        $secondQuery = new LocationQuery([
4595
            'sortClauses' => [
4596
                new SortClause\Random($secondSeed),
4597
            ],
4598
        ]);
4599
4600
        $repository = $this->getRepository();
4601
        $searchService = $repository->getSearchService();
4602
4603
        $method = 'assertNotEquals';
4604
4605
        if ($firstSeed !== null && $secondSeed !== null && ($firstSeed === $secondSeed)) {
4606
            $method = 'assertEquals';
4607
        }
4608
4609
        try {
4610
            $this->$method(
4611
                $searchService->findLocations($firstQuery)->searchHits,
4612
                $searchService->findLocations($secondQuery)->searchHits
4613
            );
4614
        } catch (NotImplementedException $e) {
4615
            $this->markTestSkipped(
4616
                'This feature is not supported by the current search backend: ' . $e->getMessage()
4617
            );
4618
        }
4619
    }
4620
4621
    public function getSeedsForRandomSortClause()
4622
    {
4623
        $randomSeed = mt_rand();
4624
4625
        return [
4626
            [
4627
                null,
4628
                null,
4629
            ],
4630
            [
4631
                123456,
4632
                123456,
4633
            ],
4634
            [
4635
               $randomSeed,
4636
               2 * $randomSeed,
4637
            ],
4638
        ];
4639
    }
4640
4641
    private function skipIfSeedNotImplemented()
4642
    {
4643
        /** @var \eZ\Publish\API\Repository\Tests\SetupFactory\Legacy $setupFactory */
4644
        $setupFactory = $this->getSetupFactory();
4645
4646
        $db = $setupFactory->getDB();
4647
4648
        if (in_array($db, ['sqlite', 'pgsql'])) {
4649
            $this->markTestSkipped(
4650
                'Seed function is not implemented in ' . $db . '.'
4651
            );
4652
        }
4653
    }
4654
}
4655