/**
* 搜索引擎工具类
* 提供知识库搜索和匹配功能
*/
const { knowledgeDatabase } = require('../data/knowledge-database.js');
class SearchEngine {
constructor() {
this.database = knowledgeDatabase;
this.searchIndex = this.buildSearchIndex();
}
/**
* 构建搜索索引
*/
buildSearchIndex() {
const index = {
hexagrams: new Map(),
trigrams: new Map(),
stems: new Map(),
branches: new Map(),
elements: new Map(),
tenGods: new Map(),
patterns: new Map()
};
// 索引六十四卦
Object.entries(this.database.hexagrams).forEach(([key, hexagram]) => {
const searchTerms = [
hexagram.name,
hexagram.chinese,
hexagram.meaning,
hexagram.judgment,
...Object.values(hexagram.interpretation)
].join(' ').toLowerCase();
index.hexagrams.set(key, {
data: hexagram,
searchTerms,
keywords: [hexagram.name, hexagram.chinese, hexagram.meaning]
});
});
// 索引八卦
Object.entries(this.database.trigrams).forEach(([key, trigram]) => {
const searchTerms = [
key,
trigram.meaning,
trigram.element,
trigram.direction,
trigram.season,
...trigram.characteristics
].join(' ').toLowerCase();
index.trigrams.set(key, {
data: trigram,
searchTerms,
keywords: [key, trigram.meaning, trigram.element]
});
});
// 索引天干
Object.entries(this.database.heavenly_stems).forEach(([key, stem]) => {
const searchTerms = [
key,
stem.element,
stem.polarity,
stem.meaning,
stem.personality,
...stem.characteristics
].join(' ').toLowerCase();
index.stems.set(key, {
data: stem,
searchTerms,
keywords: [key, stem.element, stem.meaning]
});
});
// 索引地支
Object.entries(this.database.earthly_branches).forEach(([key, branch]) => {
const searchTerms = [
key,
branch.element,
branch.animal,
branch.meaning,
branch.season,
...branch.characteristics
].join(' ').toLowerCase();
index.branches.set(key, {
data: branch,
searchTerms,
keywords: [key, branch.animal, branch.meaning]
});
});
// 索引十神
Object.entries(this.database.ten_gods).forEach(([key, god]) => {
const searchTerms = [
key,
god.relationship,
god.meaning,
god.career,
god.personality,
...god.characteristics,
...god.positive,
...god.negative
].join(' ').toLowerCase();
index.tenGods.set(key, {
data: god,
searchTerms,
keywords: [key, god.meaning, god.relationship]
});
});
// 索引格局
Object.entries(this.database.patterns).forEach(([key, pattern]) => {
const searchTerms = [
key,
pattern.description,
pattern.life_advice,
...pattern.characteristics,
...pattern.suitable_career,
...pattern.famous_examples
].join(' ').toLowerCase();
index.patterns.set(key, {
data: pattern,
searchTerms,
keywords: [key, pattern.description, ...pattern.characteristics]
});
});
return index;
}
/**
* 搜索六十四卦
* @param {string} query - 搜索关键词
* @param {number} limit - 返回结果数量限制
* @returns {Array} 搜索结果
*/
searchHexagrams(query, limit = 10) {
return this.performSearch(this.searchIndex.hexagrams, query, limit);
}
/**
* 搜索八卦
* @param {string} query - 搜索关键词
* @param {number} limit - 返回结果数量限制
* @returns {Array} 搜索结果
*/
searchTrigrams(query, limit = 8) {
return this.performSearch(this.searchIndex.trigrams, query, limit);
}
/**
* 搜索天干
* @param {string} query - 搜索关键词
* @param {number} limit - 返回结果数量限制
* @returns {Array} 搜索结果
*/
searchHeavenlyStems(query, limit = 10) {
return this.performSearch(this.searchIndex.stems, query, limit);
}
/**
* 搜索地支
* @param {string} query - 搜索关键词
* @param {number} limit - 返回结果数量限制
* @returns {Array} 搜索结果
*/
searchEarthlyBranches(query, limit = 12) {
return this.performSearch(this.searchIndex.branches, query, limit);
}
/**
* 搜索十神
* @param {string} query - 搜索关键词
* @param {number} limit - 返回结果数量限制
* @returns {Array} 搜索结果
*/
searchTenGods(query, limit = 10) {
return this.performSearch(this.searchIndex.tenGods, query, limit);
}
/**
* 搜索格局
* @param {string} query - 搜索关键词
* @param {number} limit - 返回结果数量限制
* @returns {Array} 搜索结果
*/
searchPatterns(query, limit = 10) {
return this.performSearch(this.searchIndex.patterns, query, limit);
}
/**
* 综合搜索
* @param {string} query - 搜索关键词
* @param {Object} options - 搜索选项
* @returns {Object} 分类搜索结果
*/
searchAll(query, options = {}) {
const {
includeHexagrams = true,
includeTrigrams = true,
includeStems = true,
includeBranches = true,
includeTenGods = true,
includePatterns = true,
limit = 5
} = options;
const results = {};
if (includeHexagrams) {
results.hexagrams = this.searchHexagrams(query, limit);
}
if (includeTrigrams) {
results.trigrams = this.searchTrigrams(query, limit);
}
if (includeStems) {
results.heavenly_stems = this.searchHeavenlyStems(query, limit);
}
if (includeBranches) {
results.earthly_branches = this.searchEarthlyBranches(query, limit);
}
if (includeTenGods) {
results.ten_gods = this.searchTenGods(query, limit);
}
if (includePatterns) {
results.patterns = this.searchPatterns(query, limit);
}
return results;
}
/**
* 执行搜索
* @param {Map} index - 搜索索引
* @param {string} query - 搜索关键词
* @param {number} limit - 结果限制
* @returns {Array} 搜索结果
*/
performSearch(index, query, limit) {
if (!query || typeof query !== 'string') {
return [];
}
const searchQuery = query.toLowerCase().trim();
const results = [];
for (const [key, item] of index) {
const score = this.calculateRelevanceScore(item, searchQuery);
if (score > 0) {
results.push({
key,
data: item.data,
score,
relevance: this.getRelevanceLevel(score)
});
}
}
// 按相关性得分排序
results.sort((a, b) => b.score - a.score);
return results.slice(0, limit);
}
/**
* 计算相关性得分
* @param {Object} item - 索引项
* @param {string} query - 搜索查询
* @returns {number} 相关性得分
*/
calculateRelevanceScore(item, query) {
let score = 0;
const queryTerms = query.split(/\s+/);
// 关键词完全匹配得分最高
for (const keyword of item.keywords) {
if (keyword.toLowerCase() === query) {
score += 100;
} else if (keyword.toLowerCase().includes(query)) {
score += 50;
}
}
// 搜索词在内容中的匹配
for (const term of queryTerms) {
if (term.length > 0) {
const termCount = (item.searchTerms.match(new RegExp(term, 'gi')) || []).length;
score += termCount * 10;
}
}
// 部分匹配
if (item.searchTerms.includes(query)) {
score += 20;
}
return score;
}
/**
* 获取相关性级别
* @param {number} score - 相关性得分
* @returns {string} 相关性级别
*/
getRelevanceLevel(score) {
if (score >= 100) return 'high';
if (score >= 50) return 'medium';
if (score >= 20) return 'low';
return 'minimal';
}
/**
* 根据元素查找相关内容
* @param {string} element - 五行元素
* @returns {Object} 相关内容
*/
findByElement(element) {
const results = {
trigrams: [],
stems: [],
branches: [],
characteristics: null
};
// 查找相关八卦
for (const [key, trigram] of Object.entries(this.database.trigrams)) {
if (trigram.element === element) {
results.trigrams.push({ key, ...trigram });
}
}
// 查找相关天干
for (const [key, stem] of Object.entries(this.database.heavenly_stems)) {
if (stem.element === element) {
results.stems.push({ key, ...stem });
}
}
// 查找相关地支
for (const [key, branch] of Object.entries(this.database.earthly_branches)) {
if (branch.element === element) {
results.branches.push({ key, ...branch });
}
}
// 获取元素特性
results.characteristics = this.database.element_relations.characteristics[element];
return results;
}
/**
* 根据十神查找相关信息
* @param {string} tenGod - 十神名称
* @returns {Object} 十神详细信息
*/
findTenGodInfo(tenGod) {
return this.database.ten_gods[tenGod] || null;
}
/**
* 根据格局查找相关信息
* @param {string} pattern - 格局名称
* @returns {Object} 格局详细信息
*/
findPatternInfo(pattern) {
return this.database.patterns[pattern] || null;
}
/**
* 获取五行相生相克关系
* @param {string} element1 - 第一个元素
* @param {string} element2 - 第二个元素
* @returns {Object} 关系信息
*/
getElementRelation(element1, element2) {
const relations = this.database.element_relations;
if (relations.generating[element1] === element2) {
return { type: 'generating', description: `${element1}生${element2}` };
}
if (relations.destructive[element1] === element2) {
return { type: 'destructive', description: `${element1}克${element2}` };
}
if (relations.generating[element2] === element1) {
return { type: 'being_generated', description: `${element2}生${element1}` };
}
if (relations.destructive[element2] === element1) {
return { type: 'being_destructed', description: `${element2}克${element1}` };
}
if (element1 === element2) {
return { type: 'same', description: `${element1}同类` };
}
return { type: 'neutral', description: `${element1}与${element2}无直接关系` };
}
/**
* 获取建议和指导
* @param {string} category - 类别
* @param {string} key - 关键词
* @returns {Array} 建议列表
*/
getAdvice(category, key) {
const advice = [];
switch (category) {
case 'health':
const healthData = this.database.health.constitution_types[key];
if (healthData) {
advice.push(...healthData.wellness_advice);
}
break;
case 'career':
const patternData = this.database.patterns[key];
if (patternData) {
advice.push(patternData.life_advice);
advice.push(...patternData.suitable_career.map(career => `适合从事${career}相关工作`));
}
break;
case 'element':
const elementData = this.database.element_relations.characteristics[key];
if (elementData) {
advice.push(`发挥${key}的特质:${elementData.personality.join('、')}`);
}
break;
}
return advice;
}
/**
* 模糊搜索
* @param {string} query - 搜索关键词
* @param {number} threshold - 相似度阈值
* @returns {Array} 搜索结果
*/
fuzzySearch(query, threshold = 0.6) {
const results = [];
const allIndexes = [
{ name: 'hexagrams', index: this.searchIndex.hexagrams },
{ name: 'trigrams', index: this.searchIndex.trigrams },
{ name: 'stems', index: this.searchIndex.stems },
{ name: 'branches', index: this.searchIndex.branches },
{ name: 'tenGods', index: this.searchIndex.tenGods },
{ name: 'patterns', index: this.searchIndex.patterns }
];
for (const { name, index } of allIndexes) {
for (const [key, item] of index) {
for (const keyword of item.keywords) {
const similarity = this.calculateSimilarity(query.toLowerCase(), keyword.toLowerCase());
if (similarity >= threshold) {
results.push({
category: name,
key,
data: item.data,
similarity,
matchedKeyword: keyword
});
}
}
}
}
return results.sort((a, b) => b.similarity - a.similarity);
}
/**
* 计算字符串相似度
* @param {string} str1 - 字符串1
* @param {string} str2 - 字符串2
* @returns {number} 相似度(0-1)
*/
calculateSimilarity(str1, str2) {
const longer = str1.length > str2.length ? str1 : str2;
const shorter = str1.length > str2.length ? str2 : str1;
if (longer.length === 0) {
return 1.0;
}
const editDistance = this.levenshteinDistance(longer, shorter);
return (longer.length - editDistance) / longer.length;
}
/**
* 计算编辑距离
* @param {string} str1 - 字符串1
* @param {string} str2 - 字符串2
* @returns {number} 编辑距离
*/
levenshteinDistance(str1, str2) {
const matrix = [];
for (let i = 0; i <= str2.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= str1.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= str2.length; i++) {
for (let j = 1; j <= str1.length; j++) {
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
);
}
}
}
return matrix[str2.length][str1.length];
}
}
module.exports = SearchEngine;