diff --git a/src/components/SearchDemo.tsx b/src/components/SearchDemo.tsx
new file mode 100644
index 0000000..9c72ef8
--- /dev/null
+++ b/src/components/SearchDemo.tsx
@@ -0,0 +1,269 @@
+import React, { useState } from 'react';
+import { Search, Bot, Lightbulb, Play, CheckCircle } from 'lucide-react';
+import { useAppStore } from '../store/useAppStore';
+
+interface SearchExample {
+ query: string;
+ type: 'realtime' | 'ai';
+ description: string;
+ expectedResults: string[];
+}
+
+const searchExamples: SearchExample[] = [
+ {
+ query: 'react',
+ type: 'realtime',
+ description: '实时搜索仓库名称',
+ expectedResults: ['匹配名称包含"react"的仓库']
+ },
+ {
+ query: 'vue',
+ type: 'realtime',
+ description: '快速匹配Vue相关仓库',
+ expectedResults: ['Vue.js相关项目']
+ },
+ {
+ query: '查找所有笔记应用',
+ type: 'ai',
+ description: 'AI语义搜索中文查询',
+ expectedResults: ['Obsidian', 'Notion', 'Logseq等笔记工具']
+ },
+ {
+ query: 'find machine learning frameworks',
+ type: 'ai',
+ description: 'AI跨语言搜索',
+ expectedResults: ['TensorFlow', 'PyTorch', 'scikit-learn等ML框架']
+ },
+ {
+ query: '代码编辑器',
+ type: 'ai',
+ description: 'AI理解中文意图',
+ expectedResults: ['VSCode', 'Vim', 'Emacs等编辑器']
+ },
+ {
+ query: 'web development tools',
+ type: 'ai',
+ description: 'AI匹配开发工具',
+ expectedResults: ['Webpack', 'Vite', 'React等前端工具']
+ }
+];
+
+export const SearchDemo: React.FC = () => {
+ const { language } = useAppStore();
+ const [selectedExample, setSelectedExample] = useState
(null);
+ const [showDemo, setShowDemo] = useState(false);
+
+ const t = (zh: string, en: string) => language === 'zh' ? zh : en;
+
+ const handleExampleClick = (example: SearchExample) => {
+ setSelectedExample(example);
+ // 这里可以触发实际的搜索演示
+ console.log(`演示搜索: ${example.query} (${example.type})`);
+ };
+
+ if (!showDemo) {
+ return (
+
+
+
+
+
+
+
+
+ {t('搜索功能升级', 'Search Feature Upgrade')}
+
+
+ {t('体验全新的实时搜索和AI语义搜索功能', 'Experience new real-time and AI semantic search features')}
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ {t('搜索功能演示', 'Search Feature Demo')}
+
+
+ {t('点击下方示例体验不同的搜索模式', 'Click examples below to experience different search modes')}
+
+
+
+
+
+
+
+ {/* 实时搜索示例 */}
+
+
+
+
+ {t('实时搜索', 'Real-time Search')}
+
+
+ {searchExamples
+ .filter(example => example.type === 'realtime')
+ .map((example, index) => (
+
+ ))}
+
+
+ {/* AI搜索示例 */}
+
+
+
+
+ {t('AI语义搜索', 'AI Semantic Search')}
+
+
+ {searchExamples
+ .filter(example => example.type === 'ai')
+ .map((example, index) => (
+
+ ))}
+
+
+
+ {/* 选中示例的详细信息 */}
+ {selectedExample && (
+
+
+ {selectedExample.type === 'realtime' ? (
+
+ ) : (
+
+ )}
+
+ {selectedExample.description}
+
+
+
+
+
+ {t('预期结果:', 'Expected Results:')}
+
+
+ {selectedExample.expectedResults.map((result, index) => (
+ -
+
+ {result}
+
+ ))}
+
+
+
+
+
+ {selectedExample.type === 'realtime' ? (
+ t(
+ '💡 实时搜索会在您输入时立即显示匹配的仓库名称,响应速度极快。',
+ '💡 Real-time search instantly shows matching repository names as you type, with extremely fast response.'
+ )
+ ) : (
+ t(
+ '🤖 AI搜索使用语义理解,能够跨语言匹配并智能排序结果,适合复杂查询。',
+ '🤖 AI search uses semantic understanding, can match across languages and intelligently rank results, perfect for complex queries.'
+ )
+ )}
+
+
+
+ )}
+
+ {/* 使用提示 */}
+
+
+ {t('使用技巧', 'Usage Tips')}
+
+
+
+
+
+
+ {t('实时搜索', 'Real-time Search')}
+
+
+
+ - • {t('输入时自动触发', 'Automatically triggered while typing')}
+ - • {t('匹配仓库名称', 'Matches repository names')}
+ - • {t('支持中文输入法', 'Supports Chinese IME')}
+ - • {t('响应速度快', 'Fast response time')}
+
+
+
+
+
+
+ {t('AI语义搜索', 'AI Semantic Search')}
+
+
+
+ - • {t('点击AI搜索按钮触发', 'Click AI Search button to trigger')}
+ - • {t('支持自然语言查询', 'Supports natural language queries')}
+ - • {t('跨语言匹配', 'Cross-language matching')}
+ - • {t('智能结果排序', 'Intelligent result ranking')}
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/services/aiService.ts b/src/services/aiService.ts
index d55c23e..4239c4c 100644
--- a/src/services/aiService.ts
+++ b/src/services/aiService.ts
@@ -340,6 +340,55 @@ Focus on practicality and accurate categorization to help users quickly understa
return this.performBasicSearch(repositories, query);
}
+ async searchRepositoriesWithReranking(repositories: Repository[], query: string): Promise {
+ if (!query.trim()) return repositories;
+
+ try {
+ // Step 1: Get AI-enhanced search terms and semantic understanding
+ const searchPrompt = this.createEnhancedSearchPrompt(query);
+
+ const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${this.config.apiKey}`,
+ },
+ body: JSON.stringify({
+ model: this.config.model,
+ messages: [
+ {
+ role: 'system',
+ content: this.language === 'zh'
+ ? '你是一个专业的仓库搜索和排序助手。请理解用户的搜索意图,提供多语言关键词匹配,并对搜索结果进行智能排序。'
+ : 'You are a professional repository search and ranking assistant. Please understand user search intent, provide multilingual keyword matching, and intelligently rank search results.',
+ },
+ {
+ role: 'user',
+ content: searchPrompt,
+ },
+ ],
+ temperature: 0.1,
+ max_tokens: 300,
+ }),
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ const content = data.choices[0]?.message?.content;
+
+ if (content) {
+ const searchAnalysis = this.parseEnhancedSearchResponse(content);
+ return this.performSemanticSearchWithReranking(repositories, query, searchAnalysis);
+ }
+ }
+ } catch (error) {
+ console.warn('AI enhanced search failed, falling back to basic search:', error);
+ }
+
+ // Fallback to basic search
+ return this.performBasicSearch(repositories, query);
+ }
+
private createSearchPrompt(query: string): string {
if (this.language === 'zh') {
return `
@@ -376,6 +425,74 @@ Reply in JSON format:
}
}
+ private createEnhancedSearchPrompt(query: string): string {
+ if (this.language === 'zh') {
+ return `
+用户搜索查询: "${query}"
+
+请深度分析这个搜索查询并提供:
+1. 核心搜索意图和目标
+2. 多语言关键词(中文、英文、技术术语)
+3. 相关的应用类型、技术栈、平台类型
+4. 同义词和相关概念
+5. 重要性权重(用于排序)
+
+以JSON格式回复:
+{
+ "intent": "用户的核心搜索意图",
+ "keywords": {
+ "primary": ["主要关键词1", "primary keyword1"],
+ "secondary": ["次要关键词1", "secondary keyword1"],
+ "technical": ["技术术语1", "technical term1"]
+ },
+ "categories": ["应用分类1", "category1"],
+ "platforms": ["平台类型1", "platform1"],
+ "synonyms": ["同义词1", "synonym1"],
+ "weights": {
+ "name_match": 0.4,
+ "description_match": 0.3,
+ "tags_match": 0.2,
+ "summary_match": 0.1
+ }
+}
+
+注意:请确保能够跨语言匹配,即使用户用中文搜索,也要能匹配到英文仓库,反之亦然。
+ `.trim();
+ } else {
+ return `
+User search query: "${query}"
+
+Please deeply analyze this search query and provide:
+1. Core search intent and objectives
+2. Multilingual keywords (Chinese, English, technical terms)
+3. Related application types, tech stacks, platform types
+4. Synonyms and related concepts
+5. Importance weights (for ranking)
+
+Reply in JSON format:
+{
+ "intent": "User's core search intent",
+ "keywords": {
+ "primary": ["primary keyword1", "主要关键词1"],
+ "secondary": ["secondary keyword1", "次要关键词1"],
+ "technical": ["technical term1", "技术术语1"]
+ },
+ "categories": ["category1", "应用分类1"],
+ "platforms": ["platform1", "平台类型1"],
+ "synonyms": ["synonym1", "同义词1"],
+ "weights": {
+ "name_match": 0.4,
+ "description_match": 0.3,
+ "tags_match": 0.2,
+ "summary_match": 0.1
+ }
+}
+
+Note: Ensure cross-language matching, so Chinese queries can match English repositories and vice versa.
+ `.trim();
+ }
+ }
+
private parseSearchResponse(content: string): string[] {
try {
const jsonMatch = content.match(/\{[\s\S]*\}/);
@@ -394,6 +511,61 @@ Reply in JSON format:
return [];
}
+ private parseEnhancedSearchResponse(content: string): {
+ intent: string;
+ keywords: {
+ primary: string[];
+ secondary: string[];
+ technical: string[];
+ };
+ categories: string[];
+ platforms: string[];
+ synonyms: string[];
+ weights: {
+ name_match: number;
+ description_match: number;
+ tags_match: number;
+ summary_match: number;
+ };
+ } {
+ const defaultResponse = {
+ intent: '',
+ keywords: { primary: [], secondary: [], technical: [] },
+ categories: [],
+ platforms: [],
+ synonyms: [],
+ weights: { name_match: 0.4, description_match: 0.3, tags_match: 0.2, summary_match: 0.1 }
+ };
+
+ try {
+ const jsonMatch = content.match(/\{[\s\S]*\}/);
+ if (jsonMatch) {
+ const parsed = JSON.parse(jsonMatch[0]);
+ return {
+ intent: parsed.intent || '',
+ keywords: {
+ primary: Array.isArray(parsed.keywords?.primary) ? parsed.keywords.primary : [],
+ secondary: Array.isArray(parsed.keywords?.secondary) ? parsed.keywords.secondary : [],
+ technical: Array.isArray(parsed.keywords?.technical) ? parsed.keywords.technical : []
+ },
+ categories: Array.isArray(parsed.categories) ? parsed.categories : [],
+ platforms: Array.isArray(parsed.platforms) ? parsed.platforms : [],
+ synonyms: Array.isArray(parsed.synonyms) ? parsed.synonyms : [],
+ weights: {
+ name_match: parsed.weights?.name_match || 0.4,
+ description_match: parsed.weights?.description_match || 0.3,
+ tags_match: parsed.weights?.tags_match || 0.2,
+ summary_match: parsed.weights?.summary_match || 0.1
+ }
+ };
+ }
+ } catch (error) {
+ console.warn('Failed to parse enhanced AI search response:', error);
+ }
+
+ return defaultResponse;
+ }
+
private performEnhancedSearch(repositories: Repository[], originalQuery: string, aiTerms: string[]): Repository[] {
const allSearchTerms = [originalQuery, ...aiTerms];
@@ -419,6 +591,145 @@ Reply in JSON format:
});
}
+ private performSemanticSearchWithReranking(
+ repositories: Repository[],
+ originalQuery: string,
+ searchAnalysis: any
+ ): Repository[] {
+ // Collect all search terms from the analysis
+ const allSearchTerms = [
+ originalQuery,
+ ...searchAnalysis.keywords.primary,
+ ...searchAnalysis.keywords.secondary,
+ ...searchAnalysis.keywords.technical,
+ ...searchAnalysis.categories,
+ ...searchAnalysis.platforms,
+ ...searchAnalysis.synonyms
+ ].filter(term => term && typeof term === 'string');
+
+ // First, filter repositories that match any search terms
+ const matchedRepos = repositories.filter(repo => {
+ const searchableFields = {
+ name: repo.name.toLowerCase(),
+ fullName: repo.full_name.toLowerCase(),
+ description: (repo.description || '').toLowerCase(),
+ language: (repo.language || '').toLowerCase(),
+ topics: (repo.topics || []).join(' ').toLowerCase(),
+ aiSummary: (repo.ai_summary || '').toLowerCase(),
+ aiTags: (repo.ai_tags || []).join(' ').toLowerCase(),
+ aiPlatforms: (repo.ai_platforms || []).join(' ').toLowerCase(),
+ customDescription: (repo.custom_description || '').toLowerCase(),
+ customTags: (repo.custom_tags || []).join(' ').toLowerCase()
+ };
+
+ // Check if any search term matches any field
+ return allSearchTerms.some(term => {
+ const normalizedTerm = term.toLowerCase();
+ return Object.values(searchableFields).some(fieldValue => {
+ return fieldValue.includes(normalizedTerm) ||
+ // Fuzzy matching for partial matches
+ normalizedTerm.split(/\s+/).every(word => fieldValue.includes(word));
+ });
+ });
+ });
+
+ // If no matches found, return empty array (don't show irrelevant results)
+ if (matchedRepos.length === 0) {
+ return [];
+ }
+
+ // Calculate relevance scores for matched repositories
+ const scoredRepos = matchedRepos.map(repo => {
+ let score = 0;
+ const weights = searchAnalysis.weights;
+
+ const searchableFields = {
+ name: repo.name.toLowerCase(),
+ fullName: repo.full_name.toLowerCase(),
+ description: (repo.description || '').toLowerCase(),
+ language: (repo.language || '').toLowerCase(),
+ topics: (repo.topics || []).join(' ').toLowerCase(),
+ aiSummary: (repo.ai_summary || '').toLowerCase(),
+ aiTags: (repo.ai_tags || []).join(' ').toLowerCase(),
+ aiPlatforms: (repo.ai_platforms || []).join(' ').toLowerCase(),
+ customDescription: (repo.custom_description || '').toLowerCase(),
+ customTags: (repo.custom_tags || []).join(' ').toLowerCase()
+ };
+
+ // Score based on different types of matches
+ allSearchTerms.forEach(term => {
+ const normalizedTerm = term.toLowerCase();
+
+ // Name matches (highest weight)
+ if (searchableFields.name.includes(normalizedTerm) || searchableFields.fullName.includes(normalizedTerm)) {
+ score += weights.name_match;
+ }
+
+ // Description matches
+ if (searchableFields.description.includes(normalizedTerm) || searchableFields.customDescription.includes(normalizedTerm)) {
+ score += weights.description_match;
+ }
+
+ // Tags and topics matches
+ if (searchableFields.topics.includes(normalizedTerm) ||
+ searchableFields.aiTags.includes(normalizedTerm) ||
+ searchableFields.customTags.includes(normalizedTerm)) {
+ score += weights.tags_match;
+ }
+
+ // AI summary matches
+ if (searchableFields.aiSummary.includes(normalizedTerm)) {
+ score += weights.summary_match;
+ }
+
+ // Platform matches
+ if (searchableFields.aiPlatforms.includes(normalizedTerm)) {
+ score += weights.tags_match * 0.8; // Slightly lower than tags
+ }
+
+ // Language matches
+ if (searchableFields.language.includes(normalizedTerm)) {
+ score += weights.tags_match * 0.6;
+ }
+ });
+
+ // Boost score for primary keywords
+ searchAnalysis.keywords.primary.forEach(primaryTerm => {
+ const normalizedTerm = primaryTerm.toLowerCase();
+ Object.values(searchableFields).forEach(fieldValue => {
+ if (fieldValue.includes(normalizedTerm)) {
+ score += 0.2; // Additional boost for primary keywords
+ }
+ });
+ });
+
+ // Boost score for exact matches
+ const exactMatch = allSearchTerms.some(term => {
+ const normalizedTerm = term.toLowerCase();
+ return searchableFields.name === normalizedTerm ||
+ searchableFields.name.includes(` ${normalizedTerm} `) ||
+ searchableFields.name.startsWith(`${normalizedTerm} `) ||
+ searchableFields.name.endsWith(` ${normalizedTerm}`);
+ });
+
+ if (exactMatch) {
+ score += 0.5;
+ }
+
+ // Consider repository popularity as a tie-breaker
+ const popularityScore = Math.log10(repo.stargazers_count + 1) * 0.05;
+ score += popularityScore;
+
+ return { repo, score };
+ });
+
+ // Sort by relevance score (descending) and return only repositories with meaningful scores
+ return scoredRepos
+ .filter(item => item.score > 0.1) // Filter out very low relevance matches
+ .sort((a, b) => b.score - a.score)
+ .map(item => item.repo);
+ }
+
private performBasicSearch(repositories: Repository[], query: string): Repository[] {
const normalizedQuery = query.toLowerCase();
diff --git a/src/utils/searchTestUtils.ts b/src/utils/searchTestUtils.ts
new file mode 100644
index 0000000..fe2f23e
--- /dev/null
+++ b/src/utils/searchTestUtils.ts
@@ -0,0 +1,283 @@
+import { Repository } from '../types';
+
+/**
+ * 搜索功能测试工具
+ * 用于验证实时搜索和AI搜索的功能
+ */
+
+// 模拟仓库数据用于测试
+export const mockRepositories: Repository[] = [
+ {
+ id: 1,
+ name: 'react',
+ full_name: 'facebook/react',
+ description: 'A declarative, efficient, and flexible JavaScript library for building user interfaces.',
+ html_url: 'https://github.com/facebook/react',
+ stargazers_count: 220000,
+ language: 'JavaScript',
+ created_at: '2013-05-24T16:15:54Z',
+ updated_at: '2024-01-15T10:30:00Z',
+ pushed_at: '2024-01-15T10:30:00Z',
+ owner: {
+ login: 'facebook',
+ avatar_url: 'https://avatars.githubusercontent.com/u/69631?v=4'
+ },
+ topics: ['javascript', 'react', 'frontend', 'ui'],
+ ai_summary: '一个用于构建用户界面的声明式、高效且灵活的JavaScript库',
+ ai_tags: ['前端框架', 'UI库', 'JavaScript工具'],
+ ai_platforms: ['web', 'cli']
+ },
+ {
+ id: 2,
+ name: 'vue',
+ full_name: 'vuejs/vue',
+ description: 'Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.',
+ html_url: 'https://github.com/vuejs/vue',
+ stargazers_count: 207000,
+ language: 'JavaScript',
+ created_at: '2013-07-29T03:24:51Z',
+ updated_at: '2024-01-14T15:20:00Z',
+ pushed_at: '2024-01-14T15:20:00Z',
+ owner: {
+ login: 'vuejs',
+ avatar_url: 'https://avatars.githubusercontent.com/u/6128107?v=4'
+ },
+ topics: ['javascript', 'vue', 'frontend', 'framework'],
+ ai_summary: '渐进式、可逐步采用的JavaScript框架,用于构建Web UI',
+ ai_tags: ['前端框架', 'Web应用', 'JavaScript工具'],
+ ai_platforms: ['web']
+ },
+ {
+ id: 3,
+ name: 'vscode',
+ full_name: 'microsoft/vscode',
+ description: 'Visual Studio Code',
+ html_url: 'https://github.com/microsoft/vscode',
+ stargazers_count: 158000,
+ language: 'TypeScript',
+ created_at: '2015-09-03T20:23:21Z',
+ updated_at: '2024-01-16T09:45:00Z',
+ pushed_at: '2024-01-16T09:45:00Z',
+ owner: {
+ login: 'microsoft',
+ avatar_url: 'https://avatars.githubusercontent.com/u/6154722?v=4'
+ },
+ topics: ['editor', 'typescript', 'electron'],
+ ai_summary: '功能强大的代码编辑器,支持多种编程语言和扩展',
+ ai_tags: ['代码编辑器', '开发工具', 'IDE'],
+ ai_platforms: ['windows', 'mac', 'linux']
+ },
+ {
+ id: 4,
+ name: 'obsidian-sample-plugin',
+ full_name: 'obsidianmd/obsidian-sample-plugin',
+ description: 'Sample plugin for Obsidian (https://obsidian.md)',
+ html_url: 'https://github.com/obsidianmd/obsidian-sample-plugin',
+ stargazers_count: 2500,
+ language: 'TypeScript',
+ created_at: '2020-10-15T14:30:00Z',
+ updated_at: '2024-01-10T11:15:00Z',
+ pushed_at: '2024-01-10T11:15:00Z',
+ owner: {
+ login: 'obsidianmd',
+ avatar_url: 'https://avatars.githubusercontent.com/u/65011256?v=4'
+ },
+ topics: ['obsidian', 'plugin', 'notes', 'markdown'],
+ ai_summary: 'Obsidian笔记应用的示例插件,展示如何开发笔记工具扩展',
+ ai_tags: ['笔记工具', '插件开发', '效率工具'],
+ ai_platforms: ['windows', 'mac', 'linux']
+ },
+ {
+ id: 5,
+ name: 'tensorflow',
+ full_name: 'tensorflow/tensorflow',
+ description: 'An Open Source Machine Learning Framework for Everyone',
+ html_url: 'https://github.com/tensorflow/tensorflow',
+ stargazers_count: 185000,
+ language: 'C++',
+ created_at: '2015-11-07T01:19:20Z',
+ updated_at: '2024-01-16T14:20:00Z',
+ pushed_at: '2024-01-16T14:20:00Z',
+ owner: {
+ login: 'tensorflow',
+ avatar_url: 'https://avatars.githubusercontent.com/u/15658638?v=4'
+ },
+ topics: ['machine-learning', 'deep-learning', 'neural-networks', 'ai'],
+ ai_summary: '开源机器学习框架,支持深度学习和神经网络开发',
+ ai_tags: ['机器学习', 'AI框架', '深度学习'],
+ ai_platforms: ['linux', 'mac', 'windows', 'docker']
+ }
+];
+
+/**
+ * 测试实时搜索功能
+ */
+export function testRealTimeSearch(repositories: Repository[], query: string): Repository[] {
+ if (!query.trim()) return repositories;
+
+ const normalizedQuery = query.toLowerCase();
+ return repositories.filter(repo => {
+ return repo.name.toLowerCase().includes(normalizedQuery) ||
+ repo.full_name.toLowerCase().includes(normalizedQuery);
+ });
+}
+
+/**
+ * 测试基础文本搜索功能
+ */
+export function testBasicTextSearch(repositories: Repository[], query: string): Repository[] {
+ if (!query.trim()) return repositories;
+
+ const normalizedQuery = query.toLowerCase();
+
+ return repositories.filter(repo => {
+ const searchableText = [
+ repo.name,
+ repo.full_name,
+ repo.description || '',
+ repo.language || '',
+ ...(repo.topics || []),
+ repo.ai_summary || '',
+ ...(repo.ai_tags || []),
+ ...(repo.ai_platforms || []),
+ ].join(' ').toLowerCase();
+
+ // Split query into words and check if all words are present
+ const queryWords = normalizedQuery.split(/\s+/);
+ return queryWords.every(word => searchableText.includes(word));
+ });
+}
+
+/**
+ * 测试搜索场景
+ */
+export const searchTestCases = [
+ {
+ name: '实时搜索 - 仓库名匹配',
+ type: 'realtime',
+ queries: [
+ { query: 'react', expectedCount: 1, description: '应该找到react仓库' },
+ { query: 'vue', expectedCount: 1, description: '应该找到vue仓库' },
+ { query: 'vs', expectedCount: 1, description: '应该找到vscode仓库' },
+ { query: 'obsidian', expectedCount: 1, description: '应该找到obsidian相关仓库' }
+ ]
+ },
+ {
+ name: '基础文本搜索 - 多字段匹配',
+ type: 'basic',
+ queries: [
+ { query: 'javascript', expectedCount: 2, description: '应该找到JavaScript相关仓库' },
+ { query: '前端框架', expectedCount: 2, description: '应该找到前端框架相关仓库' },
+ { query: 'machine learning', expectedCount: 1, description: '应该找到机器学习相关仓库' },
+ { query: '笔记', expectedCount: 1, description: '应该找到笔记相关仓库' },
+ { query: 'editor', expectedCount: 1, description: '应该找到编辑器相关仓库' }
+ ]
+ },
+ {
+ name: 'AI搜索测试场景',
+ type: 'ai',
+ queries: [
+ { query: '查找所有前端框架', description: '应该匹配React和Vue' },
+ { query: 'find note-taking apps', description: '应该匹配Obsidian插件' },
+ { query: '代码编辑器', description: '应该匹配VSCode' },
+ { query: 'AI工具', description: '应该匹配TensorFlow' },
+ { query: 'web development tools', description: '应该匹配前端相关工具' }
+ ]
+ }
+];
+
+/**
+ * 运行搜索测试
+ */
+export function runSearchTests(): void {
+ console.log('🔍 开始搜索功能测试...\n');
+
+ searchTestCases.forEach(testCase => {
+ console.log(`📋 测试类型: ${testCase.name}`);
+
+ if (testCase.type === 'realtime') {
+ testCase.queries.forEach(({ query, expectedCount, description }) => {
+ const results = testRealTimeSearch(mockRepositories, query);
+ const passed = results.length === expectedCount;
+ console.log(` ${passed ? '✅' : '❌'} "${query}" - ${description} (期望: ${expectedCount}, 实际: ${results.length})`);
+ if (!passed) {
+ console.log(` 找到的仓库: ${results.map(r => r.name).join(', ')}`);
+ }
+ });
+ } else if (testCase.type === 'basic') {
+ testCase.queries.forEach(({ query, expectedCount, description }) => {
+ const results = testBasicTextSearch(mockRepositories, query);
+ const passed = results.length === expectedCount;
+ console.log(` ${passed ? '✅' : '❌'} "${query}" - ${description} (期望: ${expectedCount}, 实际: ${results.length})`);
+ if (!passed) {
+ console.log(` 找到的仓库: ${results.map(r => r.name).join(', ')}`);
+ }
+ });
+ } else if (testCase.type === 'ai') {
+ testCase.queries.forEach(({ query, description }) => {
+ console.log(` 🤖 "${query}" - ${description} (需要AI服务支持)`);
+ });
+ }
+
+ console.log('');
+ });
+
+ console.log('🎉 搜索功能测试完成!');
+}
+
+/**
+ * 性能测试
+ */
+export function performanceTest(repositories: Repository[], iterations: number = 1000): void {
+ console.log(`⚡ 开始性能测试 (${iterations} 次迭代)...\n`);
+
+ const testQueries = ['react', 'javascript', '前端', 'machine learning'];
+
+ testQueries.forEach(query => {
+ // 实时搜索性能测试
+ const realtimeStart = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ testRealTimeSearch(repositories, query);
+ }
+ const realtimeEnd = performance.now();
+ const realtimeAvg = (realtimeEnd - realtimeStart) / iterations;
+
+ // 基础搜索性能测试
+ const basicStart = performance.now();
+ for (let i = 0; i < iterations; i++) {
+ testBasicTextSearch(repositories, query);
+ }
+ const basicEnd = performance.now();
+ const basicAvg = (basicEnd - basicStart) / iterations;
+
+ console.log(`查询 "${query}":`);
+ console.log(` 实时搜索平均耗时: ${realtimeAvg.toFixed(3)}ms`);
+ console.log(` 基础搜索平均耗时: ${basicAvg.toFixed(3)}ms`);
+ console.log(` 性能比率: ${(basicAvg / realtimeAvg).toFixed(2)}x\n`);
+ });
+}
+
+/**
+ * 中文输入法测试场景
+ */
+export const imeTestCases = [
+ {
+ description: '中文拼音输入测试',
+ scenarios: [
+ { input: 'qian', expected: '前', description: '拼音输入过程中不应触发搜索' },
+ { input: 'qianduan', expected: '前端', description: '完整拼音输入' },
+ { input: 'biji', expected: '笔记', description: '笔记应用搜索' }
+ ]
+ }
+];
+
+// 导出给开发者使用的测试函数
+export default {
+ mockRepositories,
+ testRealTimeSearch,
+ testBasicTextSearch,
+ searchTestCases,
+ runSearchTests,
+ performanceTest,
+ imeTestCases
+};
\ No newline at end of file