feat: Add Neo4j support to txt2kg

Add Neo4j as an alternative graph database backend alongside ArangoDB.
This includes updates to the frontend components, API routes, and
docker-compose configuration for Neo4j integration.
This commit is contained in:
Santosh Bhavani 2025-12-12 10:58:59 -08:00
parent 6ef03c813f
commit 6105731e70
No known key found for this signature in database
GPG Key ID: 6C8EB1DD3AD81AF0
10 changed files with 228 additions and 90 deletions

View File

@ -7,7 +7,13 @@ services:
ports: ports:
- '3001:3000' - '3001:3000'
environment: environment:
- ARANGODB_URL=http://arangodb:8529 # Neo4j configuration (ARM64 compatible - works with 64KB pages)
- NEO4J_URI=bolt://neo4j:7687
- NEO4J_USER=neo4j
- NEO4J_PASSWORD=password123
- GRAPH_DB_TYPE=neo4j
# ArangoDB disabled - set to localhost to prevent DNS errors if accidentally accessed
- ARANGODB_URL=http://localhost:8529
- ARANGODB_DB=txt2kg - ARANGODB_DB=txt2kg
- QDRANT_URL=http://qdrant:6333 - QDRANT_URL=http://qdrant:6333
- VECTOR_DB_TYPE=qdrant - VECTOR_DB_TYPE=qdrant
@ -31,32 +37,47 @@ services:
- txt2kg-network - txt2kg-network
- pinecone-net - pinecone-net
depends_on: depends_on:
- arangodb neo4j:
- ollama condition: service_healthy
ollama:
condition: service_started
# Optional: sentence-transformers and entity-embeddings are only needed for vector search # Optional: sentence-transformers and entity-embeddings are only needed for vector search
# Traditional graph search works without these services # Traditional graph search works without these services
arangodb:
image: arangodb:latest # Neo4j - ARM64 compatible graph database (works with 64KB page size kernel)
neo4j:
image: neo4j:5-community
ports: ports:
- '8529:8529' - '7474:7474' # HTTP
- '7687:7687' # Bolt
environment: environment:
- ARANGO_NO_AUTH=1 - NEO4J_AUTH=neo4j/password123
- NEO4J_server_memory_heap_initial__size=512m
- NEO4J_server_memory_heap_max__size=2G
volumes: volumes:
- arangodb_data:/var/lib/arangodb3 - neo4j_data:/data
- arangodb_apps_data:/var/lib/arangodb3-apps - neo4j_logs:/logs
arangodb-init: networks:
image: arangodb:latest - default
depends_on: restart: unless-stopped
arangodb: healthcheck:
condition: service_started test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:7474 || exit 1"]
restart: on-failure interval: 15s
entrypoint: > timeout: 10s
sh -c " retries: 10
echo 'Waiting for ArangoDB to start...' && start_period: 60s
sleep 10 &&
echo 'Creating txt2kg database...' && # ArangoDB disabled - doesn't support ARM64 with 64KB page size
arangosh --server.endpoint tcp://arangodb:8529 --server.authentication false --javascript.execute-string 'try { db._createDatabase(\"txt2kg\"); console.log(\"Database txt2kg created successfully!\"); } catch(e) { if(e.message.includes(\"duplicate\")) { console.log(\"Database txt2kg already exists\"); } else { throw e; } }' # Uncomment if using 4KB page kernel (linux-nvidia-6.14)
" # arangodb:
# image: arangodb:latest
# ports:
# - '8529:8529'
# environment:
# - ARANGO_NO_AUTH=1
# volumes:
# - arangodb_data:/var/lib/arangodb3
# - arangodb_apps_data:/var/lib/arangodb3-apps
ollama: ollama:
build: build:
context: ../services/ollama context: ../services/ollama
@ -70,11 +91,14 @@ services:
environment: environment:
- NVIDIA_VISIBLE_DEVICES=all # Make all GPUs visible to the container - NVIDIA_VISIBLE_DEVICES=all # Make all GPUs visible to the container
- NVIDIA_DRIVER_CAPABILITIES=compute,utility # Required capabilities for CUDA - NVIDIA_DRIVER_CAPABILITIES=compute,utility # Required capabilities for CUDA
- CUDA_VISIBLE_DEVICES=0 # Use first GPU
- OLLAMA_FLASH_ATTENTION=1 # Enable flash attention for better performance - OLLAMA_FLASH_ATTENTION=1 # Enable flash attention for better performance
- OLLAMA_KEEP_ALIVE=30m # Keep models loaded for 30 minutes - OLLAMA_KEEP_ALIVE=30m # Keep models loaded for 30 minutes
- OLLAMA_NUM_PARALLEL=4 # Process 4 requests in parallel - DGX Spark has unified memory - OLLAMA_NUM_PARALLEL=4 # Process 4 requests in parallel - DGX Spark has unified memory
- OLLAMA_MAX_LOADED_MODELS=1 # Load only one model at a time to avoid VRAM contention - OLLAMA_MAX_LOADED_MODELS=1 # Load only one model at a time to avoid VRAM contention
- OLLAMA_KV_CACHE_TYPE=q8_0 # Reduce KV cache VRAM usage with minimal performance impact - OLLAMA_KV_CACHE_TYPE=q8_0 # Reduce KV cache VRAM usage with minimal performance impact
- OLLAMA_GPU_LAYERS=-1 # Force all layers on GPU
- OLLAMA_LLM_LIBRARY=cuda # Force CUDA backend
networks: networks:
- default - default
restart: unless-stopped restart: unless-stopped
@ -161,10 +185,12 @@ services:
- vector-search - vector-search
volumes: volumes:
arangodb_data: neo4j_data:
arangodb_apps_data: neo4j_logs:
ollama_data: ollama_data:
qdrant_data: qdrant_data:
# arangodb_data:
# arangodb_apps_data:
networks: networks:
default: default:

