feat: 支持拼音搜索
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m38s
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m38s
- 使用pinyin-pro进行汉语拼音转换 - 调整搜索权重
This commit is contained in:
@ -77,7 +77,7 @@
|
|||||||
const fuzzyMatchedDocuments = fuzzyMatch(documents.value, {
|
const fuzzyMatchedDocuments = fuzzyMatch(documents.value, {
|
||||||
keyword: filters.keyword,
|
keyword: filters.keyword,
|
||||||
keys: ['title'],
|
keys: ['title'],
|
||||||
threshold: 0.2,
|
threshold: 0.3,
|
||||||
});
|
});
|
||||||
return fuzzyMatchedDocuments.filter((doc: DocumentListView) => {
|
return fuzzyMatchedDocuments.filter((doc: DocumentListView) => {
|
||||||
const matchProduct = filters.selectedProduct
|
const matchProduct = filters.selectedProduct
|
||||||
|
|||||||
@ -77,7 +77,7 @@
|
|||||||
const fuzzyMatchedQuestions = fuzzyMatch(questions.value, {
|
const fuzzyMatchedQuestions = fuzzyMatch(questions.value, {
|
||||||
keyword: filters.keyword,
|
keyword: filters.keyword,
|
||||||
keys: ['title', 'content'],
|
keys: ['title', 'content'],
|
||||||
threshold: 0.2,
|
threshold: 0.3,
|
||||||
});
|
});
|
||||||
return fuzzyMatchedQuestions.filter((question: QuestionListView) => {
|
return fuzzyMatchedQuestions.filter((question: QuestionListView) => {
|
||||||
const matchProduct = filters.selectedProduct
|
const matchProduct = filters.selectedProduct
|
||||||
|
|||||||
@ -41,8 +41,30 @@ export function fuzzyMatch<T extends object>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// --- 文本标准化函数 ---
|
// --- 文本标准化函数 ---
|
||||||
const normalizeText = (text: string): string =>
|
const normalizeText = (text: string): string => {
|
||||||
text.normalize('NFKC').toLowerCase().trim();
|
const normalizedText = text.normalize('NFKC').toLowerCase().trim();
|
||||||
|
const translit = transliterateText(normalizedText);
|
||||||
|
return `${normalizedText} ${translit}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型安全的对象取值函数
|
||||||
|
*/
|
||||||
|
function getPropertyByPath<T>(
|
||||||
|
obj: T,
|
||||||
|
path: string | string[]
|
||||||
|
): string | undefined {
|
||||||
|
const keys = Array.isArray(path) ? path : path.split('.');
|
||||||
|
let value: unknown = obj;
|
||||||
|
for (const key of keys) {
|
||||||
|
if (value && typeof value === 'object' && key in value) {
|
||||||
|
value = (value as Record<string, unknown>)[key];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typeof value === 'string' ? value : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// --- fallback 模糊匹配算法 ---
|
// --- fallback 模糊匹配算法 ---
|
||||||
const fallbackFuzzyMatch = (text: string, pattern: string): boolean => {
|
const fallbackFuzzyMatch = (text: string, pattern: string): boolean => {
|
||||||
@ -70,6 +92,23 @@ export function fuzzyMatch<T extends object>(
|
|||||||
threshold,
|
threshold,
|
||||||
minMatchCharLength,
|
minMatchCharLength,
|
||||||
ignoreLocation: true,
|
ignoreLocation: true,
|
||||||
|
findAllMatches: true,
|
||||||
|
isCaseSensitive: false,
|
||||||
|
getFn: (obj, path) => {
|
||||||
|
const value = getPropertyByPath(obj, path);
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const normalized = value
|
||||||
|
.normalize('NFKC')
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.trim();
|
||||||
|
const translit = transliterateText(normalized);
|
||||||
|
logger.debug(`${normalized} => ${translit}`);
|
||||||
|
// 拼接原文 + 转写,提升中外文混合匹配效果
|
||||||
|
return `${normalized} ${translit}`;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
sortFn: (a, b) => {
|
sortFn: (a, b) => {
|
||||||
const itemA = a.item as T;
|
const itemA = a.item as T;
|
||||||
const itemB = b.item as T;
|
const itemB = b.item as T;
|
||||||
@ -83,6 +122,8 @@ export function fuzzyMatch<T extends object>(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.debug('Fuzzy search options:', options);
|
||||||
|
logger.debug('Fuzzy search keyword:', k);
|
||||||
return fuse.search(k).map((result) => result.item);
|
return fuse.search(k).map((result) => result.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
app/utils/transliterateText.ts
Normal file
17
app/utils/transliterateText.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { pinyin } from 'pinyin-pro';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将汉语文本转换为拼音形式
|
||||||
|
*/
|
||||||
|
export function transliterateText(input: string): string {
|
||||||
|
if (!input) return '';
|
||||||
|
const text = input.normalize('NFKC').trim();
|
||||||
|
|
||||||
|
// 检测是否包含中文字符
|
||||||
|
if (/[\u4e00-\u9fa5]/.test(text)) {
|
||||||
|
return pinyin(text, { toneType: 'none', type: 'array' }).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则返回原文本
|
||||||
|
return text;
|
||||||
|
}
|
||||||
@ -29,6 +29,7 @@
|
|||||||
"meilisearch": "^0.53.0",
|
"meilisearch": "^0.53.0",
|
||||||
"nuxt": "^4.0.3",
|
"nuxt": "^4.0.3",
|
||||||
"nuxt-directus": "5.7.0",
|
"nuxt-directus": "5.7.0",
|
||||||
|
"pinyin-pro": "^3.27.0",
|
||||||
"sass": "^1.90.0",
|
"sass": "^1.90.0",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.3",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.18",
|
||||||
|
|||||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -62,6 +62,9 @@ importers:
|
|||||||
nuxt-directus:
|
nuxt-directus:
|
||||||
specifier: 5.7.0
|
specifier: 5.7.0
|
||||||
version: 5.7.0(magicast@0.3.5)
|
version: 5.7.0(magicast@0.3.5)
|
||||||
|
pinyin-pro:
|
||||||
|
specifier: ^3.27.0
|
||||||
|
version: 3.27.0
|
||||||
sass:
|
sass:
|
||||||
specifier: ^1.90.0
|
specifier: ^1.90.0
|
||||||
version: 1.92.1
|
version: 1.92.1
|
||||||
@ -4399,6 +4402,9 @@ packages:
|
|||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
pinyin-pro@3.27.0:
|
||||||
|
resolution: {integrity: sha512-Osdgjwe7Rm17N2paDMM47yW+jUIUH3+0RGo8QP39ZTLpTaJVDK0T58hOLaMQJbcMmAebVuK2ePunTEVEx1clNQ==}
|
||||||
|
|
||||||
pkg-types@1.3.1:
|
pkg-types@1.3.1:
|
||||||
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
|
||||||
|
|
||||||
@ -10618,6 +10624,8 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.9.2
|
typescript: 5.9.2
|
||||||
|
|
||||||
|
pinyin-pro@3.27.0: {}
|
||||||
|
|
||||||
pkg-types@1.3.1:
|
pkg-types@1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
confbox: 0.1.8
|
confbox: 0.1.8
|
||||||
|
|||||||
Reference in New Issue
Block a user