API Reference
Indexes API
Index configuration, enabling indexing, rebuilding, and the full-text search surface
#Index API
Indexes are configured on ContainerOptions.Indexes — a fluent IndexConfiguration builder (PhoenixmlDb.Core) — when a container is created. There is no live API to add, drop, list, or inspect an index against a container that already exists; a container's index set is fixed at creation. See Indexing for a conceptual walkthrough and Full-Text Search for the full-text index in depth.
#
IndexConfiguration
Reached via ContainerOptions.Indexes inside the configure delegate passed to CreateContainerAsync / OpenOrCreateContainerAsync. Every Add*Index method returns this, so calls chain.
#AddNameIndex
IndexConfiguration AddNameIndex(string? namespaceUri = null)
IndexConfiguration AddNameIndex(Uri? namespaceUri = null)
Speeds up element/attribute name lookups. Pass a namespace URI to restrict the index to that namespace, or null to index names in every namespace. Because both overloads default to null, calling AddNameIndex() with no argument is ambiguous (CS0121) — pass an explicit (string?)null / (Uri?)null, or a real URI.
opts.Indexes.AddNameIndex((string?)null);
#AddPathIndex
IndexConfiguration AddPathIndex(string pathPattern)
Speeds up path expression evaluation. pathPattern is an XPath-like pattern: / (child), // (descendant-or-self), @ (attribute), * (wildcard) — e.g. "/product/name", "//customer/address". Call it once per path; there is no multi-path overload, so index several paths with successive chained calls:
opts.Indexes
.AddPathIndex("/product/name")
.AddPathIndex("/product/category");
#AddValueIndex
IndexConfiguration AddValueIndex(string pathPattern, XdmValueType valueType, string? collation = null)
Enables typed range queries and sorting. valueType is PhoenixmlDb.Core.XdmValueType:
XdmString, XdmInteger, XdmLong, XdmDecimal, XdmDouble, XdmFloat,
Boolean, DateTime, Date, Time, Duration, AnyUri, QName,
Base64Binary, HexBinary
opts.Indexes.AddValueIndex("/product/price", XdmValueType.XdmDecimal);
collation is null (binary/codepoint comparison) unless you need locale-aware string ordering.
#AddFullTextIndex
IndexConfiguration AddFullTextIndex(string? pathPattern = null, FullTextIndexOptions? options = null)
Declares a Lucene-backed full-text index — see Full-Text Search for the write path, staleness guarantee, and operational detail this page doesn't repeat. pathPattern of null indexes every element, not the document as a whole.
opts.Indexes.AddFullTextIndex("/product/description", new FullTextIndexOptions
{
Language = "en",
Stemming = true,
CaseSensitive = false,
});
FullTextIndexOptions (PhoenixmlDb.Core):
|
Property |
Type |
Default |
Notes |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Accepted but has no effect — nothing reads it yet. |
#AddMetadataIndex
IndexConfiguration AddMetadataIndex(XdmQName metadataName, XdmValueType valueType = XdmValueType.XdmString)
IndexConfiguration AddMetadataIndex<T>(MetadataProperty<T> descriptor, XdmValueType valueType = XdmValueType.XdmString)
Enables efficient queries by document metadata key/value. The name is a qualified XdmQName (PhoenixmlDb.Xdm), not a bare string — metadata itself is namespaced (see Metadata), and an index declared for one namespace's key does not answer for another's.
The string-name metadata methods (SetMetadataAsync(documentName, "status", ...)) place the name in the container's default metadata namespace, so index that qualified name. Setting DefaultMetadataNamespace explicitly makes the name easy to build before the container exists:
using PhoenixmlDb.Xdm;
const string meta = "https://example.com/meta";
var status = new XdmQName(db.GetOrCreateNamespaceId(meta), "status");
var docs = await db.CreateContainerAsync("docs", opts =>
{
opts.DefaultMetadataNamespace = meta;
opts.Indexes.AddMetadataIndex(status, XdmValueType.XdmString);
});
await docs.SetMetadataAsync("d1.xml", "status", "draft"); // indexed under {meta}status
#EnableStructuralIndex
IndexConfiguration EnableStructuralIndex(bool enabled = true)
Controls the parent-child/sibling index that accelerates axis navigation (parent::, ancestor::, following-sibling::, ...). Enabled by default; disable only for containers that are never navigated with XQuery axis steps.
#Enabling and running indexing
#
DocumentDatabase.EnableIndexing()
// PhoenixmlDb.Indexing
public static IndexManager EnableIndexing(this DocumentDatabase db)
Declaring indexes in ContainerOptions.Indexes only records configuration; nothing is maintained until the owning process calls this once per DocumentDatabase. Returns the IndexManager used for every full-text operation below. Calling it again returns the manager already attached. The database owns the manager and disposes it when the database is disposed. Do not call it while holding a transaction from BeginWriteAsync: it may need the database write lock, which is not reentrant.
using var db = DocumentDatabase.Open(dbPath);
var manager = db.EnableIndexing();
#
DocumentDatabase.RebuildIndexesAsync
ValueTask<IndexRebuildResult> RebuildIndexesAsync(string containerName, CancellationToken cancellationToken = default)
Requires EnableIndexing() to have been called first. Clears the container's previous index entries, walks every stored document, and re-indexes each one — clearing the container's stale flag as part of the same commit. It runs as one write transaction under the database write lock, so writes to every container wait for it; do not call it while holding a transaction from BeginWriteAsync. Throws ContainerNotFoundException for an unknown container name.
var result = await db.RebuildIndexesAsync("products");
// result.DocumentsIndexed, result.EntriesRemoved, result.EntriesWritten
IndexRebuildResult is sealed record IndexRebuildResult(long DocumentsIndexed, long EntriesRemoved, long EntriesWritten).
#
DocumentDatabase.ContainersWithStaleIndexes()
IReadOnlyList<string> ContainersWithStaleIndexes()
Names every container whose indexes may be missing entries, so queries against it scan instead of using the indexes until it is rebuilt: a container written to while indexing was not enabled, a container last written by an engine that predates stale-tracking, or one whose persisted full-text index was analyzed differently from how the current process analyzes (EnableIndexing() marks those).
It also names a container whose full-text index cannot be read; the full-text indexer marks those. See Full-Text Search: An index that cannot be read.
#
DocumentDatabase.RebuildStaleIndexesAsync
ValueTask<IReadOnlyDictionary<string, IndexRebuildResult>> RebuildStaleIndexesAsync(CancellationToken cancellationToken = default)
Rebuilds every container listed by ContainersWithStaleIndexes(), one at a time, and returns each result by container name. Meant for startup, after EnableIndexing().
#Full-text search (IndexManager)
These members live on the IndexManager returned by EnableIndexing(). Full detail, including the exactness guarantee while the background worker lags, is on the Full-Text Search page — this is the member-by-member reference.
|
Member |
Signature |
Purpose |
|---|---|---|
|
|
|
Search; throws |
|
|
|
Documents the Lucene index does not yet (or no longer) correctly reflect. |
|
|
|
Diagnostic: entries currently held in the Lucene index. |
|
|
|
Applies up to |
|
|
|
Starts a background drain loop; disposing stops it and awaits completion. |
FullTextSearchHit is readonly record struct FullTextSearchHit(DocumentId Document, NodeId Node, double Score).
FullTextIndexingOptions (PhoenixmlDb.Indexing) controls the background loop:
|
Property |
Type |
Default |
Notes |
|---|---|---|---|
|
|
|
|
Maximum queued documents applied per drain. |
|
|
|
|
Per-batch time budget; bounds worst-case shutdown latency. |
|
|
|
|
How long the loop waits after a batch drains nothing. |
#Error Handling
SearchFullText throws InvalidOperationException when the container's indexes are stale — call RebuildIndexesAsync first (see Rebuilding). RebuildIndexesAsync throws InvalidOperationException if EnableIndexing() was never called. Beyond those two, index configuration errors surface as ordinary argument validation on IndexConfiguration's Add*Index methods (e.g. a null required path pattern) — there is no dedicated IndexExistsException / InvalidIndexDefinitionException family, because there is no live create/drop to raise them against.
#Next Steps
|
Concepts |
Execution |
Optimization |
|---|---|---|
|
Indexing Indexing concepts |
Queries API Query execution |
Full-Text Search The full-text index in depth |