Initial Commit
This commit is contained in:
188
src/services/searchIndexer.js
Normal file
188
src/services/searchIndexer.js
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Meilisearch indexer for search functionality
|
||||
*/
|
||||
|
||||
import meilisearch from '../config/meilisearch.js';
|
||||
import { Service } from '../models/Service.js';
|
||||
import { Analysis } from '../models/Analysis.js';
|
||||
|
||||
const INDEX_NAME = 'services';
|
||||
|
||||
export class SearchIndexer {
|
||||
static index = null;
|
||||
|
||||
/**
|
||||
* Initialize the search index
|
||||
*/
|
||||
static async init() {
|
||||
try {
|
||||
console.log('Initializing Meilisearch...');
|
||||
|
||||
// Check if index exists
|
||||
const indexes = await meilisearch.getIndexes();
|
||||
const indexExists = indexes.results.some(idx => idx.uid === INDEX_NAME);
|
||||
|
||||
if (!indexExists) {
|
||||
console.log('Creating search index...');
|
||||
await meilisearch.createIndex(INDEX_NAME, { primaryKey: 'id' });
|
||||
}
|
||||
|
||||
this.index = meilisearch.index(INDEX_NAME);
|
||||
|
||||
// Configure searchable attributes
|
||||
await this.index.updateSettings({
|
||||
searchableAttributes: [
|
||||
'name',
|
||||
'findings.positive.title',
|
||||
'findings.positive.description',
|
||||
'findings.negative.title',
|
||||
'findings.negative.description',
|
||||
'data_types_collected',
|
||||
'third_parties.name'
|
||||
],
|
||||
filterableAttributes: ['grade', 'overall_score'],
|
||||
sortableAttributes: ['name', 'created_at', 'overall_score'],
|
||||
rankingRules: [
|
||||
'words',
|
||||
'typo',
|
||||
'proximity',
|
||||
'attribute',
|
||||
'sort',
|
||||
'exactness'
|
||||
]
|
||||
});
|
||||
|
||||
console.log('Meilisearch initialized successfully');
|
||||
} catch (error) {
|
||||
console.error('Meilisearch initialization error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index a single service
|
||||
* @param {Object} service - Service with analysis
|
||||
*/
|
||||
static async indexService(service) {
|
||||
try {
|
||||
if (!this.index) {
|
||||
await this.init();
|
||||
}
|
||||
|
||||
const document = {
|
||||
id: service.id,
|
||||
name: service.name,
|
||||
url: service.url,
|
||||
logo_url: service.logo_url,
|
||||
grade: service.grade,
|
||||
overall_score: service.overall_score,
|
||||
findings: service.findings || { positive: [], negative: [], neutral: [] },
|
||||
data_types_collected: service.data_types_collected || [],
|
||||
third_parties: service.third_parties || [],
|
||||
last_analyzed: service.last_analyzed,
|
||||
created_at: service.created_at
|
||||
};
|
||||
|
||||
await this.index.addDocuments([document]);
|
||||
console.log(`Indexed service: ${service.name}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to index service ${service.name}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index all services
|
||||
*/
|
||||
static async indexAll() {
|
||||
try {
|
||||
console.log('Indexing all services...');
|
||||
|
||||
const services = await Service.findAllWithLatestAnalysis();
|
||||
|
||||
const documents = services.map(service => ({
|
||||
id: service.id,
|
||||
name: service.name,
|
||||
url: service.url,
|
||||
logo_url: service.logo_url,
|
||||
grade: service.grade,
|
||||
overall_score: service.overall_score,
|
||||
findings: service.findings || { positive: [], negative: [], neutral: [] },
|
||||
data_types_collected: service.data_types_collected || [],
|
||||
third_parties: service.third_parties || [],
|
||||
last_analyzed: service.last_analyzed,
|
||||
created_at: service.created_at
|
||||
}));
|
||||
|
||||
if (documents.length > 0) {
|
||||
await this.index.addDocuments(documents);
|
||||
console.log(`Indexed ${documents.length} services`);
|
||||
}
|
||||
|
||||
return { indexed: documents.length };
|
||||
} catch (error) {
|
||||
console.error('Bulk indexing error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search services
|
||||
* @param {string} query - Search query
|
||||
* @param {Object} options - Search options
|
||||
*/
|
||||
static async search(query, options = {}) {
|
||||
try {
|
||||
if (!this.index) {
|
||||
await this.init();
|
||||
}
|
||||
|
||||
const searchOptions = {
|
||||
limit: options.limit || 25,
|
||||
offset: options.offset || 0,
|
||||
...options
|
||||
};
|
||||
|
||||
const results = await this.index.search(query, searchOptions);
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error('Search error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a service from the index
|
||||
* @param {number} serviceId - Service ID
|
||||
*/
|
||||
static async deleteService(serviceId) {
|
||||
try {
|
||||
if (!this.index) {
|
||||
await this.init();
|
||||
}
|
||||
|
||||
await this.index.deleteDocument(serviceId);
|
||||
console.log(`Deleted service ${serviceId} from index`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to delete service ${serviceId}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index stats
|
||||
*/
|
||||
static async getStats() {
|
||||
try {
|
||||
if (!this.index) {
|
||||
await this.init();
|
||||
}
|
||||
|
||||
const stats = await this.index.getStats();
|
||||
return stats;
|
||||
} catch (error) {
|
||||
console.error('Failed to get stats:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user