# Carry document permissions into the search index

Indexing protected documents is a data-modelling job as well as a search job. Build the ingestion path so content and access rules stay attached to each other.

By Cobnex editorial. Published 2026-09-10. Updated 2026-09-11.

## Define the smallest searchable record

Start with one document and write down what the application will retrieve from it. If a report becomes twenty passages, each passage needs a durable relationship to the report. Page numbers alone are insufficient because a revised document can move the same text to a different page.

Use a stable source identifier and an explicit source version. Add a passage identifier that is unique within that version. Keep the display title and source location for citations, but do not use the title as the primary identifier. Two departments can upload files with the same name.

Access metadata belongs in the same record as the searchable content. For a shared index, include the organisation boundary as well as any group-level permission. This makes it possible to reject a cross-organisation result even if group identifiers have been mapped incorrectly.

## Treat permission extraction as a required stage

An ingestion worker should obtain the content and its effective access rules from the source system. Effective rules are the rules that actually apply after inheritance, group membership and exceptions have been considered. Copying the permissions shown on one folder can be wrong when a document has a specific exception.

Do not publish a passage while its permissions are still being fetched. Put incomplete records in a staging area and record the reason they are not searchable. A queue entry such as "source permissions unavailable" gives an operator something to investigate. An empty permissions array must not silently mean public access.

The following is an application record, not a vendor API request. Its purpose is to show which facts should travel together.

```json
{
  "organisationId": "org-17",
  "sourceId": "contract-84",
  "sourceVersion": "v6",
  "passageId": "contract-84:v6:section-3",
  "allowedGroups": ["account-team-12"],
  "permissionVersion": "acl-29",
  "text": "The approved passage content"
}
```

## Make publication an observable transition

A source update may require replacing several passages. Decide how the serving path avoids mixing the new content with old permissions during that replacement. One option is to ingest a complete new version, verify its records, and then make that version eligible for queries. The exact mechanism depends on the index and the consistency it provides.

Record which version is published and how many passages belong to it. Counts are a useful reconciliation check, though they do not prove the content or permissions are correct. Sample records against the source and include documents with unusual permission inheritance.

Keep deletion in the design from the start. When the source is removed or access becomes restricted, the old passages must stop serving. A failed deletion deserves a visible retry state. It should not disappear into the same success count used for new uploads.

## Build the filter on the server

The query handler should obtain the caller's organisation and groups from trusted identity information. Build the search predicate through the supported query API and escape values according to that API. Do not accept a finished filter expression from the browser and assume it represents the user's rights.

Wrap this behaviour in the search access layer so ordinary callers cannot accidentally bypass it. Administrative tools need separate, explicit access. A convenient debug endpoint that searches the whole index can undo the boundary built into the main application.

Before importing the full collection, run a small fixture with a public document, a restricted document, a document with inherited permissions and a document with missing access metadata. Confirm which passages are searchable under each account. That small fixture becomes a useful regression test whenever ingestion or identity mapping changes.

## Sources

- [Microsoft Learn: security filter records and queries](https://learn.microsoft.com/en-us/azure/search/search-security-trimming-for-azure-search)
- [Microsoft Learn: updating and rebuilding an index](https://learn.microsoft.com/en-us/azure/search/search-howto-reindex)
