survey-mcp-server
Version:
Survey management server handling survey creation, response collection, analysis, and reporting with database access for data management
198 lines • 7.71 kB
JavaScript
import Typesense from 'typesense';
import { config } from './config.js';
import { logger } from './logger.js';
export class TypesenseClient {
constructor() {
this.client = null;
this.isAvailable = false;
this.initialized = false;
// Don't initialize client immediately
// It will be initialized on first use or explicit initialization
}
static getInstance() {
if (!TypesenseClient.instance) {
TypesenseClient.instance = new TypesenseClient();
}
return TypesenseClient.instance;
}
async initialize() {
try {
logger.info('Initializing Typesense client with config:', {
host: config.typesense.nodes[0]?.host,
port: config.typesense.nodes[0]?.port,
protocol: config.typesense.nodes[0]?.protocol,
hasApiKey: !!config.typesense.apiKey
});
this.client = new Typesense.Client({
nodes: config.typesense.nodes,
apiKey: config.typesense.apiKey,
connectionTimeoutSeconds: config.typesense.connectionTimeoutSeconds
});
// Test the connection
await this.client.health.retrieve();
this.isAvailable = true;
this.initialized = true;
logger.info('Typesense client initialized and connected successfully');
}
catch (error) {
logger.error('Failed to initialize Typesense client:', error);
this.isAvailable = false;
this.initialized = false;
this.client = null;
throw error;
}
}
async reinitialize() {
this.client = null;
this.isAvailable = false;
this.initialized = false;
await this.initialize();
}
async ensureInitialized() {
if (!this.initialized) {
await this.initialize();
}
}
checkAvailability() {
if (!this.client || !this.isAvailable) {
throw new Error('Typesense client is not available. Please check your Typesense configuration and ensure initialize() has been called.');
}
}
get collections() {
this.checkAvailability();
return this.client.collections;
}
getAvailability() {
return this.isAvailable && this.client !== null && this.initialized;
}
isInitialized() {
return this.initialized;
}
getClient() {
this.checkAvailability();
if (!this.client) {
throw new Error('Typesense client is not initialized');
}
return this.client;
}
async search(collectionName, searchParameters) {
try {
await this.ensureInitialized();
this.checkAvailability();
logger.debug(`Searching in collection '${collectionName}' with parameters:`, searchParameters);
const results = await this.client.collections(collectionName).documents().search(searchParameters);
logger.debug(`Search completed. Found ${results.found} results`);
return results;
}
catch (error) {
logger.error(`Error searching collection '${collectionName}':`, error);
throw error;
}
}
async createCollection(collectionSchema) {
try {
await this.ensureInitialized();
this.checkAvailability();
logger.info(`Creating collection with schema:`, collectionSchema);
const result = await this.client.collections().create(collectionSchema);
logger.info(`Collection '${collectionSchema.name}' created successfully`);
return result;
}
catch (error) {
logger.error(`Error creating collection:`, error);
throw error;
}
}
async indexDocument(collectionName, document) {
try {
await this.ensureInitialized();
this.checkAvailability();
const result = await this.client.collections(collectionName).documents().create(document);
logger.debug(`Document indexed in collection '${collectionName}'`);
return result;
}
catch (error) {
logger.error(`Error indexing document in collection '${collectionName}':`, error);
throw error;
}
}
async updateDocument(collectionName, documentId, document) {
try {
await this.ensureInitialized();
this.checkAvailability();
const result = await this.client.collections(collectionName).documents(documentId).update(document);
logger.debug(`Document updated in collection '${collectionName}'`);
return result;
}
catch (error) {
logger.error(`Error updating document in collection '${collectionName}':`, error);
throw error;
}
}
async upsertDocument(collectionName, document) {
try {
await this.ensureInitialized();
this.checkAvailability();
// Try to update first
const result = await this.client.collections(collectionName).documents(document.id.toString()).update(document);
logger.debug(`Document updated in collection '${collectionName}'`);
return result;
}
catch (updateError) {
// If update fails, create new document
if (updateError.httpStatus === 404) {
const createResult = await this.indexDocument(collectionName, document);
logger.debug(`Document created (upsert) in collection '${collectionName}'`);
return createResult;
}
else {
throw updateError;
}
}
}
async importDocuments(collectionName, documents, options = {}) {
try {
await this.ensureInitialized();
this.checkAvailability();
logger.info(`Importing ${documents.length} documents to collection '${collectionName}'`);
const result = await this.client.collections(collectionName).documents().import(documents, options);
logger.info(`Documents imported successfully to collection '${collectionName}'`);
return result;
}
catch (error) {
logger.error(`Error importing documents to collection '${collectionName}':`, error);
throw error;
}
}
async deleteCollection(collectionName) {
try {
await this.ensureInitialized();
this.checkAvailability();
const result = await this.client.collections(collectionName).delete();
logger.info(`Collection '${collectionName}' deleted successfully`);
return result;
}
catch (error) {
logger.error(`Error deleting collection '${collectionName}':`, error);
throw error;
}
}
async getHealth() {
try {
await this.ensureInitialized();
this.checkAvailability();
const health = await this.client.health.retrieve();
this.isAvailable = true; // Set availability on successful health check
logger.debug('Typesense health check completed');
return health;
}
catch (error) {
this.isAvailable = false; // Set unavailability on failed health check
logger.error('Typesense health check failed:', error);
throw error;
}
}
}
// Export singleton instance - but don't initialize it yet
export const typesenseClient = TypesenseClient.getInstance();
//# sourceMappingURL=typesense.js.map