Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 102 additions & 9 deletions en/02_Developer_Guides/00_Model/12_Indexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ The Silverstripe CMS framework already places certain indexes for you by default
- The `ClassName` column if your model inherits from `DataObject`
- All relationships defined in the model have indexes for their `has_one` entity (for `many_many` relationships
this index is present on the associative entity).
- All fields used in `default_sort` configuration
- All fields used in `default_sort` configuration (see [`default_sort` index mode](#default-sort-index-mode) below for additional options)
- Some built-in models (such as [`Member`](api:SilverStripe\Security\Member)) have specific indexes added
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this last note for completion's sake


## Defining an index

Expand All @@ -39,7 +40,7 @@ use SilverStripe\ORM\DataObject;

class MyObject extends DataObject
{
private static $indexes = [
private static array $indexes = [
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated - just updating the config references in this page to follow latest best practice

'<column-name>' => true,
'<index-name>' => [
'type' => '<type>',
Expand Down Expand Up @@ -74,12 +75,12 @@ use SilverStripe\ORM\DataObject;

class MyTestObject extends DataObject
{
private static $db = [
private static array $db = [
'MyField' => 'Varchar',
'MyOtherField' => 'Varchar',
];

private static $indexes = [
private static array $indexes = [
'MyIndexName' => ['MyField', 'MyOtherField'],
];
}
Expand All @@ -90,21 +91,113 @@ class MyTestObject extends DataObject
For complex queries it may be necessary to define a complex or composite index on the supporting object. To create a
composite index, define the fields in the index order as a comma separated list.

- index (col1) - `WHERE col1 = ?`
- index (col1, col2) = `WHERE (col1 = ? AND col2 = ?)`
- index (col1, col2, col3) = `WHERE (col1 = ? AND col2 = ? AND col3 = ?)`
```php
namespace App\Model;

use SilverStripe\ORM\DataObject;

class MyTestObject extends DataObject
{
// ...
private static array $indexes = [
'MyCompositeIndex' => ['MyField', 'MyOtherField', 'MyThirdField'],
];
}
```

Composite indexes can be used for any query that uses the fields in this specific order, include queries that omit columns from later in the index. For example, the index would be used for these queries:

The index would not be used for a query `WHERE col2 = ?` or for `WHERE col1 = ? OR col2 = ?`
- `WHERE MyField = ?`
- `WHERE (MyField = ? AND MyOtherField = ?)`
- `WHERE (MyField = ? AND MyOtherField = ? AND MyThirdField = ?)`

The index would not be used for a query `WHERE MyOtherField = ?` (because the first column is not used in this query) or for `WHERE MyField = ? OR MyOtherField = ?` (because the database has to check all rows to see if the `MyOtherField` column matches on its own).

As an alternative to a composite index, you can also create a hashed column which is a combination of information from
other columns. If this is indexed, smaller and reasonably unique it might be faster that an index on the whole column.

### Directionality

You can choose to define the direction for each column in the index. This can be useful if you know you usually sort a model in a specific direction. It's crucial for composite indexes if you are sorting on one column in a different direction than the others.

The direction is defined by adding either `ASC` or `DESC` after the column name. `ASC` is implied if no direction is specified.

```php
namespace App\Model;

use SilverStripe\ORM\DataObject;

class MyTestObject extends DataObject
{
// ...
private static array $indexes = [
'MyCompositeIndex' => ['MyField ASC', 'MyOtherField DESC'],
];
}
```

### `default_sort` index mode {#default-sort-index-mode}

By default, if the [`default_sort`](api:SilverStripe\ORM\DataObject->default_sort) configuration for your `DataObject` subclass specifies multiple columns, the ORM will create an index for each column as well as a composite index that includes all of the columns in your sort order. The composite index is always called `default_sort_composite`.

You can change what indexes will be created by setting the [`default_sort_index_mode`](api:SilverStripe\ORM\DataObject->default_sort_index_mode) configuration for your class. It takes the following options:

|Constant|Value|What it does|
|---|---|---|
|[`DataObjectSchema::SORT_INDEX_MODE_NONE`](api:SilverStripe\ORM\DataObjectSchema::SORT_INDEX_MODE_NONE)|none|Do not create any indexes for `default_sort`. Implies you will define your own indexes.|
|[`DataObjectSchema::SORT_INDEX_MODE_SINGLE`](api:SilverStripe\ORM\DataObjectSchema::SORT_INDEX_MODE_SINGLE)|single|Do not create a composite index, only create an index for each column.|
|[`DataObjectSchema::SORT_INDEX_MODE_COMPOSITE`](api:SilverStripe\ORM\DataObjectSchema::SORT_INDEX_MODE_COMPOSITE)|composite|Create only a composite index, not an index for each column.|
|[`DataObjectSchema::SORT_INDEX_MODE_BOTH`](api:SilverStripe\ORM\DataObjectSchema::SORT_INDEX_MODE_BOTH)|both|Create both a composite index and an index for each column (this is the default).|

> [!WARNING]
> Note that the ORM cannot create composite indexes that include columns in a different table (e.g. from a parent class or a relation).

If you have the following classes:

```php
namespace App\Model;

use SilverStripe\ORM\DataObject;

class MyTestObject extends DataObject
{
private static array $db = [
'MyField' => 'Varchar',
'MyOtherField' => 'Varchar',
];

private static array $default_sort = 'ID';
}
```

```php
namespace App\Model;

class MySubclass extends MyTestObject
{
private static array $db = [
'MyThirdField' => 'Varchar',
];

private static array $default_sort = 'MyField, MyOtherField, MyThirdField';
}
```

The ORM will not create a composite index for either class. It doesn't create one for `MyTestObject` because its `default_sort` definition only includes one column, and it doesn't create one for `MySubclass` because the first two columns it references belong on the superclass table, not the subclass table.

> [!NOTE]
> If you set `default_sort_index_mode` to "Composite" but your `default_sort` configuration only contains a single column, a `default_sort_composite` index will be created, though it will only contain that single column.

In these situations you may want to create your own composite index - for this example it would be an index with the first two columns in the `MyTestObject` table, as it is likely this index would be used when sorting `MySubclass` records.

Note that `ID` as a column at the *end* of your sort order won't be included in any composite indexes, because the database already implicitly adds that to all indexes.

## Index creation/destruction

Indexes are generated and removed automatically when building the database based on your configuration. Caution if you're working with large tables and
modify an index as the next time the database is built it will `DROP` the index, and then `ADD` it.

Note that the ORM won't automatically drop indexes if you remove them from the `indexes` configuration array. Instead, you need to set the value to `false` like so:
Note that the ORM won't automatically drop indexes if you remove them from the `indexes` configuration array or update your `default_sort` configuration. Instead, you need to set the value to `false` like so:

```php
namespace App\Model;
Expand Down
10 changes: 10 additions & 0 deletions en/08_Changelogs/6.1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ To remedy this, we've made the following changes:
If you are using a different adapter that might support globbing such as using FTP (for specific FTP servers), consider implementing the `GlobContentLister` interface.
If you use your own implementation of [`FileIDHelper`](api:SilverStripe\Assets\FilenameParsing\FileIDHelper) consider also implementing the `GlobbableFileIDHelper` interface.

#### Composite indexes for `default_sort` {#sort-indexes}

The ORM automatically creates an index for each column in the [`default_sort`](api:SilverStripe\ORM\DataObject->default_sort) configuration for your `DataObject` subclasses. This index might only be useful for part of the sort, or in some cases ignored entirely if the database server decides it will be faster to just traverse the whole table itself.

To remedy this, a new [`DataObject.default_sort_index_mode`](api:SilverStripe\ORM\DataObject->default_sort_index_mode) configuration property has been added, which by default will tell the ORM to create a composite index (if you're sorting by multiple fields) in addition to the individual column indexes it was already making.

When created, the composite index will always be called `default_sort_composite`.

There are some caveats to this, along with some other options you might want to set, so check out the [indexes documentation](/developer_guides/model/indexes/) for more details.

### Drop indexes in `indexes` configuration {#drop-indexes}

If you define some database index in the [`indexes`](api:SilverStripe\ORM\DataObject->indexes) configuration for a `DataObject` model and then remove that index from the configuration, the ORM won't drop that index for you. This means you can have indexes taking up space in your database that you don't want anymore.
Expand Down