Orama 是一个开源的 JavaScript 搜索引擎支持在浏览器、服务器和边缘网络中运行完整的搜索和 RAG 流程。它最吸引人的特点是体积极小小于 2KB却提供了全文搜索、向量搜索和混合搜索等企业级功能。对于需要在本地或边缘环境部署搜索服务的开发者来说这是一个值得关注的选择。这个项目由 oramasearch 团队维护目前 GitHub 上有 10.5k 星支持 TypeScript 开发采用 Apache 2.0 开源协议。它最大的优势是轻量级和跨平台部署能力可以在浏览器端直接运行不需要依赖外部搜索服务。本文将详细介绍 Orama 的核心功能、部署方式、API 使用方法和实际应用场景。我们会从环境准备开始逐步演示如何创建搜索数据库、插入数据、执行各种搜索操作最后还会介绍如何通过插件扩展功能构建完整的 RAG 应用。1. 核心能力速览能力项说明项目类型JavaScript/TypeScript 搜索引擎库开源协议Apache 2.0核心功能全文搜索、向量搜索、混合搜索、RAG 流程部署环境浏览器、Node.js、Deno、边缘网络包体积小于 2KB核心功能搜索模式全文搜索、向量搜索、混合搜索数据类型支持 10 种数据类型包括字符串、数字、布尔值、枚举、地理点、数组和向量插件系统支持功能扩展官方提供 embeddings、安全代理等插件API 支持完整的 JavaScript/TypeScript API适合场景本地搜索应用、边缘计算、浏览器内搜索、RAG 系统2. 适用场景与使用边界Orama 最适合需要轻量级、可嵌入搜索功能的场景。比如在静态网站中实现客户端搜索避免依赖外部搜索服务或者在边缘计算环境中部署搜索功能减少网络延迟。对于中小型数据集的本地搜索需求Orama 能够提供很好的性能表现。在 RAG检索增强生成应用场景中Orama 可以作为检索组件与 LLM 结合构建智能问答系统。它的混合搜索能力能够同时利用关键词匹配和语义相似度提高检索准确性。需要注意的是Orama 主要针对中小规模数据集优化。对于亿级数据量的搜索需求可能需要考虑分布式搜索方案。此外虽然 Orama 支持向量搜索但向量生成需要依赖外部模型或插件在实际部署时要考虑 embeddings 生成的计算成本。在合规性方面使用 Orama 处理用户数据时需要注意数据隐私保护。特别是在浏览器端部署时要确保敏感数据不会泄露。对于商业应用建议仔细阅读 Apache 2.0 协议的具体条款。3. 环境准备与前置条件Orama 的环境要求相对简单主要取决于你的部署目标环境。3.1 浏览器环境在现代浏览器中可以直接通过 ES 模块导入使用支持 Chrome 90、Firefox 88、Safari 14 等主流浏览器。不需要额外的构建工具但建议使用支持 ES 模块的现代浏览器版本。3.2 Node.js 环境推荐使用 Node.js 16 版本确保支持 ES 模块。可以通过 npm、yarn、pnpm 或 bun 安装# 使用 npm npm install orama/orama # 使用 yarn yarn add orama/orama # 使用 pnpm pnpm add orama/orama # 使用 bun bun add orama/orama3.3 Deno 环境在 Deno 中可以直接通过 npm 说明符或 CDN 导入// 使用 npm 说明符 import { create, search, insert } from npm:orama/orama // 或使用 CDN import { create, insert, search } from https://cdn.jsdelivr.net/npm/orama/oramalatest/esm3.4 硬件要求由于 Orama 本身很轻量硬件要求主要取决于数据量大小。对于中小型数据集数万条记录在普通配置的电脑或服务器上都能良好运行。向量搜索功能需要额外的计算资源具体取决于向量维度和数据量。4. 安装部署与启动方式Orama 的安装和启动方式非常灵活下面介绍几种常见的部署方案。4.1 浏览器直接使用最简单的使用方式是在 HTML 中直接导入!DOCTYPE html html head titleOrama 搜索示例/title /head body script typemodule import { create, insert, search } from https://cdn.jsdelivr.net/npm/orama/oramalatest/esm // 立即创建搜索数据库 const db await create({ schema: { title: string, content: string } }) // 插入示例数据 await insert(db, { title: 欢迎使用 Orama, content: 这是一个轻量级搜索引擎示例 }) console.log(Orama 已初始化完成) /script /body /html4.2 Node.js 项目集成在 Node.js 项目中首先安装依赖然后创建搜索服务// searchService.js import { create, insert, search } from orama/orama class SearchService { constructor() { this.db null } async initialize() { this.db await create({ schema: { id: string, title: string, description: string, content: string, category: string, tags: string[], rating: number, embedding: vector[384] } }) } async addDocument(doc) { return await insert(this.db, doc) } async searchDocuments(query, options {}) { return await search(this.db, { term: query, ...options }) } } // 使用示例 const searchService new SearchService() await searchService.initialize() // 添加文档 await searchService.addDocument({ id: doc1, title: JavaScript 教程, description: 全面的 JavaScript 学习指南, content: JavaScript 是一种动态编程语言..., category: programming, tags: [javascript, web, frontend], rating: 4.5 }) // 执行搜索 const results await searchService.searchDocuments(JavaScript 学习) console.log(results)4.3 Express.js API 服务可以基于 Express.js 构建搜索 API 服务// server.js import express from express import { create, insert, search } from orama/orama const app express() app.use(express.json()) let db null // 初始化搜索数据库 async function initializeSearch() { db await create({ schema: { id: string, title: string, content: string, type: string } }) // 预加载示例数据 await insert(db, { id: 1, title: Orama 搜索指南, content: 学习如何使用 Orama 构建搜索功能, type: guide }) } // 搜索 API app.post(/api/search, async (req, res) { try { const { query, limit 10, offset 0 } req.body const results await search(db, { term: query, limit, offset }) res.json({ success: true, data: results }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } }) // 添加文档 API app.post(/api/documents, async (req, res) { try { const document req.body const result await insert(db, document) res.json({ success: true, data: result }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } }) // 启动服务 initializeSearch().then(() { app.listen(3000, () { console.log(搜索服务运行在 http://localhost:3000) }) })5. 功能测试与效果验证5.1 基础全文搜索测试首先测试最基本的全文搜索功能import { create, insert, search } from orama/orama // 创建测试数据库 const db await create({ schema: { name: string, description: string, price: number, category: string } }) // 插入测试数据 const products [ { name: 无线耳机, description: 高品质无线蓝牙耳机降噪功能, price: 299, category: electronics }, { name: 机械键盘, description: 青轴机械键盘RGB 背光, price: 450, category: electronics }, { name: 编程书籍, description: JavaScript 高级程序设计, price: 89, category: books } ] for (const product of products) { await insert(db, product) } // 测试搜索功能 const testCases [ { query: 耳机, expected: 无线耳机 }, { query: 键盘, expected: 机械键盘 }, { query: 编程, expected: 编程书籍 }, { query: 降噪, expected: 无线耳机 } ] for (const testCase of testCases) { const results await search(db, { term: testCase.query, properties: [name, description] }) console.log(搜索 ${testCase.query}:) console.log(- 找到 ${results.count} 个结果) console.log(- 最相关: ${results.hits[0]?.document.name}) console.log(- 预期: ${testCase.expected}) console.log(---) }5.2 向量搜索测试测试 Orama 的向量搜索能力import { create, insertMultiple, search } from orama/orama // 创建支持向量搜索的数据库 const db await create({ schema: { title: string, embedding: vector[5] // 5维向量示例 } }) // 插入带向量的数据 await insertMultiple(db, [ { title: 人工智能研究, embedding: [0.9, 0.8, 0.1, 0.2, 0.3] }, { title: 机器学习算法, embedding: [0.8, 0.7, 0.2, 0.3, 0.4] }, { title: 网页开发教程, embedding: [0.1, 0.2, 0.9, 0.8, 0.7] } ]) // 向量搜索测试 const vectorResults await search(db, { mode: vector, vector: { value: [0.85, 0.75, 0.15, 0.25, 0.35], // 搜索向量 property: embedding }, similarity: 0.7, // 相似度阈值 limit: 5 }) console.log(向量搜索结果:) vectorResults.hits.forEach((hit, index) { console.log(${index 1}. ${hit.document.title} (相似度: ${hit.score.toFixed(3)})) })5.3 混合搜索测试测试结合关键词和向量搜索的混合模式// 混合搜索示例 const hybridResults await search(db, { term: 人工智能, // 关键词 mode: hybrid, // 混合模式 vector: { value: [0.85, 0.75, 0.15, 0.25, 0.35], property: embedding }, similarity: 0.6, limit: 10 }) console.log(混合搜索结果:) hybridResults.hits.forEach((hit, index) { console.log(${index 1}. ${hit.document.title} (分数: ${hit.score.toFixed(3)})) })6. 接口 API 与批量任务6.1 完整的 REST API 实现下面实现一个功能完整的搜索 API 服务// advancedSearchService.js import express from express import { create, insert, search, remove } from orama/orama const app express() app.use(express.json()) class AdvancedSearchService { constructor() { this.db null this.app express() this.setupMiddleware() this.setupRoutes() } setupMiddleware() { this.app.use(express.json()) this.app.use((req, res, next) { console.log(${new Date().toISOString()} - ${req.method} ${req.path}) next() }) } setupRoutes() { // 健康检查 this.app.get(/health, (req, res) { res.json({ status: healthy, timestamp: new Date().toISOString() }) }) // 搜索接口 this.app.post(/search, this.handleSearch.bind(this)) // 批量插入接口 this.app.post(/documents/batch, this.handleBatchInsert.bind(this)) // 文档管理接口 this.app.get(/documents/:id, this.handleGetDocument.bind(this)) this.app.delete(/documents/:id, this.handleDeleteDocument.bind(this)) // 统计信息 this.app.get(/stats, this.handleGetStats.bind(this)) } async initialize(schema) { this.db await create({ schema }) console.log(搜索数据库初始化完成) } async handleSearch(req, res) { try { const { query, mode fulltext, properties, limit 20, offset 0, similarity, vector } req.body const searchParams { term: query, mode, limit, offset } if (properties) searchParams.properties properties if (similarity) searchParams.similarity similarity if (vector) searchParams.vector vector const results await search(this.db, searchParams) res.json({ success: true, data: { query, total: results.count, hits: results.hits, elapsed: results.elapsed } }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } } async handleBatchInsert(req, res) { try { const { documents } req.body if (!Array.isArray(documents)) { return res.status(400).json({ success: false, error: documents 必须是数组 }) } const results [] for (const doc of documents) { try { const result await insert(this.db, doc) results.push({ success: true, id: result.id }) } catch (error) { results.push({ success: false, error: error.message }) } } res.json({ success: true, data: { total: documents.length, processed: results.length, results } }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } } async handleGetDocument(req, res) { // 简化实现实际需要维护文档索引 res.json({ message: 文档获取功能待实现 }) } async handleDeleteDocument(req, res) { try { const { id } req.params await remove(this.db, id) res.json({ success: true, message: 文档删除成功 }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } } async handleGetStats(req, res) { // 返回搜索统计信息 res.json({ success: true, data: { initialized: !!this.db, timestamp: new Date().toISOString() } }) } start(port 3000) { return this.app.listen(port, () { console.log(搜索服务运行在端口 ${port}) }) } } // 使用示例 const searchService new AdvancedSearchService() // 初始化数据库 schema await searchService.initialize({ schema: { id: string, title: string, content: string, tags: string[], category: string, importance: number, embedding: vector[1536], createdAt: string } }) // 启动服务 searchService.start(3000)6.2 批量任务处理对于大量数据的处理需要实现批量任务队列// batchProcessor.js import { create, insertMultiple } from orama/orama class BatchProcessor { constructor(batchSize 100) { this.batchSize batchSize this.queue [] this.processing false } async addDocuments(documents) { this.queue.push(...documents) if (!this.processing) { this.processQueue() } } async processQueue() { this.processing true while (this.queue.length 0) { const batch this.queue.splice(0, this.batchSize) try { await this.processBatch(batch) console.log(处理了 ${batch.length} 个文档剩余 ${this.queue.length} 个) } catch (error) { console.error(批量处理失败:, error) // 重试逻辑可以在这里实现 } // 添加延迟避免过度占用资源 await this.delay(100) } this.processing false } async processBatch(documents) { // 实际处理逻辑 return await insertMultiple(this.db, documents) } delay(ms) { return new Promise(resolve setTimeout(resolve, ms)) } } // 使用批量处理器 const processor new BatchProcessor(50) // 模拟添加大量文档 const mockDocuments Array.from({ length: 1000 }, (_, i) ({ id: doc-${i}, title: 文档 ${i}, content: 这是第 ${i} 个文档的内容, category: i % 2 0 ? tech : general })) processor.addDocuments(mockDocuments)7. 插件系统与功能扩展Orama 的插件系统是其强大功能的关键下面介绍几个重要插件的使用方法。7.1 Embeddings 插件使用官方 embeddings 插件自动生成文本向量import { create } from orama/orama import { pluginEmbeddings } from orama/plugin-embeddings // 初始化 embeddings 插件 const plugin await pluginEmbeddings({ embeddings: { defaultProperty: embeddings, onInsert: { generate: true, properties: [description], verbose: true, } } }) // 创建支持自动 embeddings 的数据库 const db await create({ schema: { description: string, embeddings: vector[512] // 插件生成 512 维向量 }, plugins: [plugin] }) // 插入数据时会自动生成 embeddings await insert(db, { description: Classroom Headphones Bulk 5 Pack, Student On Ear Color Varieties }) await insert(db, { description: Kids Wired Headphones for School Students K-12 }) // 搜索时自动使用 embeddings const searchResults await search(db, { term: Headphones for 12th grade students, mode: vector, similarity: 0.75, })7.2 安全代理插件安全地调用 OpenAI 等外部 APIimport { create, insert } from orama/orama import { pluginSecureProxy } from orama/plugin-secure-proxy const secureProxy await pluginSecureProxy({ apiKey: my-api-key, defaultProperty: embeddings, models: { chat: openai/gpt-4o-mini } }) const db await create({ schema: { name: string }, plugins: [secureProxy] }) insert(db, { name: John Doe }) insert(db, { name: Jane Doe }) // 创建类 ChatGPT 的会话体验 const session new AnswerSession(db, { systemPrompt: 你将会获得一个名字作为上下文请提供问候信息, events: { onStateChange: console.log, } }) const response await session.ask({ term: john }) console.log(response) // 输出: Hello, John Doe! How are you doing?8. 性能优化与资源管理8.1 搜索性能优化// 优化搜索参数 const optimizedSearch async (db, query, options {}) { const defaultOptions { term: query, limit: options.limit || 20, offset: options.offset || 0, properties: options.properties || [title, content, description], boost: options.boost || { title: 2, // 标题权重更高 content: 1 }, tolerance: options.tolerance || 1, // 拼写容错 exact: options.exact || false // 是否精确匹配 } return await search(db, { ...defaultOptions, ...options }) } // 使用优化后的搜索 const results await optimizedSearch(db, JavaScript 编程, { limit: 10, boost: { title: 3, content: 1 } })8.2 内存管理策略对于大型数据集需要合理管理内存使用class MemoryAwareSearch { constructor(maxDocuments 10000) { this.maxDocuments maxDocuments this.documentCount 0 this.db null } async initialize(schema) { this.db await create({ schema }) } async insertWithMemoryCheck(document) { if (this.documentCount this.maxDocuments) { await this.cleanupOldDocuments() } await insert(this.db, document) this.documentCount } async cleanupOldDocuments() { // 实现旧文档清理逻辑 // 可以根据时间戳、使用频率等策略清理 console.log(执行内存清理...) this.documentCount Math.floor(this.maxDocuments * 0.7) // 保留 70% 文档 } }9. 实际应用案例9.1 文档搜索系统构建一个企业文档搜索系统// documentSearchSystem.js import { create, insert, search } from orama/orama class DocumentSearchSystem { constructor() { this.db null } async initialize() { this.db await create({ schema: { id: string, title: string, content: string, author: string, department: string, tags: string[], createdDate: string, lastModified: string, importance: number, accessLevel: string } }) } async indexDocument(document) { // 文档预处理 const processedDoc { ...document, createdDate: document.createdDate || new Date().toISOString(), lastModified: new Date().toISOString() } return await insert(this.db, processedDoc) } async searchDocuments(query, filters {}) { const searchParams { term: query, properties: [title, content, tags], boost: { title: 3, content: 1, tags: 2 } } // 应用过滤器 if (filters.department) { searchParams.where { department: { eq: filters.department } } } if (filters.accessLevel) { searchParams.where { ...searchParams.where, accessLevel: { eq: filters.accessLevel } } } const results await search(this.db, searchParams) // 结果后处理 return this.rankResults(results, filters) } rankResults(results, filters) { // 根据重要性、时间等因素重新排序 return results.hits.sort((a, b) { let scoreA a.score let scoreB b.score // 重要性加权 scoreA * (1 (a.document.importance || 0) * 0.1) scoreB * (1 (b.document.importance || 0) * 0.1) return scoreB - scoreA }) } } // 使用示例 const docSystem new DocumentSearchSystem() await docSystem.initialize() // 索引示例文档 await docSystem.indexDocument({ id: doc-001, title: 公司信息安全政策, content: 本文档规定了公司信息安全的相关政策和流程..., author: 安全部门, department: security, tags: [安全, 政策, 合规], importance: 5, accessLevel: internal })9.2 电子商务产品搜索构建电商网站的产品搜索功能// ecommerceSearch.js class EcommerceSearch { async initialize() { this.db await create({ schema: { productId: string, name: string, description: string, category: string, price: number, brand: string, tags: string[], inStock: boolean, rating: number, reviewCount: number } }) } async searchProducts(query, filters {}) { const searchParams { term: query, properties: [name, description, brand, tags], boost: { name: 4, brand: 3, tags: 2, description: 1 } } // 价格范围过滤 if (filters.minPrice ! undefined || filters.maxPrice ! undefined) { searchParams.where { price: { ...(filters.minPrice ! undefined { gte: filters.minPrice }), ...(filters.maxPrice ! undefined { lte: filters.maxPrice }) } } } // 类别过滤 if (filters.category) { searchParams.where { ...searchParams.where, category: { eq: filters.category } } } // 库存过滤 if (filters.inStockOnly) { searchParams.where { ...searchParams.where, inStock: { eq: true } } } const results await search(this.db, searchParams) return this.sortResults(results, filters.sortBy) } sortResults(results, sortBy relevance) { const hits [...results.hits] switch (sortBy) { case price-low: return hits.sort((a, b) a.document.price - b.document.price) case price-high: return hits.sort((a, b) b.document.price - a.document.price) case rating: return hits.sort((a, b) b.document.rating - a.document.rating) default: // relevance return hits.sort((a, b) b.score - a.score) } } }10. 常见问题与解决方案10.1 性能问题排查问题现象可能原因解决方案搜索速度慢数据量过大或搜索条件复杂优化 schema、使用索引、限制搜索范围内存占用高文档数量过多或向量维度太大分批处理、清理旧数据、优化向量维度搜索结果不准确权重配置不合理或数据质量差调整 boost 参数、改善数据质量10.2 功能问题排查问题现象可能原因解决方案向量搜索不工作向量维度不匹配或相似度阈值过高检查向量维度配置、调整相似度阈值插件加载失败版本不兼容或配置错误检查插件版本、验证配置参数批量插入失败数据格式错误或内存不足验证数据格式、分批处理10.3 部署问题排查问题现象可能原因解决方案浏览器中无法运行浏览器不支持 ES 模块使用构建工具转换或使用传统脚本方式Node.js 模块错误模块导入方式不正确检查 package.json 类型字段或使用 .mjs 扩展名API 服务无法启动端口被占用或依赖缺失更换端口、检查依赖安装11. 最佳实践建议11.1 Schema 设计最佳实践设计 schema 时考虑搜索需求// 良好的 schema 设计示例 const optimalSchema { // 标识字段 id: string, // 主要搜索字段 title: string, content: string, // 分类和过滤字段 category: string, tags: string[], // 数值字段用于范围和排序 price: number, rating: number, // 时间字段 createdAt: string, updatedAt: string, // 向量字段如果需要语义搜索 embedding: vector[384] }11.2 数据预处理建议在索引前对数据进行清洗和标准化class DataPreprocessor { static normalizeText(text) { return text .toLowerCase() .replace(/[^\w\s]/g, ) // 移除标点 .replace(/\s/g, ) // 合并空格 .trim() } static extractKeywords(text) { // 简单的关键词提取逻辑 const words text.split( ) const stopWords new Set([the, a, an, in, on, at]) return words.filter(word word.length 2 !stopWords.has(word) ) } static prepareDocument(doc) { return { ...doc, title: this.normalizeText(doc.title), content: this.normalizeText(doc.content), tags: doc.tags.map(tag this.normalizeText(tag)) } } }11.3 生产环境部署建议监控和日志添加搜索性能监控和错误日志备份策略定期备份搜索索引数据版本控制对 schema 变更进行版本管理安全考虑对用户输入进行验证和清理性能测试在生产环境部署前进行压力测试Orama 作为一个轻量级但功能强大的搜索解决方案在正确的使用场景下能够提供出色的搜索体验。通过本文介绍的各种技术和最佳实践你可以根据具体需求构建高效的搜索系统。
Orama轻量级JavaScript搜索引擎:全文/向量/混合搜索与RAG应用指南
发布时间:2026/7/17 4:15:42
Orama 是一个开源的 JavaScript 搜索引擎支持在浏览器、服务器和边缘网络中运行完整的搜索和 RAG 流程。它最吸引人的特点是体积极小小于 2KB却提供了全文搜索、向量搜索和混合搜索等企业级功能。对于需要在本地或边缘环境部署搜索服务的开发者来说这是一个值得关注的选择。这个项目由 oramasearch 团队维护目前 GitHub 上有 10.5k 星支持 TypeScript 开发采用 Apache 2.0 开源协议。它最大的优势是轻量级和跨平台部署能力可以在浏览器端直接运行不需要依赖外部搜索服务。本文将详细介绍 Orama 的核心功能、部署方式、API 使用方法和实际应用场景。我们会从环境准备开始逐步演示如何创建搜索数据库、插入数据、执行各种搜索操作最后还会介绍如何通过插件扩展功能构建完整的 RAG 应用。1. 核心能力速览能力项说明项目类型JavaScript/TypeScript 搜索引擎库开源协议Apache 2.0核心功能全文搜索、向量搜索、混合搜索、RAG 流程部署环境浏览器、Node.js、Deno、边缘网络包体积小于 2KB核心功能搜索模式全文搜索、向量搜索、混合搜索数据类型支持 10 种数据类型包括字符串、数字、布尔值、枚举、地理点、数组和向量插件系统支持功能扩展官方提供 embeddings、安全代理等插件API 支持完整的 JavaScript/TypeScript API适合场景本地搜索应用、边缘计算、浏览器内搜索、RAG 系统2. 适用场景与使用边界Orama 最适合需要轻量级、可嵌入搜索功能的场景。比如在静态网站中实现客户端搜索避免依赖外部搜索服务或者在边缘计算环境中部署搜索功能减少网络延迟。对于中小型数据集的本地搜索需求Orama 能够提供很好的性能表现。在 RAG检索增强生成应用场景中Orama 可以作为检索组件与 LLM 结合构建智能问答系统。它的混合搜索能力能够同时利用关键词匹配和语义相似度提高检索准确性。需要注意的是Orama 主要针对中小规模数据集优化。对于亿级数据量的搜索需求可能需要考虑分布式搜索方案。此外虽然 Orama 支持向量搜索但向量生成需要依赖外部模型或插件在实际部署时要考虑 embeddings 生成的计算成本。在合规性方面使用 Orama 处理用户数据时需要注意数据隐私保护。特别是在浏览器端部署时要确保敏感数据不会泄露。对于商业应用建议仔细阅读 Apache 2.0 协议的具体条款。3. 环境准备与前置条件Orama 的环境要求相对简单主要取决于你的部署目标环境。3.1 浏览器环境在现代浏览器中可以直接通过 ES 模块导入使用支持 Chrome 90、Firefox 88、Safari 14 等主流浏览器。不需要额外的构建工具但建议使用支持 ES 模块的现代浏览器版本。3.2 Node.js 环境推荐使用 Node.js 16 版本确保支持 ES 模块。可以通过 npm、yarn、pnpm 或 bun 安装# 使用 npm npm install orama/orama # 使用 yarn yarn add orama/orama # 使用 pnpm pnpm add orama/orama # 使用 bun bun add orama/orama3.3 Deno 环境在 Deno 中可以直接通过 npm 说明符或 CDN 导入// 使用 npm 说明符 import { create, search, insert } from npm:orama/orama // 或使用 CDN import { create, insert, search } from https://cdn.jsdelivr.net/npm/orama/oramalatest/esm3.4 硬件要求由于 Orama 本身很轻量硬件要求主要取决于数据量大小。对于中小型数据集数万条记录在普通配置的电脑或服务器上都能良好运行。向量搜索功能需要额外的计算资源具体取决于向量维度和数据量。4. 安装部署与启动方式Orama 的安装和启动方式非常灵活下面介绍几种常见的部署方案。4.1 浏览器直接使用最简单的使用方式是在 HTML 中直接导入!DOCTYPE html html head titleOrama 搜索示例/title /head body script typemodule import { create, insert, search } from https://cdn.jsdelivr.net/npm/orama/oramalatest/esm // 立即创建搜索数据库 const db await create({ schema: { title: string, content: string } }) // 插入示例数据 await insert(db, { title: 欢迎使用 Orama, content: 这是一个轻量级搜索引擎示例 }) console.log(Orama 已初始化完成) /script /body /html4.2 Node.js 项目集成在 Node.js 项目中首先安装依赖然后创建搜索服务// searchService.js import { create, insert, search } from orama/orama class SearchService { constructor() { this.db null } async initialize() { this.db await create({ schema: { id: string, title: string, description: string, content: string, category: string, tags: string[], rating: number, embedding: vector[384] } }) } async addDocument(doc) { return await insert(this.db, doc) } async searchDocuments(query, options {}) { return await search(this.db, { term: query, ...options }) } } // 使用示例 const searchService new SearchService() await searchService.initialize() // 添加文档 await searchService.addDocument({ id: doc1, title: JavaScript 教程, description: 全面的 JavaScript 学习指南, content: JavaScript 是一种动态编程语言..., category: programming, tags: [javascript, web, frontend], rating: 4.5 }) // 执行搜索 const results await searchService.searchDocuments(JavaScript 学习) console.log(results)4.3 Express.js API 服务可以基于 Express.js 构建搜索 API 服务// server.js import express from express import { create, insert, search } from orama/orama const app express() app.use(express.json()) let db null // 初始化搜索数据库 async function initializeSearch() { db await create({ schema: { id: string, title: string, content: string, type: string } }) // 预加载示例数据 await insert(db, { id: 1, title: Orama 搜索指南, content: 学习如何使用 Orama 构建搜索功能, type: guide }) } // 搜索 API app.post(/api/search, async (req, res) { try { const { query, limit 10, offset 0 } req.body const results await search(db, { term: query, limit, offset }) res.json({ success: true, data: results }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } }) // 添加文档 API app.post(/api/documents, async (req, res) { try { const document req.body const result await insert(db, document) res.json({ success: true, data: result }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } }) // 启动服务 initializeSearch().then(() { app.listen(3000, () { console.log(搜索服务运行在 http://localhost:3000) }) })5. 功能测试与效果验证5.1 基础全文搜索测试首先测试最基本的全文搜索功能import { create, insert, search } from orama/orama // 创建测试数据库 const db await create({ schema: { name: string, description: string, price: number, category: string } }) // 插入测试数据 const products [ { name: 无线耳机, description: 高品质无线蓝牙耳机降噪功能, price: 299, category: electronics }, { name: 机械键盘, description: 青轴机械键盘RGB 背光, price: 450, category: electronics }, { name: 编程书籍, description: JavaScript 高级程序设计, price: 89, category: books } ] for (const product of products) { await insert(db, product) } // 测试搜索功能 const testCases [ { query: 耳机, expected: 无线耳机 }, { query: 键盘, expected: 机械键盘 }, { query: 编程, expected: 编程书籍 }, { query: 降噪, expected: 无线耳机 } ] for (const testCase of testCases) { const results await search(db, { term: testCase.query, properties: [name, description] }) console.log(搜索 ${testCase.query}:) console.log(- 找到 ${results.count} 个结果) console.log(- 最相关: ${results.hits[0]?.document.name}) console.log(- 预期: ${testCase.expected}) console.log(---) }5.2 向量搜索测试测试 Orama 的向量搜索能力import { create, insertMultiple, search } from orama/orama // 创建支持向量搜索的数据库 const db await create({ schema: { title: string, embedding: vector[5] // 5维向量示例 } }) // 插入带向量的数据 await insertMultiple(db, [ { title: 人工智能研究, embedding: [0.9, 0.8, 0.1, 0.2, 0.3] }, { title: 机器学习算法, embedding: [0.8, 0.7, 0.2, 0.3, 0.4] }, { title: 网页开发教程, embedding: [0.1, 0.2, 0.9, 0.8, 0.7] } ]) // 向量搜索测试 const vectorResults await search(db, { mode: vector, vector: { value: [0.85, 0.75, 0.15, 0.25, 0.35], // 搜索向量 property: embedding }, similarity: 0.7, // 相似度阈值 limit: 5 }) console.log(向量搜索结果:) vectorResults.hits.forEach((hit, index) { console.log(${index 1}. ${hit.document.title} (相似度: ${hit.score.toFixed(3)})) })5.3 混合搜索测试测试结合关键词和向量搜索的混合模式// 混合搜索示例 const hybridResults await search(db, { term: 人工智能, // 关键词 mode: hybrid, // 混合模式 vector: { value: [0.85, 0.75, 0.15, 0.25, 0.35], property: embedding }, similarity: 0.6, limit: 10 }) console.log(混合搜索结果:) hybridResults.hits.forEach((hit, index) { console.log(${index 1}. ${hit.document.title} (分数: ${hit.score.toFixed(3)})) })6. 接口 API 与批量任务6.1 完整的 REST API 实现下面实现一个功能完整的搜索 API 服务// advancedSearchService.js import express from express import { create, insert, search, remove } from orama/orama const app express() app.use(express.json()) class AdvancedSearchService { constructor() { this.db null this.app express() this.setupMiddleware() this.setupRoutes() } setupMiddleware() { this.app.use(express.json()) this.app.use((req, res, next) { console.log(${new Date().toISOString()} - ${req.method} ${req.path}) next() }) } setupRoutes() { // 健康检查 this.app.get(/health, (req, res) { res.json({ status: healthy, timestamp: new Date().toISOString() }) }) // 搜索接口 this.app.post(/search, this.handleSearch.bind(this)) // 批量插入接口 this.app.post(/documents/batch, this.handleBatchInsert.bind(this)) // 文档管理接口 this.app.get(/documents/:id, this.handleGetDocument.bind(this)) this.app.delete(/documents/:id, this.handleDeleteDocument.bind(this)) // 统计信息 this.app.get(/stats, this.handleGetStats.bind(this)) } async initialize(schema) { this.db await create({ schema }) console.log(搜索数据库初始化完成) } async handleSearch(req, res) { try { const { query, mode fulltext, properties, limit 20, offset 0, similarity, vector } req.body const searchParams { term: query, mode, limit, offset } if (properties) searchParams.properties properties if (similarity) searchParams.similarity similarity if (vector) searchParams.vector vector const results await search(this.db, searchParams) res.json({ success: true, data: { query, total: results.count, hits: results.hits, elapsed: results.elapsed } }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } } async handleBatchInsert(req, res) { try { const { documents } req.body if (!Array.isArray(documents)) { return res.status(400).json({ success: false, error: documents 必须是数组 }) } const results [] for (const doc of documents) { try { const result await insert(this.db, doc) results.push({ success: true, id: result.id }) } catch (error) { results.push({ success: false, error: error.message }) } } res.json({ success: true, data: { total: documents.length, processed: results.length, results } }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } } async handleGetDocument(req, res) { // 简化实现实际需要维护文档索引 res.json({ message: 文档获取功能待实现 }) } async handleDeleteDocument(req, res) { try { const { id } req.params await remove(this.db, id) res.json({ success: true, message: 文档删除成功 }) } catch (error) { res.status(500).json({ success: false, error: error.message }) } } async handleGetStats(req, res) { // 返回搜索统计信息 res.json({ success: true, data: { initialized: !!this.db, timestamp: new Date().toISOString() } }) } start(port 3000) { return this.app.listen(port, () { console.log(搜索服务运行在端口 ${port}) }) } } // 使用示例 const searchService new AdvancedSearchService() // 初始化数据库 schema await searchService.initialize({ schema: { id: string, title: string, content: string, tags: string[], category: string, importance: number, embedding: vector[1536], createdAt: string } }) // 启动服务 searchService.start(3000)6.2 批量任务处理对于大量数据的处理需要实现批量任务队列// batchProcessor.js import { create, insertMultiple } from orama/orama class BatchProcessor { constructor(batchSize 100) { this.batchSize batchSize this.queue [] this.processing false } async addDocuments(documents) { this.queue.push(...documents) if (!this.processing) { this.processQueue() } } async processQueue() { this.processing true while (this.queue.length 0) { const batch this.queue.splice(0, this.batchSize) try { await this.processBatch(batch) console.log(处理了 ${batch.length} 个文档剩余 ${this.queue.length} 个) } catch (error) { console.error(批量处理失败:, error) // 重试逻辑可以在这里实现 } // 添加延迟避免过度占用资源 await this.delay(100) } this.processing false } async processBatch(documents) { // 实际处理逻辑 return await insertMultiple(this.db, documents) } delay(ms) { return new Promise(resolve setTimeout(resolve, ms)) } } // 使用批量处理器 const processor new BatchProcessor(50) // 模拟添加大量文档 const mockDocuments Array.from({ length: 1000 }, (_, i) ({ id: doc-${i}, title: 文档 ${i}, content: 这是第 ${i} 个文档的内容, category: i % 2 0 ? tech : general })) processor.addDocuments(mockDocuments)7. 插件系统与功能扩展Orama 的插件系统是其强大功能的关键下面介绍几个重要插件的使用方法。7.1 Embeddings 插件使用官方 embeddings 插件自动生成文本向量import { create } from orama/orama import { pluginEmbeddings } from orama/plugin-embeddings // 初始化 embeddings 插件 const plugin await pluginEmbeddings({ embeddings: { defaultProperty: embeddings, onInsert: { generate: true, properties: [description], verbose: true, } } }) // 创建支持自动 embeddings 的数据库 const db await create({ schema: { description: string, embeddings: vector[512] // 插件生成 512 维向量 }, plugins: [plugin] }) // 插入数据时会自动生成 embeddings await insert(db, { description: Classroom Headphones Bulk 5 Pack, Student On Ear Color Varieties }) await insert(db, { description: Kids Wired Headphones for School Students K-12 }) // 搜索时自动使用 embeddings const searchResults await search(db, { term: Headphones for 12th grade students, mode: vector, similarity: 0.75, })7.2 安全代理插件安全地调用 OpenAI 等外部 APIimport { create, insert } from orama/orama import { pluginSecureProxy } from orama/plugin-secure-proxy const secureProxy await pluginSecureProxy({ apiKey: my-api-key, defaultProperty: embeddings, models: { chat: openai/gpt-4o-mini } }) const db await create({ schema: { name: string }, plugins: [secureProxy] }) insert(db, { name: John Doe }) insert(db, { name: Jane Doe }) // 创建类 ChatGPT 的会话体验 const session new AnswerSession(db, { systemPrompt: 你将会获得一个名字作为上下文请提供问候信息, events: { onStateChange: console.log, } }) const response await session.ask({ term: john }) console.log(response) // 输出: Hello, John Doe! How are you doing?8. 性能优化与资源管理8.1 搜索性能优化// 优化搜索参数 const optimizedSearch async (db, query, options {}) { const defaultOptions { term: query, limit: options.limit || 20, offset: options.offset || 0, properties: options.properties || [title, content, description], boost: options.boost || { title: 2, // 标题权重更高 content: 1 }, tolerance: options.tolerance || 1, // 拼写容错 exact: options.exact || false // 是否精确匹配 } return await search(db, { ...defaultOptions, ...options }) } // 使用优化后的搜索 const results await optimizedSearch(db, JavaScript 编程, { limit: 10, boost: { title: 3, content: 1 } })8.2 内存管理策略对于大型数据集需要合理管理内存使用class MemoryAwareSearch { constructor(maxDocuments 10000) { this.maxDocuments maxDocuments this.documentCount 0 this.db null } async initialize(schema) { this.db await create({ schema }) } async insertWithMemoryCheck(document) { if (this.documentCount this.maxDocuments) { await this.cleanupOldDocuments() } await insert(this.db, document) this.documentCount } async cleanupOldDocuments() { // 实现旧文档清理逻辑 // 可以根据时间戳、使用频率等策略清理 console.log(执行内存清理...) this.documentCount Math.floor(this.maxDocuments * 0.7) // 保留 70% 文档 } }9. 实际应用案例9.1 文档搜索系统构建一个企业文档搜索系统// documentSearchSystem.js import { create, insert, search } from orama/orama class DocumentSearchSystem { constructor() { this.db null } async initialize() { this.db await create({ schema: { id: string, title: string, content: string, author: string, department: string, tags: string[], createdDate: string, lastModified: string, importance: number, accessLevel: string } }) } async indexDocument(document) { // 文档预处理 const processedDoc { ...document, createdDate: document.createdDate || new Date().toISOString(), lastModified: new Date().toISOString() } return await insert(this.db, processedDoc) } async searchDocuments(query, filters {}) { const searchParams { term: query, properties: [title, content, tags], boost: { title: 3, content: 1, tags: 2 } } // 应用过滤器 if (filters.department) { searchParams.where { department: { eq: filters.department } } } if (filters.accessLevel) { searchParams.where { ...searchParams.where, accessLevel: { eq: filters.accessLevel } } } const results await search(this.db, searchParams) // 结果后处理 return this.rankResults(results, filters) } rankResults(results, filters) { // 根据重要性、时间等因素重新排序 return results.hits.sort((a, b) { let scoreA a.score let scoreB b.score // 重要性加权 scoreA * (1 (a.document.importance || 0) * 0.1) scoreB * (1 (b.document.importance || 0) * 0.1) return scoreB - scoreA }) } } // 使用示例 const docSystem new DocumentSearchSystem() await docSystem.initialize() // 索引示例文档 await docSystem.indexDocument({ id: doc-001, title: 公司信息安全政策, content: 本文档规定了公司信息安全的相关政策和流程..., author: 安全部门, department: security, tags: [安全, 政策, 合规], importance: 5, accessLevel: internal })9.2 电子商务产品搜索构建电商网站的产品搜索功能// ecommerceSearch.js class EcommerceSearch { async initialize() { this.db await create({ schema: { productId: string, name: string, description: string, category: string, price: number, brand: string, tags: string[], inStock: boolean, rating: number, reviewCount: number } }) } async searchProducts(query, filters {}) { const searchParams { term: query, properties: [name, description, brand, tags], boost: { name: 4, brand: 3, tags: 2, description: 1 } } // 价格范围过滤 if (filters.minPrice ! undefined || filters.maxPrice ! undefined) { searchParams.where { price: { ...(filters.minPrice ! undefined { gte: filters.minPrice }), ...(filters.maxPrice ! undefined { lte: filters.maxPrice }) } } } // 类别过滤 if (filters.category) { searchParams.where { ...searchParams.where, category: { eq: filters.category } } } // 库存过滤 if (filters.inStockOnly) { searchParams.where { ...searchParams.where, inStock: { eq: true } } } const results await search(this.db, searchParams) return this.sortResults(results, filters.sortBy) } sortResults(results, sortBy relevance) { const hits [...results.hits] switch (sortBy) { case price-low: return hits.sort((a, b) a.document.price - b.document.price) case price-high: return hits.sort((a, b) b.document.price - a.document.price) case rating: return hits.sort((a, b) b.document.rating - a.document.rating) default: // relevance return hits.sort((a, b) b.score - a.score) } } }10. 常见问题与解决方案10.1 性能问题排查问题现象可能原因解决方案搜索速度慢数据量过大或搜索条件复杂优化 schema、使用索引、限制搜索范围内存占用高文档数量过多或向量维度太大分批处理、清理旧数据、优化向量维度搜索结果不准确权重配置不合理或数据质量差调整 boost 参数、改善数据质量10.2 功能问题排查问题现象可能原因解决方案向量搜索不工作向量维度不匹配或相似度阈值过高检查向量维度配置、调整相似度阈值插件加载失败版本不兼容或配置错误检查插件版本、验证配置参数批量插入失败数据格式错误或内存不足验证数据格式、分批处理10.3 部署问题排查问题现象可能原因解决方案浏览器中无法运行浏览器不支持 ES 模块使用构建工具转换或使用传统脚本方式Node.js 模块错误模块导入方式不正确检查 package.json 类型字段或使用 .mjs 扩展名API 服务无法启动端口被占用或依赖缺失更换端口、检查依赖安装11. 最佳实践建议11.1 Schema 设计最佳实践设计 schema 时考虑搜索需求// 良好的 schema 设计示例 const optimalSchema { // 标识字段 id: string, // 主要搜索字段 title: string, content: string, // 分类和过滤字段 category: string, tags: string[], // 数值字段用于范围和排序 price: number, rating: number, // 时间字段 createdAt: string, updatedAt: string, // 向量字段如果需要语义搜索 embedding: vector[384] }11.2 数据预处理建议在索引前对数据进行清洗和标准化class DataPreprocessor { static normalizeText(text) { return text .toLowerCase() .replace(/[^\w\s]/g, ) // 移除标点 .replace(/\s/g, ) // 合并空格 .trim() } static extractKeywords(text) { // 简单的关键词提取逻辑 const words text.split( ) const stopWords new Set([the, a, an, in, on, at]) return words.filter(word word.length 2 !stopWords.has(word) ) } static prepareDocument(doc) { return { ...doc, title: this.normalizeText(doc.title), content: this.normalizeText(doc.content), tags: doc.tags.map(tag this.normalizeText(tag)) } } }11.3 生产环境部署建议监控和日志添加搜索性能监控和错误日志备份策略定期备份搜索索引数据版本控制对 schema 变更进行版本管理安全考虑对用户输入进行验证和清理性能测试在生产环境部署前进行压力测试Orama 作为一个轻量级但功能强大的搜索解决方案在正确的使用场景下能够提供出色的搜索体验。通过本文介绍的各种技术和最佳实践你可以根据具体需求构建高效的搜索系统。