@solapi/mcp-server
Version:
MCP server for SOLAPI document search and integration
138 lines • 4.92 kB
JavaScript
export class WeightedSearchEngine {
/**
* 검색 쿼리에 대한 예제의 관련도 점수를 계산합니다.
*/
static calculateScore(example, query) {
const q = query.toLowerCase().trim();
let score = 0;
const matchedFields = [];
// 쿼리를 단어별로 분리하여 각각 매칭 점수 계산
const queryWords = q.split(/\s+/).filter(word => word.length > 0);
// 전체 쿼리 매치 (가장 높은 가중치)
const fullQueryScore = this.calculateFieldScore(example, q, {
title: 20,
keywords: 15,
description: 10,
category: 8,
usage: 6,
code: 3,
id: 2
});
score += fullQueryScore.score;
matchedFields.push(...fullQueryScore.fields);
// 개별 단어 매치 (중간 가중치)
queryWords.forEach(word => {
const wordScore = this.calculateFieldScore(example, word, {
title: 10,
keywords: 8,
description: 5,
category: 4,
usage: 3,
code: 2,
id: 1
});
score += wordScore.score;
matchedFields.push(...wordScore.fields);
});
// 정확한 매치 보너스 (추가 점수)
if (example.title.toLowerCase() === q) {
score += 10; // 제목이 정확히 일치하면 보너스
}
// 키워드 정확 매치 보너스
if (example.keywords.some(keyword => keyword.toLowerCase() === q)) {
score += 5; // 키워드가 정확히 일치하면 보너스
}
// 중복 필드 제거
const uniqueMatchedFields = [...new Set(matchedFields)];
return {
example,
score,
matchedFields: uniqueMatchedFields
};
}
/**
* 특정 필드들에 대해 점수를 계산합니다.
*/
static calculateFieldScore(example, searchTerm, weights) {
let score = 0;
const fields = [];
// 제목 매치
if (example.title.toLowerCase().includes(searchTerm)) {
score += weights.title;
fields.push('title');
}
// 키워드 매치
const keywordMatches = example.keywords.filter(keyword => keyword.toLowerCase().includes(searchTerm));
if (keywordMatches.length > 0) {
score += weights.keywords * keywordMatches.length;
fields.push('keywords');
}
// 설명 매치
if (example.description.toLowerCase().includes(searchTerm)) {
score += weights.description;
fields.push('description');
}
// 카테고리 매치
if (example.category.toLowerCase().includes(searchTerm)) {
score += weights.category;
fields.push('category');
}
// 사용법 매치
if (example.usage.toLowerCase().includes(searchTerm)) {
score += weights.usage;
fields.push('usage');
}
// 코드 매치
if (example.code.toLowerCase().includes(searchTerm)) {
score += weights.code;
fields.push('code');
}
// ID 매치
if (example.id.toLowerCase().includes(searchTerm)) {
score += weights.id;
fields.push('id');
}
return { score, fields };
}
/**
* 여러 예제들을 점수순으로 정렬합니다.
*/
static sortByScore(examples, query) {
return examples
.map(example => this.calculateScore(example, query))
.sort((a, b) => b.score - a.score);
}
/**
* 점수가 0보다 큰 결과만 필터링합니다.
*/
static filterRelevantResults(scoredResults) {
return scoredResults.filter(result => result.score > 0);
}
/**
* 검색 결과를 제한된 개수만큼 반환합니다.
*/
static limitResults(scoredResults, limit) {
return scoredResults.slice(0, limit);
}
/**
* 언어별 필터링과 함께 점수 계산을 수행합니다.
*/
static searchWithLanguageFilter(examples, query, languageFilter) {
let filteredExamples = examples;
if (languageFilter) {
filteredExamples = examples.filter(languageFilter);
}
return this.sortByScore(filteredExamples, query);
}
/**
* 카테고리별 필터링과 함께 점수 계산을 수행합니다.
*/
static searchWithCategoryFilter(examples, query, category) {
let filteredExamples = examples;
if (category) {
filteredExamples = examples.filter(example => example.category.toLowerCase() === category.toLowerCase());
}
return this.sortByScore(filteredExamples, query);
}
}
//# sourceMappingURL=weightedSearchEngine.js.map