feat(endpoint): 添加/reindex/:configId用于重建meilisearch索引
This commit is contained in:
@ -1,9 +1,18 @@
|
||||
import { defineEndpoint } from '@directus/extensions-sdk';
|
||||
import { MeiliSearch } from 'meilisearch';
|
||||
import { MeiliDocs, MeiliIndexConfig, MeiliSearchConfig } from '../types/meilisearch';
|
||||
import { getNestedProperty } from '../helper/nest';
|
||||
import { createLogger } from '../logger';
|
||||
import { buildQueryFields, filterTranslations } from '../helper/collection';
|
||||
|
||||
export default defineEndpoint({
|
||||
id: 'meilisearch',
|
||||
handler: (router, context) => {
|
||||
const logger = createLogger('meilisearch_endpoint');
|
||||
|
||||
router.get('/', (_req, res) => res.send('Hello, MeiliSearch!'));
|
||||
|
||||
// 获取可用的集合列表
|
||||
router.get('/collections', async (_req, res) => {
|
||||
const { services, getSchema } = context;
|
||||
const { CollectionsService } = services;
|
||||
@ -11,18 +20,39 @@ export default defineEndpoint({
|
||||
const collSvc = new CollectionsService({
|
||||
schema,
|
||||
})
|
||||
const collections = await collSvc.readByQuery();
|
||||
const allCollections = await collSvc.readByQuery();
|
||||
|
||||
const visible = collections.filter(col => !col.meta?.hidden && !col.meta?.system && col.schema);
|
||||
const availableCollections = allCollections.filter((col) => {
|
||||
const isVisible = !col.meta?.hidden;
|
||||
const isUserDefined = !col.meta?.system;
|
||||
const hasSchema = !!col.schema;
|
||||
const isNotMeiliConfig = col.collection !== 'meili_index_configs' && col.collection !== 'meili_search_config';
|
||||
|
||||
const result = visible.map(col => ({
|
||||
return isVisible && isUserDefined && hasSchema && isNotMeiliConfig;
|
||||
});
|
||||
|
||||
const result = availableCollections.map(col => ({
|
||||
collection: col.collection,
|
||||
note: col.meta?.note || '',
|
||||
icon: col.meta?.icon || '',
|
||||
}));
|
||||
|
||||
const { ItemsService } = services;
|
||||
const meiliIndexConfigsSvc = new ItemsService('meili_index_configs', {
|
||||
schema,
|
||||
});
|
||||
|
||||
// 读取已有配置
|
||||
const existingConfigs = await meiliIndexConfigsSvc.readByQuery({ limit: -1 });
|
||||
const existingMap = new Map(existingConfigs.map((config) => [config.collection_name, config]));
|
||||
|
||||
console.log('Existing MeiliSearch index configs:', existingConfigs);
|
||||
console.log('Available collections for MeiliSearch indexing:', existingMap);
|
||||
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
// 测试连接 MeiliSearch 服务器
|
||||
router.post('/connection-test', async (req, res) => {
|
||||
const { host, apiKey } = req.body;
|
||||
console.log(req.body);
|
||||
@ -46,5 +76,97 @@ export default defineEndpoint({
|
||||
return res.json({ success: false, message: `连接失败:${(error as Error).message}` });
|
||||
}
|
||||
});
|
||||
|
||||
// 重建索引
|
||||
router.post('/reindex/:configId?', async (req, res) => {
|
||||
const { ItemsService } = context.services;
|
||||
const schema = await context.getSchema();
|
||||
|
||||
// 读取Meilisearch索引配置
|
||||
let configs = [];
|
||||
const configService = new ItemsService<MeiliIndexConfig>('meili_index_configs', { schema });
|
||||
|
||||
const configId = req.params.configId;
|
||||
if (configId) {
|
||||
const cfg = await configService.readOne(configId);
|
||||
if (!cfg) {
|
||||
return res.status(404).json({ success: false, message: '配置未找到' });
|
||||
}
|
||||
if (!cfg.enabled) {
|
||||
return res.status(400).json({ success: false, message: '该配置未启用' });
|
||||
}
|
||||
configs = [cfg];
|
||||
} else {
|
||||
// 读取所有启用的配置
|
||||
const resp = await configService.readByQuery({ filter: { enabled: { _eq: true } }, limit: -1 });
|
||||
configs = resp;
|
||||
}
|
||||
|
||||
// 获取可用语言选项
|
||||
let availableLanguages: string[] = [];
|
||||
try {
|
||||
const langService = new ItemsService('languages', { schema });
|
||||
const languages = await langService.readByQuery({ limit: -1 });
|
||||
availableLanguages = languages.map(lang => lang.code);
|
||||
} catch (error) {
|
||||
logger.error('Error fetching languages:', error);
|
||||
}
|
||||
|
||||
// 读取 MeiliSearch 全局配置
|
||||
const meiliService = new ItemsService<MeiliSearchConfig>('meili_search_config', { schema });
|
||||
const meiliConfigs = await meiliService.readByQuery({ limit: 1 });
|
||||
if (meiliConfigs.length === 0) {
|
||||
return res.status(500).json({ success: false, message: 'MeiliSearch 全局配置未设置' });
|
||||
}
|
||||
const { host } = meiliConfigs[0] || { host: '', apiKey: '' };
|
||||
|
||||
const client = new MeiliSearch({
|
||||
host,
|
||||
})
|
||||
const result: any[] = [];
|
||||
|
||||
for (const cfg of configs) {
|
||||
const fields = cfg.fields;
|
||||
const queryFields = buildQueryFields(fields);
|
||||
|
||||
const itemService = new ItemsService(cfg.collection_name, { schema });
|
||||
const items = await itemService.readByQuery({ fields: queryFields, limit: -1 });
|
||||
|
||||
// 为每种语言重建索引
|
||||
for (const lang of availableLanguages) {
|
||||
const filteredItems = filterTranslations(items, lang);
|
||||
const docs = filteredItems.map(item => {
|
||||
const doc: MeiliDocs = { id: item.id };
|
||||
for (const [key, value] of Object.entries(cfg.fields)) {
|
||||
const fieldValue = getNestedProperty(item, value);
|
||||
logger.info(`Mapping field ${key} to value: ${fieldValue}`);
|
||||
doc[key] = fieldValue;
|
||||
}
|
||||
return doc;
|
||||
});
|
||||
|
||||
const index = client.index(`${cfg.index_name}_${lang}`);
|
||||
// 删除所有索引
|
||||
await index.deleteAllDocuments();
|
||||
try {
|
||||
const enqueueRes = await index.addDocuments(docs);
|
||||
result.push({ config: cfg.collection_name, enqueued: enqueueRes });
|
||||
} catch (error) {
|
||||
console.error(`Error reindexing collection ${cfg.collection_name}:`, error);
|
||||
result.push({ config: cfg.collection_name, error: (error as Error).message });
|
||||
}
|
||||
|
||||
// 处理结果
|
||||
if (cfg.enabled) {
|
||||
console.log(`Reindexing triggered for collection: ${cfg.collection_name}`);
|
||||
} else {
|
||||
console.log(`Skipping disabled config for collection: ${cfg.collection_name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res.json({ success: true, result });
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user