Delete a quad's ID-tuple from all three index stores. Must be called within a readwrite transaction covering all index stores.
Insert a quad's ID-tuple into all three index stores. Must be called within a readwrite transaction covering all index stores.
Scan the best index for the given pattern and yield matching QuadIds.
Uses getAllKeys() to fetch all matching keys in a single IDB request,
completing the transaction before yielding. This avoids the classic
TransactionInactiveError that occurs when yield suspends execution
between cursor.continue() calls, allowing the transaction to auto-commit.
Memory trade-off: all matching key arrays are loaded before yielding. Each key is 4 numbers (32 bytes), so 10k quads ≈ 320KB — acceptable for in-browser use. For the dominant use-case (querying with bound subject or predicate), the result set is small anyway.
Count quads matching the given pattern without materializing them.
Uses IDBObjectStore.count(keyRange) which is O(log n) in most IndexedDB implementations — far cheaper than iterating.
Limitation: when g is bound but s/p/o are not fully bound, the count may over-count (IDBKeyRange cannot express "middle" predicates). We fall back to iteration in that case.