Passed
Push — master ( b4e3f0...d6a119 )
by Robbie
04:16 queued 02:31
created

tests/SolrIndexTest.php (1 issue)

1
<?php
2
3
namespace SilverStripe\FullTextSearch\Tests;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Core\Environment;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Core\Kernel;
9
use SilverStripe\Dev\SapphireTest;
10
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
11
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
12
use SilverStripe\FullTextSearch\Solr\SolrIndex;
13
use SilverStripe\FullTextSearch\Solr\Services\Solr3Service;
14
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_Container;
15
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasOne;
16
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasMany;
17
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_ManyMany;
18
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_OtherContainer;
19
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelationIndex;
20
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelationInheritedIndex;
21
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_BoostedIndex;
22
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex;
23
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex2;
24
use SilverStripe\Subsites\Model\Subsite;
25
26
class SolrIndexTest extends SapphireTest
27
{
28
    public function testFieldDataHasOne()
29
    {
30
        $index = new SolrIndexTest_FakeIndex();
31
        $data = $index->fieldData('HasOneObject.Field1');
32
33
        $data = $data[SearchUpdaterTest_Container::class . '_HasOneObject_Field1'];
34
35
        $this->assertEquals(SearchUpdaterTest_Container::class, $data['origin']);
36
        $this->assertEquals(SearchUpdaterTest_Container::class, $data['base']);
37
        $this->assertEquals(SearchUpdaterTest_HasOne::class, $data['class']);
38
    }
39
40
    public function testFieldDataHasMany()
41
    {
42
        $index = new SolrIndexTest_FakeIndex();
43
        $data = $index->fieldData('HasManyObjects.Field1');
44
        $data = $data[SearchUpdaterTest_Container::class . '_HasManyObjects_Field1'];
45
46
        $this->assertEquals(SearchUpdaterTest_Container::class, $data['origin']);
47
        $this->assertEquals(SearchUpdaterTest_Container::class, $data['base']);
48
        $this->assertEquals(SearchUpdaterTest_HasMany::class, $data['class']);
49
    }
50
51
    public function testFieldDataManyMany()
52
    {
53
        $index = new SolrIndexTest_FakeIndex();
54
        $data = $index->fieldData('ManyManyObjects.Field1');
55
        $data = $data[SearchUpdaterTest_Container::class . '_ManyManyObjects_Field1'];
56
57
        $this->assertEquals(SearchUpdaterTest_Container::class, $data['origin']);
58
        $this->assertEquals(SearchUpdaterTest_Container::class, $data['base']);
59
        $this->assertEquals(SearchUpdaterTest_ManyMany::class, $data['class']);
60
    }
61
62
    public function testFieldDataAmbiguousHasMany()
63
    {
64
        $index = new SolrIndexTest_AmbiguousRelationIndex();
65
        $data = $index->fieldData('HasManyObjects.Field1');
66
67
        $this->assertArrayHasKey(SearchUpdaterTest_Container::class . '_HasManyObjects_Field1', $data);
68
        $this->assertArrayHasKey(SearchUpdaterTest_OtherContainer::class . '_HasManyObjects_Field1', $data);
69
70
        $dataContainer = $data[SearchUpdaterTest_Container::class . '_HasManyObjects_Field1'];
71
        $this->assertEquals(SearchUpdaterTest_Container::class, $dataContainer['origin']);
72
        $this->assertEquals(SearchUpdaterTest_Container::class, $dataContainer['base']);
73
        $this->assertEquals(SearchUpdaterTest_HasMany::class, $dataContainer['class']);
74
75
        $dataOtherContainer = $data[SearchUpdaterTest_OtherContainer::class . '_HasManyObjects_Field1'];
76
        $this->assertEquals(SearchUpdaterTest_OtherContainer::class, $dataOtherContainer['origin']);
77
        $this->assertEquals(SearchUpdaterTest_OtherContainer::class, $dataOtherContainer['base']);
78
        $this->assertEquals(SearchUpdaterTest_HasMany::class, $dataOtherContainer['class']);
79
    }
80
81
    public function testFieldDataAmbiguousManyMany()
82
    {
83
        $index = new SolrIndexTest_AmbiguousRelationIndex();
84
        $data = $index->fieldData('ManyManyObjects.Field1');
85
86
        $this->assertArrayHasKey(SearchUpdaterTest_Container::class . '_ManyManyObjects_Field1', $data);
87
        $this->assertArrayHasKey(SearchUpdaterTest_OtherContainer::class . '_ManyManyObjects_Field1', $data);
88
89
        $dataContainer = $data[SearchUpdaterTest_Container::class . '_ManyManyObjects_Field1'];
90
        $this->assertEquals(SearchUpdaterTest_Container::class, $dataContainer['origin']);
91
        $this->assertEquals(SearchUpdaterTest_Container::class, $dataContainer['base']);
92
        $this->assertEquals(SearchUpdaterTest_ManyMany::class, $dataContainer['class']);
93
94
        $dataOtherContainer = $data[SearchUpdaterTest_OtherContainer::class . '_ManyManyObjects_Field1'];
95
        $this->assertEquals(SearchUpdaterTest_OtherContainer::class, $dataOtherContainer['origin']);
96
        $this->assertEquals(SearchUpdaterTest_OtherContainer::class, $dataOtherContainer['base']);
97
        $this->assertEquals(SearchUpdaterTest_ManyMany::class, $dataOtherContainer['class']);
98
    }
99
100
    public function testFieldDataAmbiguousManyManyInherited()
101
    {
102
        $index = new SolrIndexTest_AmbiguousRelationInheritedIndex();
103
        $data = $index->fieldData('ManyManyObjects.Field1');
104
105
        $this->assertArrayHasKey(SearchUpdaterTest_Container::class . '_ManyManyObjects_Field1', $data);
106
        $this->assertArrayHasKey(SearchUpdaterTest_OtherContainer::class . '_ManyManyObjects_Field1', $data);
107
        $this->assertArrayNotHasKey(SearchUpdaterTest_ExtendedContainer::class . '_ManyManyObjects_Field1', $data);
108
109
        $dataContainer = $data[SearchUpdaterTest_Container::class . '_ManyManyObjects_Field1'];
110
        $this->assertEquals(SearchUpdaterTest_Container::class, $dataContainer['origin']);
111
        $this->assertEquals(SearchUpdaterTest_Container::class, $dataContainer['base']);
112
        $this->assertEquals(SearchUpdaterTest_ManyMany::class, $dataContainer['class']);
113
114
        $dataOtherContainer = $data[SearchUpdaterTest_OtherContainer::class . '_ManyManyObjects_Field1'];
115
        $this->assertEquals(SearchUpdaterTest_OtherContainer::class, $dataOtherContainer['origin']);
116
        $this->assertEquals(SearchUpdaterTest_OtherContainer::class, $dataOtherContainer['base']);
117
        $this->assertEquals(SearchUpdaterTest_ManyMany::class, $dataOtherContainer['class']);
118
    }
119
120
    /**
121
     * Test boosting on SearchQuery
122
     */
123
    public function testBoostedQuery()
124
    {
125
        /** @var Solr3Service|PHPUnit_Framework_MockObject_MockObject $serviceMock */
126
        $serviceMock = $this->getMockBuilder(Solr3Service::class)
127
            ->setMethods(['search'])
128
            ->getMock();
129
130
        $serviceMock->expects($this->once())
0 ignored issues
show
The method expects() does not exist on SilverStripe\FullTextSea...r\Services\Solr3Service. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

130
        $serviceMock->/** @scrutinizer ignore-call */ 
131
                      expects($this->once())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
131
            ->method('search')
132
            ->with(
133
                $this->equalTo('+(Field1:term^1.5 OR HasOneObject_Field1:term^3)'),
134
                $this->anything(),
135
                $this->anything(),
136
                $this->anything(),
137
                $this->anything()
138
            )->willReturn($this->getFakeRawSolrResponse());
139
140
        $index = new SolrIndexTest_FakeIndex();
141
        $index->setService($serviceMock);
142
143
        $query = new SearchQuery();
144
        $query->addSearchTerm(
145
            'term',
146
            null,
147
            array('Field1' => 1.5, 'HasOneObject_Field1' => 3)
148
        );
149
        $index->search($query);
150
    }
151
152
    /**
153
     * Test boosting on field schema (via queried fields parameter)
154
     */
155
    public function testBoostedField()
156
    {
157
        if (class_exists(Subsite::class)) {
158
            Config::modify()->set(SearchVariantSubsites::class, 'enabled', false);
159
        }
160
161
        /** @var Solr3Service|PHPUnit_Framework_MockObject_MockObject $serviceMock */
162
        $serviceMock = $this->getMockBuilder(Solr3Service::class)
163
            ->setMethods(['search'])
164
            ->getMock();
165
166
        $serviceMock->expects($this->once())
167
            ->method('search')
168
            ->with(
169
                $this->equalTo('+term'),
170
                $this->anything(),
171
                $this->anything(),
172
                $this->equalTo([
173
                    'qf' => SearchUpdaterTest_Container::class . '_Field1^1.5 '
174
                        . SearchUpdaterTest_Container::class . '_Field2^2.1 _text',
175
                    'fq' => '',
176
                ]),
177
                $this->anything()
178
            )->willReturn($this->getFakeRawSolrResponse());
179
180
        $index = new SolrIndexTest_BoostedIndex();
181
        $index->setService($serviceMock);
182
183
        $query = new SearchQuery();
184
        $query->addSearchTerm('term');
185
        $index->search($query);
186
    }
187
188
    public function testHighlightQueryOnBoost()
189
    {
190
        /** @var SilverStripe\FullTextSearch\Solr\Services\Solr3Service|ObjectProphecy $serviceMock */
191
        $serviceMock = $this->getMockBuilder(Solr3Service::class)
192
            ->setMethods(['search'])
193
            ->getMock();
194
195
        $serviceMock->expects($this->exactly(2))
196
            ->method('search')
197
            ->withConsecutive(
198
                [
199
                    $this->equalTo('+(Field1:term^1.5 OR HasOneObject_Field1:term^3)'),
200
                    $this->anything(),
201
                    $this->anything(),
202
                    $this->logicalNot(
203
                        $this->arrayHasKey('hl.q')
204
                    ),
205
                    $this->anything()
206
                ],
207
                [
208
                    $this->equalTo('+(Field1:term^1.5 OR HasOneObject_Field1:term^3)'),
209
                    $this->anything(),
210
                    $this->anything(),
211
                    $this->arrayHasKey('hl.q'),
212
                    $this->anything()
213
                ]
214
            )->willReturn($this->getFakeRawSolrResponse());
215
216
        $index = new SolrIndexTest_FakeIndex();
217
        $index->setService($serviceMock);
218
219
        // Search without highlighting
220
        $query = new SearchQuery();
221
        $query->addSearchTerm(
222
            'term',
223
            null,
224
            array('Field1' => 1.5, 'HasOneObject_Field1' => 3)
225
        );
226
        $index->search($query);
227
228
        // Search with highlighting
229
        $query = new SearchQuery();
230
        $query->addSearchTerm(
231
            'term',
232
            null,
233
            array('Field1' => 1.5, 'HasOneObject_Field1' => 3)
234
        );
235
        $index->search($query, -1, -1, array('hl' => true));
236
    }
237
238
    public function testIndexExcludesNullValues()
239
    {
240
        /** @var Solr3Service|ObjectProphecy $serviceMock */
241
        $serviceMock = $this->createMock(Solr3Service::class);
242
        $index = new SolrIndexTest_FakeIndex();
243
        $index->setService($serviceMock);
244
        $obj = new SearchUpdaterTest_Container();
245
246
        $obj->Field1 = 'Field1 val';
247
        $obj->Field2 = null;
248
        $obj->MyDate = null;
249
        $docs = $index->add($obj);
250
        $value = $docs[0]->getField(SearchUpdaterTest_Container::class . '_Field1');
251
        $this->assertEquals('Field1 val', $value['value'], 'Writes non-NULL string fields');
252
        $value = $docs[0]->getField(SearchUpdaterTest_Container::class . '_Field2');
253
        $this->assertFalse($value, 'Ignores string fields if they are NULL');
254
        $value = $docs[0]->getField(SearchUpdaterTest_Container::class . '_MyDate');
255
        $this->assertFalse($value, 'Ignores date fields if they are NULL');
256
257
        $obj->MyDate = '2010-12-30';
258
        $docs = $index->add($obj);
259
        $value = $docs[0]->getField(SearchUpdaterTest_Container::class . '_MyDate');
260
        $this->assertEquals('2010-12-30T00:00:00Z', $value['value'], 'Writes non-NULL dates');
261
    }
262
263
    public function testAddFieldExtraOptions()
264
    {
265
        Injector::inst()->get(Kernel::class)->setEnvironment('live');
266
267
        $index = new SolrIndexTest_FakeIndex();
268
269
        $defs = simplexml_load_string('<fields>' . $index->getFieldDefinitions() . '</fields>');
270
        $defField1 = $defs->xpath('field[@name="' . SearchUpdaterTest_Container::class . '_Field1"]');
271
        $this->assertEquals((string)$defField1[0]['stored'], 'false');
272
273
        $index->addFilterField('Field1', null, array('stored' => 'true'));
274
        $defs = simplexml_load_string('<fields>' . $index->getFieldDefinitions() . '</fields>');
275
        $defField1 = $defs->xpath('field[@name="' . SearchUpdaterTest_Container::class . '_Field1"]');
276
        $this->assertEquals((string)$defField1[0]['stored'], 'true');
277
    }
278
279
    public function testAddAnalyzer()
280
    {
281
        $index = new SolrIndexTest_FakeIndex();
282
283
        $defs = simplexml_load_string('<fields>' . $index->getFieldDefinitions() . '</fields>');
284
        $defField1 = $defs->xpath('field[@name="' . SearchUpdaterTest_Container::class . '_Field1"]');
285
        $analyzers = $defField1[0]->analyzer;
286
        $this->assertFalse((bool)$analyzers);
287
288
        $index->addAnalyzer('Field1', 'charFilter', array('class' => 'solr.HTMLStripCharFilterFactory'));
289
        $defs = simplexml_load_string('<fields>' . $index->getFieldDefinitions() . '</fields>');
290
        $defField1 = $defs->xpath('field[@name="' . SearchUpdaterTest_Container::class . '_Field1"]');
291
        $analyzers = $defField1[0]->analyzer;
292
        $this->assertTrue((bool)$analyzers);
293
        $this->assertEquals('solr.HTMLStripCharFilterFactory', $analyzers[0]->charFilter[0]['class']);
294
    }
295
296
    public function testAddCopyField()
297
    {
298
        $index = new SolrIndexTest_FakeIndex();
299
        $index->addCopyField('sourceField', 'destField');
300
301
        $defs = simplexml_load_string('<fields>' . $index->getCopyFieldDefinitions() . '</fields>');
302
        $copyField = $defs->xpath('copyField');
303
304
        $this->assertEquals('sourceField', $copyField[0]['source']);
305
        $this->assertEquals('destField', $copyField[0]['dest']);
306
    }
307
308
    /**
309
     * Tests the setting of the 'stored' flag
310
     */
311
    public function testStoredFields()
312
    {
313
        // Test two fields
314
        $index = new SolrIndexTest_FakeIndex2();
315
        $index->addStoredField('Field1');
316
        $index->addFulltextField('Field2');
317
        $schema = $index->getFieldDefinitions();
318
        $this->assertContains(
319
            "<field name='" . SearchUpdaterTest_Container::class . "_Field1' type='text' indexed='true' stored='true'",
320
            $schema
321
        );
322
        $this->assertContains(
323
            "<field name='" . SearchUpdaterTest_Container::class . "_Field2' type='text' indexed='true' stored='false'",
324
            $schema
325
        );
326
327
        // Test with addAllFulltextFields
328
        $index2 = new SolrIndexTest_FakeIndex2();
329
        $index2->addAllFulltextFields();
330
        $index2->addStoredField('Field2');
331
        $schema2 = $index2->getFieldDefinitions();
332
        $this->assertContains(
333
            "<field name='" . SearchUpdaterTest_Container::class . "_Field1' type='text' indexed='true' stored='false'",
334
            $schema2
335
        );
336
        $this->assertContains(
337
            "<field name='" . SearchUpdaterTest_Container::class . "_Field2' type='text' indexed='true' stored='true'",
338
            $schema2
339
        );
340
    }
341
342
    public function testSanitiseClassName()
343
    {
344
        $index = new SolrIndexTest_FakeIndex2;
345
346
        $this->assertSame(
347
            'SilverStripe\\\\FullTextSearch\\\\Tests\\\\SolrIndexTest',
348
            $index->sanitiseClassName(static::class)
349
        );
350
351
        $this->assertSame(
352
            'SilverStripe-FullTextSearch-Tests-SolrIndexTest',
353
            $index->sanitiseClassName(static::class, '-')
354
        );
355
    }
356
357
    public function testGetIndexName()
358
    {
359
        $index = new SolrIndexTest_FakeIndex2;
360
        $this->assertSame(
361
            'SilverStripe-FullTextSearch-Tests-SolrIndexTest-SolrIndexTest_FakeIndex2',
362
            $index->getIndexName()
363
        );
364
    }
365
366
    public function testGetIndexNameWithPrefixAndSuffixFromEnvironment()
367
    {
368
        $index = new SolrIndexTest_FakeIndex2;
369
370
        Environment::putEnv('SS_SOLR_INDEX_PREFIX="foo_"');
371
        Environment::putEnv('SS_SOLR_INDEX_SUFFIX="_bar"');
372
373
        $this->assertSame(
374
            'foo_SilverStripe-FullTextSearch-Tests-SolrIndexTest-SolrIndexTest_FakeIndex2_bar',
375
            $index->getIndexName()
376
        );
377
    }
378
379
    protected function getFakeRawSolrResponse()
380
    {
381
        return new \Apache_Solr_Response(
382
            new \Apache_Solr_HttpTransport_Response(
383
                null,
384
                null,
385
                '{}'
386
            )
387
        );
388
    }
389
}
390