feat: add v5.8.0 features - transactions, pagination, and comprehensive docs
**Transaction System (TIER 1.2)** - Atomic operations with automatic rollback - 36 unit tests + 35 integration tests passing - Full documentation in docs/transactions.md **Duplicate Check Optimization (TIER 1.4)** - Optimized from O(n) to O(log n) using GraphAdjacencyIndex - Uses LSM-tree for efficient lookups - Tests verify performance improvements **GraphIndex Pagination (TIER 1.5)** - Production-scale pagination for high-degree nodes - Backward compatible API - 18 pagination tests passing **Comprehensive Filter Documentation (TIER 1.6)** - Complete operator reference (15 operators) - Compound filters (anyOf, allOf, nested logic) - Common query patterns and troubleshooting guide - 642 lines of new documentation **README Updates** - Added Filter & Query Syntax Guide to Essential Reading - Added Transactions to Core Concepts section All changes tested and production-ready for v5.8.0 release. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
52e961760d
commit
e40fee39d8
21 changed files with 5657 additions and 182 deletions
|
|
@ -136,12 +136,48 @@ export class GraphAdjacencyIndex {
|
|||
|
||||
/**
|
||||
* Core API - Neighbor lookup with LSM-tree storage
|
||||
* Now O(log n) with bloom filter optimization (90% of queries skip disk I/O)
|
||||
*
|
||||
* O(log n) with bloom filter optimization (90% of queries skip disk I/O)
|
||||
* v5.8.0: Added pagination support for high-degree nodes
|
||||
*
|
||||
* @param id Entity ID to get neighbors for
|
||||
* @param optionsOrDirection Optional: direction string OR options object
|
||||
* @returns Array of neighbor IDs (paginated if limit/offset specified)
|
||||
*
|
||||
* @example
|
||||
* // Get all neighbors (backward compatible)
|
||||
* const all = await graphIndex.getNeighbors(id)
|
||||
*
|
||||
* @example
|
||||
* // Get outgoing neighbors (backward compatible)
|
||||
* const out = await graphIndex.getNeighbors(id, 'out')
|
||||
*
|
||||
* @example
|
||||
* // Get first 50 outgoing neighbors (new API)
|
||||
* const page1 = await graphIndex.getNeighbors(id, { direction: 'out', limit: 50 })
|
||||
*
|
||||
* @example
|
||||
* // Paginate through neighbors
|
||||
* const page1 = await graphIndex.getNeighbors(id, { limit: 100, offset: 0 })
|
||||
* const page2 = await graphIndex.getNeighbors(id, { limit: 100, offset: 100 })
|
||||
*/
|
||||
async getNeighbors(id: string, direction?: 'in' | 'out' | 'both'): Promise<string[]> {
|
||||
async getNeighbors(
|
||||
id: string,
|
||||
optionsOrDirection?: {
|
||||
direction?: 'in' | 'out' | 'both'
|
||||
limit?: number
|
||||
offset?: number
|
||||
} | 'in' | 'out' | 'both'
|
||||
): Promise<string[]> {
|
||||
await this.ensureInitialized()
|
||||
|
||||
// Normalize old API (direction string) to new API (options object)
|
||||
const options = typeof optionsOrDirection === 'string'
|
||||
? { direction: optionsOrDirection }
|
||||
: (optionsOrDirection || {})
|
||||
|
||||
const startTime = performance.now()
|
||||
const direction = options.direction || 'both'
|
||||
const neighbors = new Set<string>()
|
||||
|
||||
// Query LSM-trees with bloom filter optimization
|
||||
|
|
@ -159,7 +195,16 @@ export class GraphAdjacencyIndex {
|
|||
}
|
||||
}
|
||||
|
||||
const result = Array.from(neighbors)
|
||||
// Convert to array for pagination
|
||||
let result = Array.from(neighbors)
|
||||
|
||||
// Apply pagination if requested (v5.8.0)
|
||||
if (options?.limit !== undefined || options?.offset !== undefined) {
|
||||
const offset = options.offset || 0
|
||||
const limit = options.limit !== undefined ? options.limit : result.length
|
||||
result = result.slice(offset, offset + limit)
|
||||
}
|
||||
|
||||
const elapsed = performance.now() - startTime
|
||||
|
||||
// Performance assertion - should be sub-5ms with LSM-tree
|
||||
|
|
@ -172,13 +217,37 @@ export class GraphAdjacencyIndex {
|
|||
|
||||
/**
|
||||
* Get verb IDs by source - Billion-scale optimization for getVerbsBySource
|
||||
*
|
||||
* O(log n) LSM-tree lookup with bloom filter optimization
|
||||
* v5.7.1: Filters out deleted verb IDs (tombstone deletion workaround)
|
||||
* v5.8.0: Added pagination support for entities with many relationships
|
||||
*
|
||||
* @param sourceId Source entity ID
|
||||
* @returns Array of verb IDs originating from this source (excluding deleted)
|
||||
* @param options Optional configuration
|
||||
* @param options.limit Maximum number of verb IDs to return (default: all)
|
||||
* @param options.offset Number of verb IDs to skip (default: 0)
|
||||
* @returns Array of verb IDs originating from this source (excluding deleted, paginated if requested)
|
||||
*
|
||||
* @example
|
||||
* // Get all verb IDs (backward compatible)
|
||||
* const all = await graphIndex.getVerbIdsBySource(sourceId)
|
||||
*
|
||||
* @example
|
||||
* // Get first 50 verb IDs
|
||||
* const page1 = await graphIndex.getVerbIdsBySource(sourceId, { limit: 50 })
|
||||
*
|
||||
* @example
|
||||
* // Paginate through verb IDs
|
||||
* const page1 = await graphIndex.getVerbIdsBySource(sourceId, { limit: 100, offset: 0 })
|
||||
* const page2 = await graphIndex.getVerbIdsBySource(sourceId, { limit: 100, offset: 100 })
|
||||
*/
|
||||
async getVerbIdsBySource(sourceId: string): Promise<string[]> {
|
||||
async getVerbIdsBySource(
|
||||
sourceId: string,
|
||||
options?: {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
): Promise<string[]> {
|
||||
await this.ensureInitialized()
|
||||
|
||||
const startTime = performance.now()
|
||||
|
|
@ -193,18 +262,51 @@ export class GraphAdjacencyIndex {
|
|||
// Filter out deleted verb IDs (tombstone deletion workaround)
|
||||
// LSM-tree retains all IDs, but verbIdSet tracks deletions
|
||||
const allIds = verbIds || []
|
||||
return allIds.filter(id => this.verbIdSet.has(id))
|
||||
let result = allIds.filter(id => this.verbIdSet.has(id))
|
||||
|
||||
// Apply pagination if requested (v5.8.0)
|
||||
if (options?.limit !== undefined || options?.offset !== undefined) {
|
||||
const offset = options.offset || 0
|
||||
const limit = options.limit !== undefined ? options.limit : result.length
|
||||
result = result.slice(offset, offset + limit)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Get verb IDs by target - Billion-scale optimization for getVerbsByTarget
|
||||
*
|
||||
* O(log n) LSM-tree lookup with bloom filter optimization
|
||||
* v5.7.1: Filters out deleted verb IDs (tombstone deletion workaround)
|
||||
* v5.8.0: Added pagination support for popular target entities
|
||||
*
|
||||
* @param targetId Target entity ID
|
||||
* @returns Array of verb IDs pointing to this target (excluding deleted)
|
||||
* @param options Optional configuration
|
||||
* @param options.limit Maximum number of verb IDs to return (default: all)
|
||||
* @param options.offset Number of verb IDs to skip (default: 0)
|
||||
* @returns Array of verb IDs pointing to this target (excluding deleted, paginated if requested)
|
||||
*
|
||||
* @example
|
||||
* // Get all verb IDs (backward compatible)
|
||||
* const all = await graphIndex.getVerbIdsByTarget(targetId)
|
||||
*
|
||||
* @example
|
||||
* // Get first 50 verb IDs
|
||||
* const page1 = await graphIndex.getVerbIdsByTarget(targetId, { limit: 50 })
|
||||
*
|
||||
* @example
|
||||
* // Paginate through verb IDs
|
||||
* const page1 = await graphIndex.getVerbIdsByTarget(targetId, { limit: 100, offset: 0 })
|
||||
* const page2 = await graphIndex.getVerbIdsByTarget(targetId, { limit: 100, offset: 100 })
|
||||
*/
|
||||
async getVerbIdsByTarget(targetId: string): Promise<string[]> {
|
||||
async getVerbIdsByTarget(
|
||||
targetId: string,
|
||||
options?: {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
): Promise<string[]> {
|
||||
await this.ensureInitialized()
|
||||
|
||||
const startTime = performance.now()
|
||||
|
|
@ -219,7 +321,16 @@ export class GraphAdjacencyIndex {
|
|||
// Filter out deleted verb IDs (tombstone deletion workaround)
|
||||
// LSM-tree retains all IDs, but verbIdSet tracks deletions
|
||||
const allIds = verbIds || []
|
||||
return allIds.filter(id => this.verbIdSet.has(id))
|
||||
let result = allIds.filter(id => this.verbIdSet.has(id))
|
||||
|
||||
// Apply pagination if requested (v5.8.0)
|
||||
if (options?.limit !== undefined || options?.offset !== undefined) {
|
||||
const offset = options.offset || 0
|
||||
const limit = options.limit !== undefined ? options.limit : result.length
|
||||
result = result.slice(offset, offset + limit)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue