# Indexes

Enable, use and rebuild resource indexes.

## Indexes

Indexes allow fast lookup of records by a field value without scanning the entire resource. This is the standard way to resolve a slug from a URL to a record UUID.

### Enabling indexes

Add an `index` array to `.meta` listing the field names to index:

```json
{
  "fields": {
    "title": { "name": "Title", "type": "text", "required": true },
    "title_slug": { "name": "URL slug", "type": "slug", "source": "title" }
  },
  "index": ["title_slug"]
}
```

Every time a record is written (`data_create` / `data_update`), the index is updated automatically.

### How indexes are stored

For each indexed field, Nimbly creates an empty file at:

```
ext/data/<resource>/.index/<field_name>/<md5(field_value)>/<record_uuid>
```

For example, an article with `title_slug = "my-article"` and `uuid = "abc123"` would create:

```
ext/data/articles/.index/title_slug/1a79a4d60de6718e8e5b326e338ae533/abc123
```

The file is empty - its presence is the index entry. Storing under `.index/` keeps indexes separate from record files and ensures they are naturally skipped by directory scans (which skip dot-prefixed entries).

### With splitdir

When `splitdir: true` is also set, index directories follow the same two-level split:

```
ext/data/<resource>/.index/<field_name>/<aa>/<bb>/<md5(value)>/<record_uuid>
```

The data library handles this transparently - same API regardless of whether `splitdir` is set.

### Looking up a record by slug (route.inc pattern)

```php
<?php

$parts = router_match(__FILE__);
if ($parts === false || count($parts) !== 1) return;

$slug = $parts[0];
load_library('data');
load_library('util');

$records = data_read_index('articles', 'title_slug', md5_uuid($slug));
if (empty($records)) return;

$record = reset($records);
set_variable_dot('record', $record);
router_accept();
```

`data_read_index($resource, $index_name, $index_uuid)` returns an associative array of `uuid => record` for all records matching the indexed value. For slug lookups this is always one record.

### Rebuilding indexes

If you add `index` to an existing resource's `.meta`, existing records won't have index entries yet. Rebuild them with:

```bash
php core/cli/nimbly.php index:rebuild
# prompts: lists all indexed resources and asks you to choose

php core/cli/nimbly.php index:rebuild articles
# direct: reindexes the 'articles' resource immediately
```

The command is idempotent - safe to run multiple times. `reindex` still exists
as a legacy alias, but new documentation and scripts should use
`index:rebuild`.

## `index:rebuild`
Rebuilds index entries for an indexed resource. Use this after adding `index` to an existing resource's `.meta`, or after importing records outside of the normal API flow.

```bash
php core/cli/nimbly.php index:rebuild             # interactive: lists indexed resources, prompts for choice
php core/cli/nimbly.php index:rebuild articles    # direct: reindex the 'articles' resource
```

The command scans all records in the resource and creates any missing index files. It is idempotent - existing entries are left untouched.