View File

@ -88,13 +88,18 @@ async function ensureConnection(request?: NextRequest): Promise<GraphDBType> {
* GET handler for retrieving graph data from the selected graph database * GET handler for retrieving graph data from the selected graph database
*/ */
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
console.log('[graph-db GET] Request received');
try { try {
// Initialize with connection parameters // Initialize with connection parameters
console.log('[graph-db GET] Ensuring connection...');
const graphDbType = await ensureConnection(request); const graphDbType = await ensureConnection(request);
console.log(`[graph-db GET] Using database type: ${graphDbType}`);
const graphDbService = getGraphDbService(graphDbType); const graphDbService = getGraphDbService(graphDbType);
// Get graph data from the database // Get graph data from the database
console.log('[graph-db GET] Fetching graph data...');
const graphData = await graphDbService.getGraphData(); const graphData = await graphDbService.getGraphData();
console.log(`[graph-db GET] Got ${graphData.nodes.length} nodes, ${graphData.relationships.length} relationships`);
// Transform to format expected by the frontend // Transform to format expected by the frontend
const nodes = graphData.nodes.map(node => ({ const nodes = graphData.nodes.map(node => ({

View File

@ -17,8 +17,26 @@
import { NextRequest, NextResponse } from 'next/server'; import { NextRequest, NextResponse } from 'next/server';
import { GraphDBType } from '@/lib/graph-db-service'; import { GraphDBType } from '@/lib/graph-db-service';
// In-memory storage for settings // In-memory storage for settings - use lazy initialization for env vars
// because they're not available at build time, only at runtime
let serverSettings: Record<string, string> = {}; let serverSettings: Record<string, string> = {};
let settingsInitialized = false;
function ensureSettingsInitialized() {
if (!settingsInitialized) {
// Read environment variables at runtime, not build time
serverSettings = {
graph_db_type: process.env.GRAPH_DB_TYPE || 'arangodb',
neo4j_uri: process.env.NEO4J_URI || '',
neo4j_user: process.env.NEO4J_USER || process.env.NEO4J_USERNAME || '',
neo4j_password: process.env.NEO4J_PASSWORD || '',
arangodb_url: process.env.ARANGODB_URL || '',
arangodb_db: process.env.ARANGODB_DB || '',
};
settingsInitialized = true;
console.log(`[SETTINGS] Initialized at runtime with GRAPH_DB_TYPE: "${serverSettings.graph_db_type}"`);
}
}
/** /**
* API Route to sync client settings with server environment variables * API Route to sync client settings with server environment variables
@ -27,13 +45,16 @@ let serverSettings: Record<string, string> = {};
*/ */
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
// Ensure settings are initialized from env vars first
ensureSettingsInitialized();
const { settings } = await request.json(); const { settings } = await request.json();
if (!settings || typeof settings !== 'object') { if (!settings || typeof settings !== 'object') {
return NextResponse.json({ error: 'Settings object is required' }, { status: 400 }); return NextResponse.json({ error: 'Settings object is required' }, { status: 400 });
} }
// Update server settings // Update server settings (merge with existing)
serverSettings = { ...serverSettings, ...settings }; serverSettings = { ...serverSettings, ...settings };
// Log some important settings for debugging // Log some important settings for debugging
@ -58,6 +79,9 @@ export async function POST(request: NextRequest) {
*/ */
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
try { try {
// Ensure settings are initialized from env vars first
ensureSettingsInitialized();
const url = new URL(request.url); const url = new URL(request.url);
const key = url.searchParams.get('key'); const key = url.searchParams.get('key');
@ -84,12 +108,32 @@ export async function GET(request: NextRequest) {
* For use in other API routes * For use in other API routes
*/ */
export function getSetting(key: string): string | null { export function getSetting(key: string): string | null {
ensureSettingsInitialized();
return serverSettings[key] || null; return serverSettings[key] || null;
} }
/** /**
* Get the currently selected graph database type * Get the currently selected graph database type
* Priority: serverSettings > environment variable > default 'arangodb'
*/ */
export function getGraphDbType(): GraphDBType { export function getGraphDbType(): GraphDBType {
return (serverSettings.graph_db_type as GraphDBType) || 'arangodb'; // Ensure settings are initialized from runtime environment variables
ensureSettingsInitialized();
// Check serverSettings (initialized from env vars or updated by client)
if (serverSettings.graph_db_type) {
console.log(`[getGraphDbType] Returning: "${serverSettings.graph_db_type}"`);
return serverSettings.graph_db_type as GraphDBType;
}
// Direct fallback to runtime environment variable
const envType = process.env.GRAPH_DB_TYPE;
if (envType) {
console.log(`[getGraphDbType] Returning from env: "${envType}"`);
return envType as GraphDBType;
}
// Default to arangodb for backwards compatibility
console.log(`[getGraphDbType] Returning default: "arangodb"`);
return 'arangodb';
} }

View File

@ -57,24 +57,34 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
setGraphError(null) setGraphError(null)
try { try {
// Get database type from localStorage // Get database type from localStorage, fall back to fetching from server
const graphDbType = localStorage.getItem("graph_db_type") || "arangodb" let graphDbType = localStorage.getItem("graph_db_type")
if (!graphDbType) {
// Fetch server's default (from GRAPH_DB_TYPE env var)
try {
const settingsRes = await fetch('/api/settings')
const settingsData = await settingsRes.json()
graphDbType = settingsData.settings?.graph_db_type || 'neo4j'
} catch {
graphDbType = 'neo4j'
}
}
setDbType(graphDbType === "arangodb" ? "ArangoDB" : "Neo4j") setDbType(graphDbType === "arangodb" ? "ArangoDB" : "Neo4j")
if (graphDbType === "neo4j") { if (graphDbType === "neo4j") {
// Neo4j connection logic // Neo4j connection logic - use the unified graph-db endpoint
const dbUrl = localStorage.getItem("NEO4J_URL") const dbUrl = localStorage.getItem("NEO4J_URL")
const dbUsername = localStorage.getItem("NEO4J_USERNAME") const dbUsername = localStorage.getItem("NEO4J_USERNAME")
const dbPassword = localStorage.getItem("NEO4J_PASSWORD") const dbPassword = localStorage.getItem("NEO4J_PASSWORD")
// Add query parameters if credentials exist // Add query parameters with type=neo4j
const queryParams = new URLSearchParams() const queryParams = new URLSearchParams()
queryParams.append("type", "neo4j")
if (dbUrl) queryParams.append("url", dbUrl) if (dbUrl) queryParams.append("url", dbUrl)
if (dbUsername) queryParams.append("username", dbUsername) if (dbUsername) queryParams.append("username", dbUsername)
if (dbPassword) queryParams.append("password", dbPassword) if (dbPassword) queryParams.append("password", dbPassword)
const queryString = queryParams.toString() const endpoint = `/api/graph-db?${queryParams.toString()}`
const endpoint = queryString ? `/api/neo4j?${queryString}` : '/api/neo4j'
const response = await fetch(endpoint) const response = await fetch(endpoint)
@ -98,21 +108,21 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
setConnectionUrl(dbUrl) setConnectionUrl(dbUrl)
} }
} else { } else {
// ArangoDB connection logic // ArangoDB connection logic - use the unified graph-db endpoint with type=arangodb
const arangoUrl = localStorage.getItem("arango_url") || "http://localhost:8529" const arangoUrl = localStorage.getItem("arango_url") || "http://localhost:8529"
const arangoDb = localStorage.getItem("arango_db") || "txt2kg" const arangoDb = localStorage.getItem("arango_db") || "txt2kg"
const arangoUser = localStorage.getItem("arango_user") || "" const arangoUser = localStorage.getItem("arango_user") || ""
const arangoPassword = localStorage.getItem("arango_password") || "" const arangoPassword = localStorage.getItem("arango_password") || ""
// Add query parameters if credentials exist // Add query parameters with type=arangodb
const queryParams = new URLSearchParams() const queryParams = new URLSearchParams()
queryParams.append("type", "arangodb")
if (arangoUrl) queryParams.append("url", arangoUrl) if (arangoUrl) queryParams.append("url", arangoUrl)
if (arangoDb) queryParams.append("dbName", arangoDb) if (arangoDb) queryParams.append("dbName", arangoDb)
if (arangoUser) queryParams.append("username", arangoUser) if (arangoUser) queryParams.append("username", arangoUser)
if (arangoPassword) queryParams.append("password", arangoPassword) if (arangoPassword) queryParams.append("password", arangoPassword)
const queryString = queryParams.toString() const endpoint = `/api/graph-db?${queryParams.toString()}`
const endpoint = queryString ? `/api/graph-db?${queryString}` : '/api/graph-db'
const response = await fetch(endpoint) const response = await fetch(endpoint)
@ -144,7 +154,8 @@ export function DatabaseConnection({ className }: DatabaseConnectionProps) {
// Disconnect from graph database // Disconnect from graph database
const disconnectGraph = async () => { const disconnectGraph = async () => {
try { try {
const graphDbType = localStorage.getItem("graph_db_type") || "arangodb" // Use current dbType state which was already determined from server/localStorage
const graphDbType = dbType === "Neo4j" ? "neo4j" : "arangodb"
const endpoint = graphDbType === "neo4j" ? '/api/neo4j/disconnect' : '/api/graph-db/disconnect' const endpoint = graphDbType === "neo4j" ? '/api/neo4j/disconnect' : '/api/graph-db/disconnect'
const response = await fetch(endpoint, { const response = await fetch(endpoint, {

View File

@ -171,9 +171,20 @@ export function SettingsModal() {
setIsS3Connected(s3Connected) setIsS3Connected(s3Connected)
} }
// Load graph DB type // Load graph DB type - fetch from server if not in localStorage
const storedGraphDbType = localStorage.getItem("graph_db_type") || "arangodb" const storedGraphDbType = localStorage.getItem("graph_db_type")
if (storedGraphDbType) {
setGraphDbType(storedGraphDbType as GraphDBType) setGraphDbType(storedGraphDbType as GraphDBType)
} else {
// Fetch server's default (from GRAPH_DB_TYPE env var)
fetch('/api/settings')
.then(res => res.json())
.then(data => {
const serverDefault = data.settings?.graph_db_type || 'neo4j'
setGraphDbType(serverDefault as GraphDBType)
})
.catch(() => setGraphDbType('neo4j'))
}
// Load Neo4j settings // Load Neo4j settings
setNeo4jUrl(localStorage.getItem("neo4j_url") || "") setNeo4jUrl(localStorage.getItem("neo4j_url") || "")

View File

@ -37,7 +37,15 @@ export class BackendService {
private modelName: string = 'all-MiniLM-L6-v2'; private modelName: string = 'all-MiniLM-L6-v2';
private static instance: BackendService; private static instance: BackendService;
private initialized: boolean = false; private initialized: boolean = false;
private activeGraphDbType: GraphDBType = 'arangodb'; private activeGraphDbType: GraphDBType | null = null; // Set at runtime, not build time
private getRuntimeGraphDbType(): GraphDBType {
if (this.activeGraphDbType === null) {
this.activeGraphDbType = (process.env.GRAPH_DB_TYPE as GraphDBType) || 'arangodb';
console.log(`[BackendService] Initialized activeGraphDbType at runtime: ${this.activeGraphDbType}`);
}
return this.activeGraphDbType;
}
private constructor() { private constructor() {
this.graphDBService = GraphDBService.getInstance(); this.graphDBService = GraphDBService.getInstance();
@ -64,16 +72,17 @@ export class BackendService {
/** /**
* Initialize the backend services * Initialize the backend services
* @param graphDbType - Type of graph database to use (neo4j or arangodb) * @param graphDbType - Type of graph database to use (defaults to GRAPH_DB_TYPE env var)
*/ */
public async initialize(graphDbType: GraphDBType = 'arangodb'): Promise<void> { public async initialize(graphDbType?: GraphDBType): Promise<void> {
this.activeGraphDbType = graphDbType; const dbType = graphDbType || (process.env.GRAPH_DB_TYPE as GraphDBType) || 'arangodb';
this.activeGraphDbType = dbType;
// Initialize Graph Database // Initialize Graph Database
if (!this.graphDBService.isInitialized()) { if (!this.graphDBService.isInitialized()) {
try { try {
// Get the appropriate service based on type // Get the appropriate service based on type
const graphDbService = getGraphDbService(graphDbType); const graphDbService = getGraphDbService(dbType);
// Try to get settings from server settings API first // Try to get settings from server settings API first
let serverSettings: Record<string, string> = {}; let serverSettings: Record<string, string> = {};
@ -88,7 +97,7 @@ export class BackendService {
console.log('Failed to load settings from server API, falling back to environment variables:', error); console.log('Failed to load settings from server API, falling back to environment variables:', error);
} }
if (graphDbType === 'neo4j') { if (dbType === 'neo4j') {
// Get Neo4j credentials from server settings first, then fallback to environment // Get Neo4j credentials from server settings first, then fallback to environment
const uri = serverSettings.neo4j_url || process.env.NEO4J_URI; const uri = serverSettings.neo4j_url || process.env.NEO4J_URI;
const username = serverSettings.neo4j_user || process.env.NEO4J_USER || process.env.NEO4J_USERNAME; const username = serverSettings.neo4j_user || process.env.NEO4J_USER || process.env.NEO4J_USERNAME;
@ -107,9 +116,9 @@ export class BackendService {
console.log(`Using ArangoDB database: ${dbName}`); console.log(`Using ArangoDB database: ${dbName}`);
await this.graphDBService.initialize('arangodb', url, username, password); await this.graphDBService.initialize('arangodb', url, username, password);
} }
console.log(`${graphDbType} initialized successfully in backend service`); console.log(`${dbType} initialized successfully in backend service`);
} catch (error) { } catch (error) {
console.error(`Failed to initialize ${graphDbType} in backend service:`, error); console.error(`Failed to initialize ${dbType} in backend service:`, error);
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
console.log('Development mode: Continuing despite graph database initialization error'); console.log('Development mode: Continuing despite graph database initialization error');
} else { } else {
@ -151,7 +160,7 @@ export class BackendService {
* Get the active graph database type * Get the active graph database type
*/ */
public getGraphDbType(): GraphDBType { public getGraphDbType(): GraphDBType {
return this.activeGraphDbType; return this.getRuntimeGraphDbType();
} }
/** /**
@ -253,7 +262,7 @@ export class BackendService {
const filteredKeywords = keywords.filter(kw => !this.isStopWord(kw)); const filteredKeywords = keywords.filter(kw => !this.isStopWord(kw));
// If using ArangoDB, use its native graph traversal capabilities // If using ArangoDB, use its native graph traversal capabilities
if (this.activeGraphDbType === 'arangodb') { if (this.getRuntimeGraphDbType() === 'arangodb') {
console.log(`Using ArangoDB native graph traversal for keywords: ${filteredKeywords.join(', ')}`); console.log(`Using ArangoDB native graph traversal for keywords: ${filteredKeywords.join(', ')}`);
try { try {

View File

@ -22,18 +22,17 @@
/** /**
* Initialize default database settings if not already set * Initialize default database settings if not already set
* Called before syncing with server to ensure defaults are available * Called before syncing with server to ensure defaults are available
* NOTE: Don't set graph_db_type here - let server's GRAPH_DB_TYPE env var control it
*/ */
export function initializeDefaultSettings() { export function initializeDefaultSettings() {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return; // Only run on client side return; // Only run on client side
} }
// Set default graph DB type to ArangoDB if not set // Don't set graph_db_type default - let it be controlled by server's GRAPH_DB_TYPE env var
if (!localStorage.getItem('graph_db_type')) { // The server will use its environment variable if no client setting is provided
localStorage.setItem('graph_db_type', 'arangodb');
}
// Set default ArangoDB settings if not set // Set default connection settings only (not the database type selection)
if (!localStorage.getItem('arango_url')) { if (!localStorage.getItem('arango_url')) {
localStorage.setItem('arango_url', 'http://localhost:8529'); localStorage.setItem('arango_url', 'http://localhost:8529');
} }
@ -41,6 +40,11 @@ export function initializeDefaultSettings() {
if (!localStorage.getItem('arango_db')) { if (!localStorage.getItem('arango_db')) {
localStorage.setItem('arango_db', 'txt2kg'); localStorage.setItem('arango_db', 'txt2kg');
} }
// Set default Neo4j settings
if (!localStorage.getItem('neo4j_url')) {
localStorage.setItem('neo4j_url', 'bolt://localhost:7687');
}
} }
/** /**

View File

@ -26,7 +26,7 @@ export type GraphDBType = 'neo4j' | 'arangodb';
export class GraphDBService { export class GraphDBService {
private neo4jService: Neo4jService; private neo4jService: Neo4jService;
private arangoDBService: ArangoDBService; private arangoDBService: ArangoDBService;
private activeDBType: GraphDBType = 'arangodb'; // Default to ArangoDB private activeDBType: GraphDBType | null = null; // Set at runtime, not build time
private static instance: GraphDBService; private static instance: GraphDBService;
private constructor() { private constructor() {
@ -34,6 +34,17 @@ export class GraphDBService {
this.arangoDBService = ArangoDBService.getInstance(); this.arangoDBService = ArangoDBService.getInstance();
} }
/**
* Get the active DB type, reading from env at runtime if not set
*/
private getActiveDBType(): GraphDBType {
if (this.activeDBType === null) {
this.activeDBType = (process.env.GRAPH_DB_TYPE as GraphDBType) || 'arangodb';
console.log(`[GraphDBService] Initialized activeDBType at runtime: ${this.activeDBType}`);
}
return this.activeDBType;
}
/** /**
* Get the singleton instance of GraphDBService * Get the singleton instance of GraphDBService
*/ */
@ -46,24 +57,25 @@ export class GraphDBService {
/** /**
* Initialize the graph database with the specified type * Initialize the graph database with the specified type
* @param dbType - Type of graph database to use * @param dbType - Type of graph database to use (defaults to GRAPH_DB_TYPE env var)
* @param uri - Connection URL * @param uri - Connection URL
* @param username - Database username * @param username - Database username
* @param password - Database password * @param password - Database password
*/ */
public async initialize(dbType: GraphDBType = 'arangodb', uri?: string, username?: string, password?: string): Promise<void> { public async initialize(dbType?: GraphDBType, uri?: string, username?: string, password?: string): Promise<void> {
this.activeDBType = dbType; const graphDbType = dbType || (process.env.GRAPH_DB_TYPE as GraphDBType) || 'arangodb';
this.activeDBType = graphDbType;
try { try {
if (dbType === 'neo4j') { if (graphDbType === 'neo4j') {
this.neo4jService.initialize(uri, username, password); this.neo4jService.initialize(uri, username, password);
console.log('Neo4j initialized successfully'); console.log('Neo4j initialized successfully');
} else if (dbType === 'arangodb') { } else if (graphDbType === 'arangodb') {
await this.arangoDBService.initialize(uri, undefined, username, password); await this.arangoDBService.initialize(uri, undefined, username, password);
console.log('ArangoDB initialized successfully'); console.log('ArangoDB initialized successfully');
} }
} catch (error) { } catch (error) {
console.error(`Failed to initialize ${dbType}:`, error); console.error(`Failed to initialize ${graphDbType}:`, error);
throw error; throw error;
} }
} }
@ -79,14 +91,14 @@ export class GraphDBService {
* Get the active graph database type * Get the active graph database type
*/ */
public getDBType(): GraphDBType { public getDBType(): GraphDBType {
return this.activeDBType; return this.getActiveDBType();
} }
/** /**
* Check if the active database is initialized * Check if the active database is initialized
*/ */
public isInitialized(): boolean { public isInitialized(): boolean {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
return this.neo4jService.isInitialized(); return this.neo4jService.isInitialized();
} else { } else {
return this.arangoDBService.isInitialized(); return this.arangoDBService.isInitialized();
@ -97,7 +109,7 @@ export class GraphDBService {
* Import triples into the active graph database * Import triples into the active graph database
*/ */
public async importTriples(triples: { subject: string; predicate: string; object: string }[]): Promise<void> { public async importTriples(triples: { subject: string; predicate: string; object: string }[]): Promise<void> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
await this.neo4jService.importTriples(triples); await this.neo4jService.importTriples(triples);
} else { } else {
await this.arangoDBService.importTriples(triples); await this.arangoDBService.importTriples(triples);
@ -121,7 +133,7 @@ export class GraphDBService {
[key: string]: any [key: string]: any
}>; }>;
}> { }> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
return await this.neo4jService.getGraphData(); return await this.neo4jService.getGraphData();
} else { } else {
return await this.arangoDBService.getGraphData(); return await this.arangoDBService.getGraphData();
@ -142,7 +154,7 @@ export class GraphDBService {
resultCount: number; resultCount: number;
} }
): Promise<void> { ): Promise<void> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
await this.neo4jService.logQuery(query, queryMode, metrics); await this.neo4jService.logQuery(query, queryMode, metrics);
} else { } else {
await this.arangoDBService.logQuery(query, queryMode, metrics); await this.arangoDBService.logQuery(query, queryMode, metrics);
@ -153,7 +165,7 @@ export class GraphDBService {
* Get query logs from the active graph database * Get query logs from the active graph database
*/ */
public async getQueryLogs(limit: number = 100): Promise<any[]> { public async getQueryLogs(limit: number = 100): Promise<any[]> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
return await this.neo4jService.getQueryLogs(limit); return await this.neo4jService.getQueryLogs(limit);
} else { } else {
return await this.arangoDBService.getQueryLogs(limit); return await this.arangoDBService.getQueryLogs(limit);
@ -164,7 +176,7 @@ export class GraphDBService {
* Close the connection to the active graph database * Close the connection to the active graph database
*/ */
public async close(): Promise<void> { public async close(): Promise<void> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
this.neo4jService.close(); this.neo4jService.close();
} else { } else {
this.arangoDBService.close(); this.arangoDBService.close();
@ -175,7 +187,7 @@ export class GraphDBService {
* Get info about the active graph database driver * Get info about the active graph database driver
*/ */
public getDriverInfo(): Record<string, any> { public getDriverInfo(): Record<string, any> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
return this.neo4jService.getDriverInfo(); return this.neo4jService.getDriverInfo();
} else { } else {
return this.arangoDBService.getDriverInfo(); return this.arangoDBService.getDriverInfo();
@ -197,7 +209,7 @@ export class GraphDBService {
confidence: number; confidence: number;
depth?: number; depth?: number;
}>> { }>> {
if (this.activeDBType === 'arangodb') { if (this.getActiveDBType() === 'arangodb') {
return await this.arangoDBService.graphTraversal(keywords, maxDepth, maxResults); return await this.arangoDBService.graphTraversal(keywords, maxDepth, maxResults);
} else { } else {
// Neo4j doesn't have this method yet, return empty array // Neo4j doesn't have this method yet, return empty array
@ -210,7 +222,7 @@ export class GraphDBService {
* Clear all data from the active graph database * Clear all data from the active graph database
*/ */
public async clearDatabase(): Promise<void> { public async clearDatabase(): Promise<void> {
if (this.activeDBType === 'neo4j') { if (this.getActiveDBType() === 'neo4j') {
// TODO: Implement Neo4j clear database functionality // TODO: Implement Neo4j clear database functionality
throw new Error('Clear database functionality not implemented for Neo4j'); throw new Error('Clear database functionality not implemented for Neo4j');
} else { } else {

View File

@ -18,20 +18,34 @@ import { GraphDBService, GraphDBType } from './graph-db-service';
import { Neo4jService } from './neo4j'; import { Neo4jService } from './neo4j';
import { ArangoDBService } from './arangodb'; import { ArangoDBService } from './arangodb';
/**
* Get the default graph database type from environment or fallback to arangodb
* Note: This is called at runtime, not build time, so process.env should be available
*/
function getDefaultGraphDbType(): GraphDBType {
const envType = process.env.GRAPH_DB_TYPE;
console.log(`[graph-db-util] getDefaultGraphDbType: env=${envType}`);
return (envType as GraphDBType) || 'arangodb';
}
/** /**
* Get the appropriate graph database service based on the graph database type. * Get the appropriate graph database service based on the graph database type.
* This is useful for API routes that need direct access to a specific graph database. * This is useful for API routes that need direct access to a specific graph database.
* *
* @param graphDbType - The type of graph database to use * @param graphDbType - The type of graph database to use (defaults to GRAPH_DB_TYPE env var)
*/ */
export function getGraphDbService(graphDbType: GraphDBType = 'arangodb') { export function getGraphDbService(graphDbType?: GraphDBType) {
if (graphDbType === 'neo4j') { const dbType = graphDbType || getDefaultGraphDbType();
if (dbType === 'neo4j') {
return Neo4jService.getInstance(); return Neo4jService.getInstance();
} else if (graphDbType === 'arangodb') { } else if (dbType === 'arangodb') {
return ArangoDBService.getInstance(); return ArangoDBService.getInstance();
} else { } else {
// Default to ArangoDB // Default based on environment
return ArangoDBService.getInstance(); return getDefaultGraphDbType() === 'neo4j'
? Neo4jService.getInstance()
: ArangoDBService.getInstance();
} }
} }
@ -39,12 +53,13 @@ export function getGraphDbService(graphDbType: GraphDBType = 'arangodb') {
* Initialize the graph database directly (not using GraphDBService). * Initialize the graph database directly (not using GraphDBService).
* This is useful for API routes that need direct access to a specific graph database. * This is useful for API routes that need direct access to a specific graph database.
* *
* @param graphDbType - The type of graph database to use * @param graphDbType - The type of graph database to use (defaults to GRAPH_DB_TYPE env var)
*/ */
export async function initializeGraphDb(graphDbType: GraphDBType = 'arangodb'): Promise<void> { export async function initializeGraphDb(graphDbType?: GraphDBType): Promise<void> {
const service = getGraphDbService(graphDbType); const dbType = graphDbType || getDefaultGraphDbType();
const service = getGraphDbService(dbType);
if (graphDbType === 'neo4j') { if (dbType === 'neo4j') {
// Get Neo4j credentials from environment // Get Neo4j credentials from environment
const uri = process.env.NEO4J_URI; const uri = process.env.NEO4J_URI;
const username = process.env.NEO4J_USER || process.env.NEO4J_USERNAME; const username = process.env.NEO4J_USER || process.env.NEO4J_USERNAME;
@ -54,7 +69,7 @@ export async function initializeGraphDb(graphDbType: GraphDBType = 'arangodb'):
if (service instanceof Neo4jService) { if (service instanceof Neo4jService) {
service.initialize(uri, username, password); service.initialize(uri, username, password);
} }
} else if (graphDbType === 'arangodb') { } else if (dbType === 'arangodb') {
// Get ArangoDB credentials from environment // Get ArangoDB credentials from environment
const url = process.env.ARANGODB_URL; const url = process.env.ARANGODB_URL;
const dbName = process.env.ARANGODB_DB; const dbName = process.env.ARANGODB_DB;

View File

@ -60,14 +60,15 @@ export class RemoteBackendService {
/** /**
* Initialize the remote backend with all required services * Initialize the remote backend with all required services
* @param graphDbType - Type of graph database to use * @param graphDbType - Type of graph database to use (defaults to GRAPH_DB_TYPE env var)
*/ */
public async initialize(graphDbType: GraphDBType = 'arangodb'): Promise<void> { public async initialize(graphDbType?: GraphDBType): Promise<void> {
console.log('Initializing remote backend...'); const dbType = graphDbType || (process.env.GRAPH_DB_TYPE as GraphDBType) || 'arangodb';
console.log(`Initializing remote backend with ${dbType}...`);
// Initialize Graph Database // Initialize Graph Database
await this.graphDBService.initialize(graphDbType); await this.graphDBService.initialize(dbType);
console.log(`${graphDbType} service initialized`); console.log(`${dbType} service initialized`);
// Initialize Pinecone // Initialize Pinecone
await this.pineconeService.initialize(); await this.pineconeService.initialize();