Skip to content

Commit

Permalink
Index configuration overrides DO subsite field if NULL value
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark Anthony Adriano committed May 17, 2022
1 parent 4ad8b26 commit 7a55f29
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 32 deletions.
19 changes: 11 additions & 8 deletions docs/en/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ SilverStripe\SearchService\Service\IndexConfiguration:
Let's look at each relevant node:

* `myindex`: The name of the index. The rules on what this can be named will vary depending
on your service provider. For AppSearch, it should only contain lowercase letters, numbers,
on your service provider. For AppSearch, it should only contain lowercase letters, numbers,
and hyphens.

* `includedClasses`: A list of content classes to index. These are just the _source_ of the
content, so they have no contractual bind to the module. If they are dataobjects, they
content, so they have no contractual bind to the module. If they are dataobjects, they
should have the `SearchServiceExtension` applied, however. This is discussed further below.

* `SilverStripe\CMS\Model\SiteTree`: This class already has the necessary extension applied
Expand All @@ -46,7 +46,7 @@ so when it finds that the property `$content` doesn't exist on the `SiteTree` in
will use a case matching strategy as a fallback.

It is important to note that the keys of `fields` can be named anything you like, so long
as it is valid in your search service provider (for AppSearch, that's all lowercase and
as it is valid in your search service provider (for AppSearch, that's all lowercase and
underscores). There is no reason why `title` cannot be `document_title` for instance,
in the above configuration, as we've explicitly mapped the field to `Title`.

Expand Down Expand Up @@ -81,8 +81,8 @@ SilverStripe\SearchService\Service\IndexConfiguration:
property: 'Comments.Author.Name'
```

For DataObject content, the dot syntax allows traversal of relationships. If the final
property in the notation is on a list, it will use the `->column()` function to derive
For DataObject content, the dot syntax allows traversal of relationships. If the final
property in the notation is on a list, it will use the `->column()` function to derive
the values as an array.

This will roughly get indexed as a structure like this:
Expand Down Expand Up @@ -193,15 +193,15 @@ SilverStripe\Core\Injector\Injector:
constructor:
index_variant: '`MY_CUSTOM_VAR`'

```
```

This is useful if you have multiple staging environments and you don't want to overcrowd
your search instance with distinct indexes for each one.

## Full page indexing

Page and DataObject content is eligible for full-page indexing of its content. This is
predicated upon the object having a `Link()` method defined that can be rendered in a
predicated upon the object having a `Link()` method defined that can be rendered in a
controller.

The content is extracted using an XPath selector. By default, this is `//main`, but it
Expand Down Expand Up @@ -248,12 +248,15 @@ SilverStripe\SearchService\Service\IndexConfiguration:
Note the syntax to reduce the need for copy-paste if you want to duplicate the
same configuration across.

__Additional note__:
> In the sample above, if the data object (My\Other\Class) does not have a subsite ID, then it will be included in the indexing as it is explicitly defined in the index configuration
This is handled via `SubsiteIndexConfigurationExtension` - this logic could be
replicated for other scenarios like languages if required.

## More information

* [Usage](usage.md)
* [Implementations](implementations.md)
* [Customising and extending](customising.md)
* [Customising and extending](customising.md)
* [Overview and Rationale](overview.md)
31 changes: 26 additions & 5 deletions src/Extensions/Subsites/IndexConfigurationExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,35 @@ class IndexConfigurationExtension extends Extension
public function updateIndexesForDocument(DocumentInterface $doc, array &$indexes): void
{
$docSubsiteId = null;
/** @var DataObject $doc */
$dataObj = $doc->getDataObject();

if ($doc instanceof DataObjectDocument) {
$docSubsiteId = $doc->getDataObject()->SubsiteID ?? null;
$docSubsiteId = $dataObj->SubsiteID ?? null;
}

foreach ($indexes as $indexName => $data) {
$subsiteId = $data['subsite_id'] ?? 'all';
if ($subsiteId !== 'all' && $docSubsiteId !== $subsiteId) {
unset($indexes[$indexName]);

if ($docSubsiteId === null) {
// DataObject does not have a defined SubsiteID
// So if the developer explicitly defined the dataObject to be
// included in the Subsite Index configuration then
// allow the dataObject to be added in.
foreach ($indexes as $indexName => $data) {

// DataObject explicitly defined on Subsite index definition
$explicitClasses = $data['includeClasses'] ?? [];
if (!isset($explicitClasses[$dataObj->ClassName])) {
unset($indexes[$indexName]);
break;
}
}
} else {
foreach ($indexes as $indexName => $data) {
$subsiteId = $data['subsite_id'] ?? 'all';

if ($subsiteId !== 'all' && $docSubsiteId !== $subsiteId) {
unset($indexes[$indexName]);
}
}
}
}
Expand Down
94 changes: 75 additions & 19 deletions tests/DataObject/DataObjectDocumentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

namespace SilverStripe\SearchService\Tests\DataObject;

use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\RelationList;
use SilverStripe\SearchService\DataObject\DataObjectDocument;
use SilverStripe\SearchService\Exception\IndexConfigurationException;
use SilverStripe\SearchService\Extensions\SearchServiceExtension;
use SilverStripe\SearchService\Interfaces\DocumentAddHandler;
use SilverStripe\SearchService\Interfaces\DocumentRemoveHandler;
use SilverStripe\SearchService\Schema\Field;
use SilverStripe\SearchService\Service\IndexConfiguration;
use SilverStripe\SearchService\Tests\Fake\DataObjectFake;
use SilverStripe\SearchService\Tests\Fake\DataObjectFakeVersioned;
use SilverStripe\SearchService\Tests\Fake\DataObjectSubclassFake;
Expand All @@ -18,7 +23,6 @@
use SilverStripe\SearchService\Tests\SearchServiceTest;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\SearchService\Exception\IndexConfigurationException;
use SilverStripe\Versioned\Versioned;

class DataObjectDocumentTest extends SearchServiceTest
Expand Down Expand Up @@ -85,6 +89,58 @@ public function testShouldIndex()
$this->assertFalse($doc->shouldIndex());
}

public function testSubsiteShouldIndex()
{
$config = $this->mockConfig();

$config->set(
'indexes',
[
'index0' => [
'subsite_id' => 0,
'includeClasses' => [
VersionedDataObjectFake::class => true
]
],
'index1' => [
'subsite_id' => 1,
'includeClasses' => [
DataObjectFake::class => true
]
],
]
);

/** @var Versioned $dataobject */
$dataobject1 = new VersionedDataObjectFake([
'ID' => 6,
'ShowInSearch' => true,
'SubsiteID' => 2,
]);
$dataobject1->publishSingle();
$dataobject1->can_view = true;

$doc1 = DataObjectDocument::create($dataobject1);

// Incorrect SubsiteID for DO so should fail
$this->assertFalse($doc1->shouldIndex());

/** @var Versioned $dataobject */
$dataobject2 = new VersionedDataObjectFake([
'ID' => 7,
'ShowInSearch' => true,
'SubsiteID' => null,
]);
$dataobject2->publishSingle();
$dataobject2->can_view = true;

$doc2 = DataObjectDocument::create($dataobject2);

// SubsiteID is NULL for DO but explicitly defined in config so should be true.
$this->assertNull($dataobject2->SubsiteID);
$this->assertTrue($doc2->shouldIndex());
}

public function testMarkIndexed()
{
$dataobject = new DataObjectFake(['ShowInSearch' => true]);
Expand Down Expand Up @@ -166,24 +222,24 @@ public function testToArray()
// of a method, e.g. getMyArray(): array, so it isn't coerced into a DBField.


// // exceptions
// $config->set('getFieldsForClass', [
// DataObjectFake::class => [
// new Field('customgettermap', 'CustomGetterMap'),
// ]
// ]);
// $this->expectException(IndexConfigurationException::class);
// $this->expectExceptionMessageRegExp('/associative/');
// $doc->toArray();
//
// $this->expectException(IndexConfigurationException::class);
// $this->expectExceptionMessageRegExp('/non scalar/');
// $config->set('getFieldsForClass', [
// DataObjectFake::class => [
// new Field('customgettermixed', 'CustomGetterMixedArray'),
// ]
// ]);
// $doc->toArray();
// // exceptions
// $config->set('getFieldsForClass', [
// DataObjectFake::class => [
// new Field('customgettermap', 'CustomGetterMap'),
// ]
// ]);
// $this->expectException(IndexConfigurationException::class);
// $this->expectExceptionMessageRegExp('/associative/');
// $doc->toArray();
//
// $this->expectException(IndexConfigurationException::class);
// $this->expectExceptionMessageRegExp('/non scalar/');
// $config->set('getFieldsForClass', [
// DataObjectFake::class => [
// new Field('customgettermixed', 'CustomGetterMixedArray'),
// ]
// ]);
// $doc->toArray();

$this->expectException(IndexConfigurationException::class);
$this->expectExceptionMessageRegExp('/DataObject or RelationList/');
Expand Down

0 comments on commit 7a55f29

Please sign in to comment.