Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions electron/adapters/base-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Base adapter class with common functionality for all vector database adapters
*/

import {
VectorDatabase,
DatabaseProvider,
DatabaseCapabilities,
ConnectionConfig,
CollectionInfo,
CollectionStats,
CreateCollectionParams,
VectorRecord,
QueryParams,
QueryResult,
UpsertParams,
UpdateParams,
DeleteParams,
ListParams,
PaginatedResult,
FilterExpression,
} from './types'

export abstract class BaseVectorAdapter implements VectorDatabase {
abstract readonly provider: DatabaseProvider
abstract readonly capabilities: DatabaseCapabilities

protected connected = false
protected config: ConnectionConfig | null = null

// ============================================================================
// Connection (must be implemented by each adapter)
// ============================================================================

abstract connect(config: ConnectionConfig): Promise<void>

disconnect(): void {
this.connected = false
this.config = null
this.onDisconnect()
}

isConnected(): boolean {
return this.connected
}

/** Override to clean up provider-specific resources */
protected onDisconnect(): void {
// Default: no-op
}

// ============================================================================
// Collection Operations (must be implemented by each adapter)
// ============================================================================

abstract listCollections(): Promise<CollectionInfo[]>
abstract getCollectionStats(name: string): Promise<CollectionStats>
abstract createCollection(params: CreateCollectionParams): Promise<void>
abstract deleteCollection(name: string): Promise<void>

// ============================================================================
// Vector Operations (must be implemented by each adapter)
// ============================================================================

abstract listVectors(params: ListParams): Promise<PaginatedResult<VectorRecord>>
abstract fetchVectors(collectionName: string, ids: string[], namespace?: string): Promise<VectorRecord[]>
abstract queryVectors(params: QueryParams): Promise<QueryResult>
abstract upsertVectors(params: UpsertParams): Promise<void>
abstract updateVector(params: UpdateParams): Promise<void>
abstract deleteVectors(params: DeleteParams): Promise<void>

// ============================================================================
// Filter Translation Helpers
// ============================================================================

/**
* Translate unified filter expression to provider-specific format.
* Each adapter should override this with its own implementation.
*/
protected translateFilter(filter: FilterExpression | Record<string, unknown>): unknown {
// Default: pass through as-is (adapters should override)
return filter
}

// ============================================================================
// Common Utilities
// ============================================================================

protected ensureConnected(): void {
if (!this.connected) {
throw new Error(`${this.provider} adapter not connected. Call connect() first.`)
}
}

protected validateCollectionName(name: string): void {
if (!name || typeof name !== 'string') {
throw new Error('Collection name is required')
}
}

/**
* Batch an array into chunks of specified size
*/
protected batch<T>(items: T[], batchSize: number): T[][] {
const batches: T[][] = []
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize))
}
return batches
}

/**
* Retry an operation with exponential backoff
*/
protected async withRetry<T>(
operation: () => Promise<T>,
maxRetries = 3,
baseDelayMs = 1000
): Promise<T> {
let lastError: Error | null = null

for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation()
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error))

// Don't retry on client errors (4xx)
const status = (error as { status?: number }).status
if (status && status >= 400 && status < 500) {
throw lastError
}

if (attempt < maxRetries) {
const delay = baseDelayMs * Math.pow(2, attempt)
await this.sleep(delay)
}
}
}

throw lastError
}

protected sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
}
123 changes: 123 additions & 0 deletions electron/adapters/factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Database Adapter Factory
*
* Creates the appropriate adapter based on connection configuration.
* Lazy-loads SDK dependencies to minimize bundle impact.
*/

import {
VectorDatabase,
DatabaseProvider,
ConnectionConfig,
DatabaseAdapterFactory,
PineconeConnectionConfig,
QdrantConnectionConfig,
WeaviateConnectionConfig,
} from './types'

// Lazy-loaded adapter instances
let pineconeAdapterModule: typeof import('./pinecone-adapter') | null = null
let qdrantAdapterModule: typeof import('./qdrant-adapter') | null = null
let weaviateAdapterModule: typeof import('./weaviate-adapter') | null = null

/**
* Create a database adapter for the given configuration
*/
export async function createDatabaseAdapter(config: ConnectionConfig): Promise<VectorDatabase> {
switch (config.provider) {
case 'pinecone':
return createPineconeAdapter(config as PineconeConnectionConfig)
case 'qdrant':
return createQdrantAdapter(config as QdrantConnectionConfig)
case 'weaviate':
return createWeaviateAdapter(config as WeaviateConnectionConfig)
default:
throw new Error(`Unknown database provider: ${(config as ConnectionConfig).provider}`)
}
}

async function createPineconeAdapter(config: PineconeConnectionConfig): Promise<VectorDatabase> {
if (!pineconeAdapterModule) {
pineconeAdapterModule = await import('./pinecone-adapter')
}
const adapter = new pineconeAdapterModule.PineconeAdapter()
await adapter.connect(config)
return adapter
}

async function createQdrantAdapter(config: QdrantConnectionConfig): Promise<VectorDatabase> {
if (!qdrantAdapterModule) {
qdrantAdapterModule = await import('./qdrant-adapter')
}
const adapter = new qdrantAdapterModule.QdrantAdapter()
await adapter.connect(config)
return adapter
}

async function createWeaviateAdapter(config: WeaviateConnectionConfig): Promise<VectorDatabase> {
if (!weaviateAdapterModule) {
weaviateAdapterModule = await import('./weaviate-adapter')
}
const adapter = new weaviateAdapterModule.WeaviateAdapter()
await adapter.connect(config)
return adapter
}

/**
* Get list of supported database providers
*/
export function getSupportedProviders(): DatabaseProvider[] {
return ['pinecone', 'qdrant', 'weaviate']
}

/**
* Factory class implementation (for dependency injection patterns)
*/
export class VectorDatabaseFactory implements DatabaseAdapterFactory {
private adapterCache = new Map<string, VectorDatabase>()

async create(config: ConnectionConfig): Promise<VectorDatabase> {
// Use connection name as cache key
const cacheKey = `${config.provider}:${config.name}`

// Return cached adapter if exists and connected
const cached = this.adapterCache.get(cacheKey)
if (cached?.isConnected()) {
return cached
}

// Create new adapter
const adapter = await createDatabaseAdapter(config)
this.adapterCache.set(cacheKey, adapter)
return adapter
}

getSupportedProviders(): DatabaseProvider[] {
return getSupportedProviders()
}

/**
* Disconnect and remove a specific adapter from cache
*/
removeAdapter(provider: DatabaseProvider, name: string): void {
const cacheKey = `${provider}:${name}`
const adapter = this.adapterCache.get(cacheKey)
if (adapter) {
adapter.disconnect()
this.adapterCache.delete(cacheKey)
}
}

/**
* Disconnect all cached adapters
*/
disconnectAll(): void {
for (const adapter of this.adapterCache.values()) {
adapter.disconnect()
}
this.adapterCache.clear()
}
}

// Singleton factory instance
export const databaseFactory = new VectorDatabaseFactory()
24 changes: 24 additions & 0 deletions electron/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Multi-Database Vector Explorer - Adapters Module
*
* Export all adapters and types for use throughout the application.
*/

// Types
export * from './types'

// Base adapter
export { BaseVectorAdapter } from './base-adapter'

// Provider adapters
export { PineconeAdapter } from './pinecone-adapter'
export { QdrantAdapter } from './qdrant-adapter'
export { WeaviateAdapter } from './weaviate-adapter'

// Factory
export {
createDatabaseAdapter,
getSupportedProviders,
VectorDatabaseFactory,
databaseFactory,
} from './factory'
Loading