# cloud: Introduction to Orama Cloud URL: /docs/cloud Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/index.mdx Run Orama at scale. Managed. *** title: Introduction to Orama Cloud description: Run Orama at scale. Managed. ----------------------------------------- Orama Cloud is a fully managed service that allows you to run Orama at scale. It is designed to be easy to use, with a simple API and a web interface that makes it easy to manage your Orama collections. ## History and Context When we started Orama in 2022, it was a simple JavaScript-based full-text search engine that could run in the browser. That was Orama 1.0. Orama 2.0 was a great step forward, allowing Orama to run in a CDN (Fastly, CloudFlare, etc.), allowing for a much faster and more efficient search experience. It was also the first version of Orama that could be used in production. Orama 3.0 introduced answer engine and RAG capabilities, making it a valid, modern, and powerful alternative to other engines that only focus on search. The current version of Orama, **Orama 4.0**, is a complete rewrite of the engine in Rust, which we called OramaCore. It's still open source, and now powers the cloud version of Orama. In a single service, you will find: * A fully-fledged full-text search engine * A scalable vector database * A high-performance answer engine * A powerful RAG engine * A JavaScript runtime that allows your to build agents and custom business logic And much more. ## Concepts Before you get started with Orama Cloud, it's important to understand some key concepts: * [Projects](#projects) * [Data sources](#data-sources) ### Projects A project is a set of [**data sources**](#data-sources) that you want to use for RAG, search, or any other method of retrieval. Each project has its own set of data sources, and depending on your plan, you can create as many projects as you need. > **Why do I need different data sources? And why not just use a single collection for all of my data?** That is a fair question, and the answer is simple: imagine you're building a support agent for your company. You will have some data coming from Slack, some from Notion, some from your documentation, and so on. You could put everything in a single collection, but that could complicate things when it comes to updating, deleting, or adding new data. For example, you may want to scrape your documentation every day, so you'd have to remove the old data and add the new data. But what if you also want to keep the data from Slack and Notion? You'd have to re-import all of that data every time you update your documentation, which would be a pain. Instead, you can put data that will be updated frequently in a single [data source](#data-sources) (let's call it `docs`), and put data that follows a different update schedule in another data source (let's call it `slack`). This way, you can update the `docs` data source every day, and the `slack` data source every hour, for example. You can also add new data sources as needed, without having to worry about updating everything at once. When you create a collection, you can choose its language (English, Spanish, etc.), and the embedding model you want to use. You can always change these settings in the future, but remember that they will be shared across all of the data sources in that collection. ### Data Sources A data source identifies a specific set of documents, ideally coming from a single origin. Continuing with the example above, you could have a data source for your documentation, one for your Slack messages, and one for your Notion pages. Every data source is segregated from the others, so you can update, delete, or add new data without affecting the data in the other data sources. At retrieval time (be it search, RAG, or any other method of retrieval), you can choose to use a single data source, multiple sources, or all of them. This flexibility allows you to have a single collection with multiple data sources and use them as needed for different purposes. # cloud: MCP Server URL: /docs/cloud/mcp-server Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/mcp-server.mdx Using the the Orama-provided MCP Server to interact with your data *** title: MCP Server description: Using the the Orama-provided MCP Server to interact with your data ------------------------------------------------------------------------------- When [creating a new project](/docs/cloud/projects/create), Orama Cloud will automatically expose and MCP server for you. That way, you'll be able to interact with your data from different AI interfaces, including ChatGPT, Cursor, and many other. ## What is the MCP Server? An MCP (Model Context Protocol) server is a standardized way for AI assistants like ChatGPT, Claude (and many others) to securely connect to and interact with external tools, data sources, and services. It acts as a bridge that allows the AI to access things like databases, APIs, file systems, or other applications while maintaining security and proper permissions. The MCP server handles requests from the AI, processes them, and returns the appropriate responses. This enables the AI to perform tasks such as retrieving information, executing commands, or integrating with other software in a controlled manner. ## Orama MCP Server Every Orama Cloud project comes with an MCP server that is automatically configured to interact with your project's data. You can find the MCP server URL in your project's home page: ![Orama MCP Server](/docs/cloud/projects/mcp.png) By copying the MCP Server URL, you can easily connect your Orama Cloud project to various AI assistants that support the MCP protocol. This allows you to leverage the power of AI to interact with your data in a more intuitive and efficient way. For example, you can connect Anthropic's Claude to your Orama Cloud project: ![Claude MCP Setup](/docs/cloud/projects/claude.png) This way, Claude can decide to use your Orama Cloud data when answering questions, providing more accurate and contextually relevant responses. # cloud: What is Orama Cloud? URL: /docs/cloud/what-is-orama-cloud Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/what-is-orama-cloud.mdx Learn about the Orama Cloud context server. *** title: What is Orama Cloud? description: Learn about the Orama Cloud context server. -------------------------------------------------------- Orama Cloud is the cloud version of Orama, based on the [OramaCore](https://github.com/oramasearch/oramacore) context server. ## What is a Context Server? In the era of AI, context servers play a crucial role in providing the necessary context for AI models to make accurate predictions, decisions, and content generation. They act as a bridge between the AI model and the data it needs to process, ensuring that the model has access to the most relevant information at any given time. A context server is designed to efficiently produce the minimal amount of context for an LLM to produce a correct enough answer and no more. Too much context and the LLM becomes slower and less accurate. Too little and the LLM produces a poor response. The context server works in collaboration with the LLM during chains of reasoning and is available to produce relevant and curated context for the problem space. We started building Orama with the idea of making information retrieval more efficient, scalable, and accessible to everyone. The Orama Cloud context server is the natural progression of this idea, making it possible for everyone to benefit from the power of context servers without the need for complex infrastructure or technical expertise. ## Components Orama Cloud is a distributed context server consisting of multiple services that work together to provide a fast, scalable, and high-quality data retrieval and generation service. A single Orama Cloud node consists of: 1. **Full-Text Search Engine**. A powerful full-text search engine built from scratch, optimized for speed and accuracy, as well as typo tolerance, advanced filtering, sorting, and more. 2. **Vector Search Engine**. A fast and scalable vector search engine built from the ground up to work in combination with the full-text search engine to provide a more comprehensive and accurate context for an LLM. 3. **Embedding Engine**. Sub-millisecond embedding-generation engine that can generate high-quality embeddings for your data. It supports various embedding models and can be customized to meet your specific needs. 4. **Inference Engine**. A fast inference engine that performs decision-making, reasoning, and many other tasks as well as generating text responses based on your data and user queries. This is all packed into a single context server where all the components work together within the same process, ensuring minimal latency and maximum efficiency. ## On-Premise Support We offer on-premise support for Orama Cloud, allowing you to deploy the context server on your own infrastructure. This gives you full control over your data and ensures compliance with your organization's security and privacy policies. If you're interested in deploying Orama Cloud on your own infrastructure, please contact us at [info@orama.com](mailto:info@orama.com) for more information. # orama-js: Answer Engine URL: /docs/orama-js/answer-engine Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/answer-engine.mdx Learn how to use Orama as an answer engine to perform ChatGPT-like experiences on your website. *** title: Answer Engine description: Learn how to use Orama as an answer engine to perform ChatGPT-like experiences on your website. ------------------------------------------------------------------------------------------------------------ With Orama 3.0, we introduced a new feature called **AnswerSession** that allows you to perform ChatGPT-like experiences on your website. It uses a free feature from Orama Cloud called [**Secure Proxy**](https://orama.com/blog/announcing-the-orama-secure-ai-proxy) to proxy your queries to the OpenAI API, so you don't need to worry about sharing the API key on the client-side. The APIs are designed to be as close as possible to the **Orama Cloud** APIs, so you can easily migrate your projects from **Orama Cloud** to **Orama Open Source** and vice-versa. ## Getting Started Orama implements a full-text, vector, and hybrid search engine as well as a complete **RAG** (Retrieval-Augmented Generation) pipeline to generate answers from your documents. All with minimum dependencies and configuration, so you can focus on building your project. To get started, you will need to create an account on [Orama Cloud](https://app.orama.com) and generate an API key from the **"Secure Proxy"** section. Then, you can use the API key to create an **AnswerSession** and start generating answers. Follow [this guide](/cloud/orama-ai/orama-secure-proxy) to get your API key for free and start using the **AnswerSession** APIs! ## AnswerSession Creating an answer session is as simple as: ```js copy import { create, insert } from "@orama/orama"; import { pluginSecureProxy } from "@orama/plugin-secure-proxy"; const secureProxy = await pluginSecureProxy({ apiKey: "my-api-key", defaultProperty: "embeddings", models: { embeddings: "openai/text-embedding-3-small", chat: "openai/gpt-4o-mini" } }) const db = await create({ schema: { name: 'string' } as const, plugins: [secureProxy] }) await insert(db, { name: "John Doe" }) await insert(db, { name: "Michele Riva" }) const session = new AnswerSession(db, { // Customize the prompt for the system systemPrompt: 'You will get a name as context, please provide a greeting message', events: { onStateChange: console.log } }) const response = await session.ask({ term: 'john', }) console.log(response) // Hello, John Doe! How are you doing? ``` The `onStateChange: console.log` event will log the state of the session, allowing you to reactively update your UI based on the current state of the session. In the example above, the `onStateChange` will be triggered for every new object in the following array (the `state`): ```js [ // As soon as you call the `.ask` method, the state will be populated as follows: { interactionId: "cm2anntif000008l84lvqfrvc", // Unique interaction ID for the session aborted: false, // If the session was aborted loading: true, // If the session is loading query: "john", // The query that was sent to the API response: "", // The response from the API, which is empty until the API responds sources: null, // The sources used to generate the response error: false, // If there was an error errorMessage: null, // The error message, if any }, // Then, Orama will perform search and push the sources to the state: { interactionId: "cm2anntif000008l84lvqfrvc", aborted: false, loading: false, query: "john", response: "", sources: { // The sources used to generate the response, in the same format as the search result from Orama count: 1, elapsed: { raw: 0.123, formatted: "100μs" }, hits: [ { id: "1-19238", score: 0.8, document: { name: "John Doe" } } ] }, error: false, errorMessage: null, }, // Then, Orama will update this message with incoming chunks from OpenAI (via the secure proxy): { interactionId: "cm2anntif000008l84lvqfrvc", aborted: false, loading: false, query: "john", response: "Hello, John Doe!", sources: { count: 1, elapsed: { raw: 0.123, formatted: "100μs" }, hits: [ { id: "1-19238", score: 0.8, document: { name: "John Doe" } } ] }, error: false, errorMessage: null, } ] ``` ## RAG implementation Orama provides a comprehensive toolkit for building high-performance RAG (Retrieval-Augmented Generation) pipelines tailored to your document processing needs. Below is a simple example demonstrating how to set up your application to leverage Orama's database capabilities and the [Secure Proxy Plugin](/cloud/orama-ai/orama-secure-proxy) for document-based answer generation. ```ts copy import { create, insert, search, AnswerSession } from "@orama/orama"; import { pluginSecureProxy } from "@orama/plugin-secure-proxy"; // Configuration options for better maintainability const CONFIG = { API_KEY: process.env.ORAMA_SECURE_PROXY_API_KEY, VECTOR_DIMENSIONS: 1536, }; // Sample documents const SAMPLE_DOCS = [ { description: "John Doe is a programmer, and he has 14 years." }, { description: "Mitch Smith is a programmer, and he has 32 years." }, ]; /** * Initialize the Orama database with secure proxy plugin * @returns {Promise} Configured database instance */ async function initializeDatabase() { const secureProxy = await pluginSecureProxy({ apiKey: CONFIG.API_KEY, embeddings: { model: "openai/text-embedding-ada-002", defaultProperty: "embeddings", onInsert: { generate: true, properties: ["description"], verbose: true, }, }, chat: { model: "openai/gpt-3.5-turbo", }, }); return create({ schema: { description: "string", embeddings: `vector[${CONFIG.VECTOR_DIMENSIONS}]`, }, plugins: [secureProxy], }); } /** * Populate the database with documents * @param {Object} db - Database instance * @param {Array} documents - Array of documents to insert */ async function populateDatabase(db, documents) { const insertPromises = documents.map((doc) => insert(db, { description: doc.description }) ); await Promise.all(insertPromises); } /** * Perform vector search and generate response * @param {Object} db - Database instance * @param {string} userPrompt - User's question * @returns {Promise} Generated response */ async function generateResponse(db, userPrompt) { try { const searchResults = await search(db, { mode: "vector", term: userPrompt, }); const formattedPrompt = `### Context: ${JSON.stringify( searchResults?.hits )} ### Prompt: ${userPrompt}`; const session = new AnswerSession(db, {}); return await session.ask({ term: formattedPrompt }); } catch (error) { console.error("Error generating response:", error); throw error; } } /** * Main execution function */ async function main() { try { // Initialize database const db = await initializeDatabase(); // Populate with sample data await populateDatabase(db, SAMPLE_DOCS); // Example query const userPrompt = "Who is John Doe?"; const response = await generateResponse(db, userPrompt); console.log("Response:", response); } catch (error) { console.error("Application error:", error); } } // Execute the application main(); ``` # orama-js: Introduction URL: /docs/orama-js Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/index.mdx A complete search engine and RAG pipeline in your browser, server or edge network with support for full-text, vector, and hybrid search in less than 2kb. *** title: Introduction description: A complete search engine and RAG pipeline in your browser, server or edge network with support for full-text, vector, and hybrid search in less than 2kb. ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- Orama is an open source, high performance full-text and vector search engine entirely written in TypeScript, with zero dependencies. ## Requirements A JavaScript runtime is the **only** requirement. Orama has been designed to work on any JS runtime and has no dependencies. ## Installation You can install Orama using any JavaScript package manager of your choice. ```bash npm install @orama/orama ``` Or import it directly in a browser module: ```html ``` ## Basic usage ```ts copy import { create, search, insert } from "@orama/orama"; // Create a new Orama instance const db = create({ schema: { name: "string", description: "string", price: "number", meta: { rating: "number", }, }, }); // Insert documents into the database insert(db, { name: "Wireless Headphones", description: "Experience immersive sound quality with these noise-cancelling wireless headphones.", price: 99.99, meta: { rating: 4.5, }, }); // Search for documents const searchResult = search(db, { term: "headphones", }); console.log(searchResult.hits.map((hit) => hit.document)); ``` For more information, check out the [Usage](/docs/orama-js/usage/create) section. ## CommonJS Imports Orama ships **ESM** modules by default. This allows us to move faster when providing new features and bug fixes, as well as using the `"exports"` field in `package.json` to provide a better developer experience. CommonJS imports are still supported, but we suggest you to migrate to ESM. ## TypeScript Set `moduleResolution` in the `compilerOptions` in your `tsconfig.json` to be either `Node16` or `NodeNext`. When importing types, always refer to the standard orama import: ```ts copy import type { Language } from "@orama/orama"; ``` # orama-js: Results Pinning (Merchandising) URL: /docs/orama-js/results-pinning Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/results-pinning.mdx Pin specific documents to a chosen position in the search results. *** title: Results Pinning (Merchandising) description: Pin specific documents to a chosen position in the search results. ------------------------------------------------------------------------------- Starting with Orama v3.1.16, you can pin specific documents to a chosen position in the search results. This feature is particularly useful for merchandising purposes, allowing you to highlight certain items or promotions. ## Creating a Pinning Rule To get started with results pinning, you need to create a pinning rule. A pinning rule consists of the following components: * `id`: A unique identifier for the pinning rule. * `conditions`: One or more conditions that determine when the rule applies. * `consequence`: The action to take when the conditions are met, such as pinning specific documents to certain positions. The API is pretty simple, here is an example of how to create a pinning rule: ```ts import { create, insertMultiple, search, insertPin } from '@orama/orama' const db = create({ schema: { title: 'string', description: 'string' } as const }) insertMultiple(db, [ { id: '1', title: 'Red Shirt', description: 'A red shirt' }, { id: '2', title: 'Blue Jeans', description: 'Blue denim jeans' }, { id: '3', title: 'Green Hat', description: 'A green hat' } ]) insertPin(db, { // [!code highlight] id: 'pin_blue_jeans', // [!code highlight] conditions: [{ anchoring: 'contains', pattern: 'shirt' }], // [!code highlight] consequence: { // [!code highlight] promote: [{ doc_id: '3', position: 0 }] // [!code highlight] } // [!code highlight] }) // [!code highlight] const results = search(db, { term: 'shirt' }) ``` In the example above, we're inserting three documents: 1. Red Shirt 2. Blue Jeans 3. Green Hat And without a pinning rule, searching for "shirt" would return one single result: ```json { "hits": [ { "id": "1", "title": "Red Shirt", "description": "A red shirt" } ], "count": 1 } ``` But since we added a pinning rule that promotes the "Green Hat" to the first position whenever the search term contains "shirt", the search results will now look like this: ```json { "hits": [ { "id": "3", "title": "Green Hat", "description": "A green hat" }, { "id": "1", "title": "Red Shirt", "description": "A red shirt" } ], "count": 2 } ``` As you can see, the pinned document will not replace the original results: they will just slide down to make room for the pinned document, so take that into account when choosing multiple pinning positions. ## Using Multiple Conditions When defining multiple conditions in a pinning rule, all conditions must be met for the rule to apply. For example: ```ts insertPin(db, { id: 'winter_jacket_rule', conditions: [ { anchoring: 'contains', pattern: 'winter' }, { anchoring: 'contains', pattern: 'jacket' } ], consequence: { promote: [{ doc_id: 'featured-jacket', position: 0 }] } }) ``` * ✅ Matches: "winter jacket" (contains both `"winter"` AND `"jacket"`) * ✅ Matches: "buy winter jacket now" (contains both) * ✅ Matches: "jacket for winter" (contains both, order doesn't matter) * ❌ Does NOT match: "winter coat" (missing `"jacket"`) * ❌ Does NOT match: "leather jacket" (missing `"winter"`) At the moment, all the conditions are implicitly `AND`ed together. Future versions may introduce support for `OR` conditions, if you have a use case for that, please let us know on [GitHub](https://github.com/oramasearch/orama/issues). ## Condition Anchoring Options When defining conditions for pinning rules, you can specify how the search term should match the pattern using different anchoring options. Here are the available options: ### `is` - Exact Match. ```js { anchoring: 'is', pattern: 'blue jeans' } ``` * ✅ Matches: "blue jeans" (exact, case-insensitive) * ✅ Matches: "Blue Jeans" (case-insensitive) * ❌ Does NOT match: "blue jeans jacket" * ❌ Does NOT match: "blue" ### `starts_with` - Prefix Match ```js { anchoring: 'starts_with', pattern: 'blue' } ``` * ✅ Matches: "blue" (exact) * ✅ Matches: "blue jeans" (starts with `"blue"`) * ✅ Matches: "blueberry pie" (starts with `"blue"`) * ❌ Does NOT match: "navy blue" (doesn't start with `"blue"`) ### `contains`: Substring Match ```js { anchoring: 'contains', pattern: 'blue' } ``` * ✅ Matches: "blue" (exact) * ✅ Matches: "blue jeans" (contains `"blue"`) * ✅ Matches: "navy blue" (contains `"blue"`) * ✅ Matches: "blueberry" (contains `"blue"`) * ❌ Does NOT match: "red shirt" (doesn't contain `"blue"`) ### Combining Conditions You can always combine multiple conditions and they will operate as an `AND`: ```js insertPin(db, { id: 'specific_search', conditions: [ { anchoring: 'starts_with', pattern: 'buy' }, // Must start with "buy" { anchoring: 'contains', pattern: 'winter' }, // Must contain "winter" { anchoring: 'contains', pattern: 'jacket' } // Must contain "jacket" ], consequence: { promote: [{ doc_id: 'featured', position: 0 }] } }) ``` This would match: * ✅ "buy winter jacket" * ✅ "buy a warm winter jacket" * ✅ "buy jacket for winter" * ❌ "winter jacket to buy" (doesn't start with `"buy"`) ## Consequence Options Currently, the only available consequence is `promote`, which allows you to pin specific documents to chosen positions in the search results. The `promote` array consists of objects with the following properties: * `doc_id`: The ID of the document to pin. * `position`: The zero-based position in the search results where the document should be pinned. You can pin multiple documents in a single rule, just make sure to assign different positions to each document: ```ts insertPin(db, { id: 'holiday_specials', conditions: [{ anchoring: 'contains', pattern: 'holiday' }], consequence: { promote: [ { doc_id: 'special-offer-1', position: 0 }, { doc_id: 'special-offer-2', position: 1 }, { doc_id: 'special-offer-3', position: 2 } ] } }) ``` ## Updating a Pinning Rule To update an existing pinning rule, you can use the `updatePin` function. This function allows you to modify the conditions or consequences of a rule by specifying its `id`. ```ts import { updatePin } from '@orama/orama' updatePin(db, { id: 'pin_blue_jeans', conditions: [{ anchoring: 'contains', pattern: 'jeans' }], // Updated condition consequence: { promote: [{ doc_id: '2', position: 0 }] // Updated consequence } }) ``` When updating a pinning rule, make sure to provide the complete rule definition, as the existing rule will be replaced with the new one. ## Deleting a Pinning Rule To delete a pinning rule, you can use the `deletePin` function by specifying the `id` of the rule you want to remove. ```ts import { deletePin } from '@orama/orama' deletePin(db, 'pin_blue_jeans') ``` ## Listing All Pinning Rules You can retrieve a list of all existing pinning rules using the `listPins` function. This function returns an array of pinning rules currently defined in the database. ```ts import { getAllRules } from '@orama/orama' const rules = getAllRules(db) ``` This will return an array of all pinning rules, allowing you to review or manage them as needed. ## Get One Specific Pinning Rule To retrieve a specific pinning rule by its `id`, you can use the `getPin` function. This function returns the details of the specified pinning rule. ```ts import { getPin } from '@orama/orama' const rule = getPin(db, 'pin_blue_jeans') ``` This will return the pinning rule with the ID `pin_blue_jeans`, allowing you to view its conditions and consequences. ## Errors When working with pinning rules, you might encounter the following errors: * `PINNING_RULE_ALREADY_EXISTS`: This error occurs when you try to create a pinning rule with an `id` that already exists in the database. Each pinning rule must have a unique `id`. To update an existing rule, use the `updatePin` function instead. * `PINNING_RULE_NOT_FOUND`: This error occurs when you try to update or delete a pinning rule that does not exist in the database. Make sure to provide a valid `id` of an existing rule. # cloud: Performing AI Session URL: /docs/cloud/ai-sessions/performing-ai-session Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/ai-sessions/performing-ai-session.mdx Learn how to perform an AI session with Orama Cloud. *** title: Performing AI Session description: Learn how to perform an AI session with Orama Cloud. ----------------------------------------------------------------- Once you have imported your data into Orama Cloud, you can start performing AI sessions. AI sessions are a way to interact with your data using interfaces that are different from the traditional search operations—for example, chats, recommendation systems, and more. It operates exclusively on your data, avoiding any external information (i.e., internal LLM knowledge base). ## Using the Official SDKs At the time of writing, the only way to perform AI sessions is through the official SDKs. As for today, Orama Cloud officially supports the following SDKs: * [JavaScript/TypeScript](https://github.com/oramasearch/oramacore-client-javascript) * [Python](https://github.com/oramasearch/oramacore-client-python) * [Rust](https://github.com/oramasearch/oramacore-client-rust) Although not publicly available yet, we are working on adding support for more languages and platforms. If you need a specific language or platform that is not yet supported, please reach out to us at [info@orama.com](mailto:info@orama.com) and we will do our best to accommodate your request. You can get started by installing the SDK of your choice using your preferred package manager: ```sh npm i @orama/core ``` ```sh pip install oramacore-client ``` ```toml [dependencies] oramacore-client = "1.2.0" tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } ``` Once you have your SDK installed, you're ready to get started performing AI sessions with Orama Cloud. ## Creating an AI Session Below you can find example snippets for creating an AI session with Orama Cloud using each official SDK. AI sessions let you interact with your data in a conversational way, ask complex questions, or get recommendations powered by large language models. ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession() // Stream an answer to a query for await (const chunk of aiSession.answerStream({ query: 'Explain quantum computing in simple terms' })) { console.log(chunk) } ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "" }) # Create AI session session = cloud.ai.create_ai_session() # Stream an answer async for chunk in session.answer_stream({ "query": "Explain quantum computing in simple terms" }): print(chunk, end="", flush=True) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, }; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session with LLM config let session_config = CreateAiSessionConfig::new() let ai_session = cloud.ai().create_ai_session().await?; // Stream an answer let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let mut stream = ai_session.answer_stream(answer_config).await?; while let Some(chunk) = stream.next().await { match chunk { Ok(data) => print!("{}", data), Err(e) => eprintln!("Stream error: {}", e), } } Ok(()) } ``` By default, Orama will use the internal `gpt-oss 120b` LLM for performing AI sessions, but you can specify a different, external model by providing a `LLMConfig` object when creating the AI session: ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession({ llmConfig: { // [!code highlight] provider: 'openai', // [!code highlight] model: 'gpt-4o-mini' // [!code highlight] } // [!code highlight] }) // Stream an answer to a query for await (const chunk of aiSession.answerStream({ query: 'Explain quantum computing in simple terms' })) { console.log(chunk) } ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "", }) # Create AI session session = cloud.ai.create_ai_session({ "llm_config": LLMConfig(provider="openai", model="gpt-4o-mini") # [!code highlight] }) # Stream an answer async for chunk in session.answer_stream({ "query": "Explain quantum computing in simple terms" }): print(chunk, end="", flush=True) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, types::{LlmConfig, LlmProvider}, }; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session with LLM config let session_config = CreateAiSessionConfig::new() .with_llm_config(LlmConfig { // [!code highlight] provider: LlmProvider::OpenAI, // [!code highlight] model: "gpt-4o-mini".to_string(), // [!code highlight] }); // [!code highlight] let ai_session = cloud.ai().create_ai_session().await?; // Stream an answer let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let mut stream = ai_session.answer_stream(answer_config).await?; while let Some(chunk) = stream.next().await { match chunk { Ok(data) => print!("{}", data), Err(e) => eprintln!("Stream error: {}", e), } } Ok(()) } ``` ## Listening for State Changes Every AI session has a state, and everytime the state changes, you can update the UI accordingly. A state is essentially represented with an array of `Interaction` objects. An `Interaction` looks like this: ```typescript export type Interaction = { id: string query: string optimizedQuery: Nullable response: string sources: Nullable loading: boolean error: boolean errorMessage: Nullable aborted: boolean related: Nullable currentStep: Nullable currentStepVerbose: Nullable selectedLLM: Nullable advancedAutoquery: Nullable<{ optimizedQueries?: Nullable selectedProperties?: Nullable selectedPropertiesWithValues?: { [key: string]: { collection: string properties: string[] } } queriesAndProperties?: Nullable<{ query: string properties: AnyObject filter_properties: AnyObject }[]> trackedQueries?: Nullable<{ index: number original_query: string generated_query_text: string search_params: SearchParams }[]> searchResults?: Nullable<{ original_query: string generated_query: string search_params: SearchParams results: SearchResult[] query_index: number }[]> results?: Nullable<{ original_query: string generated_query: string search_params: SearchParams results: SearchResult[] query_index: number }[]> }> } ``` ```python @dataclass class Interaction: id: str query: str response: str = "" optimized_query: Optional[SearchParams] = None sources: Optional[AnyObject] = None loading: bool = True error: bool = False error_message: Optional[str] = None aborted: bool = False related: Optional[str] = None current_step: Optional[str] = "starting" current_step_verbose: Optional[str] = None selected_llm: Optional[LLMConfig] = None advanced_autoquery: Optional[AdvancedAutoquery] = None ``` ```rust #[derive(Debug, Clone)] pub struct Interaction { pub id: String, pub query: String, pub response: String, pub sources: Option, pub loading: bool, pub error: bool, pub error_message: Option, pub aborted: bool, pub related: Option, pub current_step: Option, pub current_step_verbose: Option, pub selected_llm: Option, pub optimized_query: Option, pub advanced_autoquery: Option, } ``` Let's break it down a bit. ### The Interaction Object Every `Interaction` object has the following properties: * `id`: A unique identifier for the interaction. * `query`: The original query that was sent to the AI session. * `response`: The full response from the AI assistant. * `sources`: The context provided to the LLM, the result coming from performing either AI-Powered Search, vector search, etc as part of the RAG pipeline. * `loading`: A boolean indicating whether the current interaction is loading (i.e., the AI assistant is processing the query or sending the response one token at a time). * `error`: A boolean indicating whether an error occurred during the interaction. * `error_message`: A string containing the error message, if an error occurred. * `aborted`: A boolean indicating whether the interaction was aborted. * `related`: An optional string containing information about related queries or topics. * `current_step`: An optional string indicating the current step in the AI session. * `current_step_verbose`: An optional string containing a more detailed description of the current step. * `selected_llm`: An optional `LlmConfig` object indicating the language model used for the interaction. * `optimized_query`: An optional `SearchParams` object indicating the optimized query used for the interaction. * `advanced_autoquery`: An optional `object` containing the advanced autoquery status. ### State Callbacks When initializing the AI session, you can use the `events` properties to listen for state changes: ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession({ events: { onStateChange: (state) => { console.log(state) } } }) // Stream an answer to a query for await (const chunk of aiSession.answerStream({ query: 'Explain quantum computing in simple terms' })) { console.log(chunk) } ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "" }) # Define the event callback def on_state_change(state): print("State changed:", state) # Create AI session with events session = cloud.ai.create_ai_session({ "events": { "on_state_change": on_state_change } }) # Stream an answer async for chunk in session.answer_stream({ "query": "Explain quantum computing in simple terms" }): print(chunk) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, types::{LlmConfig, LlmProvider}, }; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session config with events - use a callback closure for state changes let mut session_config = CreateAiSessionConfig::new(); session_config.on_state_change(|state| { println!("State changed: {:?}", state); }); let ai_session = cloud.ai().create_ai_session_with_config(session_config).await?; // Stream an answer let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let mut stream = ai_session.answer_stream(answer_config).await?; while let Some(chunk) = stream.next().await { match chunk { Ok(data) => println!("{:?}", data), Err(e) => eprintln!("Stream error: {}", e), } } Ok(()) } ``` As explained above, a `state` is an array of `Interaction` objects. So every time you're calling `aiSession.answerStream` on the same session, you're adding a new interaction to the session's state. This can be particularly useful when you want to keep track of the conversation history for instance to build a chat-style interface. ## Avoiding Streaming Sometimes you might want to avoid streaming the answer and instead get the full answer at once. You can do this by calling `aiSession.answer` instead of `aiSession.answerStream`. ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession({ events: { onStateChange: (state) => { console.log(state) } } }) // Get a single answer (non-streaming) const answer = await aiSession.answer({ query: 'Explain quantum computing in simple terms' }) console.log(answer) ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "" }) # Define the event callback def on_state_change(state): print("State changed:", state) # Create AI session with events session = cloud.ai.create_ai_session({ "events": { "on_state_change": on_state_change } }) # Get a single answer (non-streaming) answer = await session.answer({ "query": "Explain quantum computing in simple terms" }) print(answer) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, types::{LlmConfig, LlmProvider}, }; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session config with events - use a callback closure for state changes let mut session_config = CreateAiSessionConfig::new(); session_config.on_state_change(|state| { println!("State changed: {:?}", state); }); let ai_session = cloud.ai().create_ai_session_with_config(session_config).await?; // Get a single answer (non-streaming) let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let answer = ai_session.answer(answer_config).await?; println!("{:?}", answer); Ok(()) } ``` ## Aborting an AI Session To abort an AI session, you can call the `abort` method on the session object. This will stop the session and release any resources associated with it: ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession({ events: { onStateChange: (state) => { console.log(state) } } }) // Abort the AI session after 1s setTimeout(async () => { await aiSession.abort() }, 1000) // Get a single answer (non-streaming) const answer = await aiSession.answer({ query: 'Explain quantum computing in simple terms' }) console.log(answer) ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "" }) # Define the event callback def on_state_change(state): print("State changed:", state) # Create AI session with events session = cloud.ai.create_ai_session({ "events": { "on_state_change": on_state_change } }) # Schedule abort after 1 second async def abort_session(): await asyncio.sleep(1) await session.abort() # Get a single answer (non-streaming) answer = await session.answer({ "query": "Explain quantum computing in simple terms" }) print(answer) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, types::{LlmConfig, LlmProvider}, }; use tokio::time::{sleep, Duration}; use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session config with events - use a callback closure for state changes let mut session_config = CreateAiSessionConfig::new(); session_config.on_state_change(|state| { println!("State changed: {:?}", state); }); let ai_session = Arc::new(cloud.ai().create_ai_session_with_config(session_config).await?); // Spawn a task to abort the session after 1s let ai_session_clone = ai_session.clone(); tokio::spawn(async move { sleep(Duration::from_secs(1)).await; let _ = ai_session_clone.abort().await; }); // Get a single answer (non-streaming) let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let answer = ai_session.answer(answer_config).await?; println!("{:?}", answer); Ok(()) } ``` ## Retrieving the State You can retrieve the state of an AI session using the `state` getter at any time: ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession() // Stream an answer to a query for await (const chunk of aiSession.answerStream({ query: 'Explain quantum computing in simple terms' })) { console.log(chunk) } // Get the current state of the session console.log('Session state:', aiSession.state) ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "" }) # Create AI session session = cloud.ai.create_ai_session() # Stream an answer async for chunk in session.answer_stream({ "query": "Explain quantum computing in simple terms" }): print(chunk, end="", flush=True) # Get the current state of the session print("Session state:", session.state) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, }; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session with LLM config let session_config = CreateAiSessionConfig::new(); let ai_session = cloud.ai().create_ai_session().await?; // Stream an answer let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let mut stream = ai_session.answer_stream(answer_config).await?; while let Some(chunk) = stream.next().await { match chunk { Ok(data) => print!("{}", data), Err(e) => eprintln!("Stream error: {}", e), } } // Get the current state of the session println!("Session state: {:?}", ai_session.state()); Ok(()) } ``` # cloud: Choosing the Right LLM URL: /docs/cloud/context-engineering/choosing-llm Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/context-engineering/choosing-llm.mdx Learn how to choose the right LLM for your Orama Cloud project. *** title: Choosing the Right LLM description: Learn how to choose the right LLM for your Orama Cloud project. ---------------------------------------------------------------------------- ![Context Engineering Section](/docs/cloud/context-engineering/context-engineering.gif) Orama Cloud makes intensive use of Large Language Models not only to reply to the user queries, but also in intermediate steps of the context engineering process. Choosing the right LLM is crucial to ensure balance between quality and performance. By default, Orama Cloud uses OpenAI's open weight `GPT-OSS 120b` model, as it ensures great quality and performance. However, you can select a different model choosing from the available options. At the time of writing, the same model will be used throughout the entire context engineering process. ## Available Models Right now, Orama Cloud supports the following models: | Provider | Author | Model | Description | | -------- | ----------- | ------------------ | --------------------------------------------------------- | | Orama | OpenAI | `GPT-OSS 120b` | OpenAI's open weight `GPT-OSS 120b` model | | Orama | Qwen | `Qwen 3 32b` | Qwen's flagship 32b model | | Orama | MoonShot AI | `Kimi K2` | 32b Parameter model by MoonShot AI | | OpenAI | OpenAI | `GPT-4o` | Great for most tasks | | OpenAI | OpenAI | `GPT-4o-mini` | Small model for focused tasks | | OpenAI | OpenAI | `GPT-4.1` | Great for quick coding and analysis | | OpenAI | OpenAI | `GPT-4.1` | Great for quick coding and analysis | | OpenAI | OpenAI | `GPT-o3` | Uses advanced reasoning | | Google | Google | `Gemini 2.5 Flash` | Perfect for rapid development and efficient data insights | | Google | Google | `Gemini 2.5 Flash` | Great for advanced reasoning and deep data analysis | You can choose any model that suits your needs and test it directly in the Orama Cloud dashboard, under the "Context Engineering" section. ## Choosing the LLM Programmatically When you perform an [AI-Powered NLP Search](/docs/cloud/performing-search/search-modes/ai-powered-nlp-search) or an [AI Session](/docs/cloud/ai-sessions/performing-ai-session), you can choose the model programmatically via the official SDKs. **During an AI-Powered NLP Search:** ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) const searchResults = await orama.ai.NLPSearch({ query: 'black elegant shoes under $200', LLMConfig: { // [!code highlight] provider: 'openai', // [!code highlight] model: 'gpt-4o-mini', // [!code highlight] }, }) console.log(searchResults) ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import NLPSearchParams from orama import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "" }) results = await cloud.ai.nlp_search( NLPSearchParams( query="black elegant shoes under $200", llm_config=LLMConfig( # [!code highlight] provider="openai", # [!code highlight] model="gpt-4o-mini" # [!code highlight] ) # [!code highlight] ) ) print("NLP Search results:", results) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, types::{NlpSearchParams, LlmConfig, LlmProvider}, }; use tokio; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new( "", "" ); let cloud = OramaCloud::new(config).await?; let nlp_params = NlpSearchParams { query: "black elegant shoes under $200".to_string(), llm_config: Some(LlmConfig { // [!code highlight] provider: LlmProvider::OpenAI, // [!code highlight] model: "gpt-4o-mini".to_string(), // [!code highlight] }), // [!code highlight] user_id: None, }; let nlp_results = cloud.ai().nlp_search::(nlp_params).await?; println!("NLP Search Results: {:?}", nlp_results); Ok(()) } ``` **During an AI Session:** ```typescript import { OramaCloud } from '@orama/core' const orama = new OramaCloud({ projectId: '', apiKey: '', }) // Create the AI session const aiSession = await orama.ai.createAISession({ llmConfig: { // [!code highlight] provider: 'openai', // [!code highlight] model: 'gpt-4o-mini' // [!code highlight] } // [!code highlight] }) // Stream an answer to a query for await (const chunk of aiSession.answerStream({ query: 'Explain quantum computing in simple terms' })) { console.log(chunk) } ``` ```python import asyncio from orama.cloud import OramaCloud from orama.collection import LLMConfig async def main(): cloud = OramaCloud({ "project_id": "", "api_key": "", }) # Create AI session session = cloud.ai.create_ai_session({ "llm_config": LLMConfig(provider="openai", model="gpt-4o-mini") # [!code highlight] }) # Stream an answer async for chunk in session.answer_stream({ "query": "Explain quantum computing in simple terms" }): print(chunk, end="", flush=True) await cloud.close() if __name__ == "__main__": asyncio.run(main()) ``` ```rust use oramacore_client::{ cloud::{OramaCloud, ProjectManagerConfig}, stream_manager::{CreateAiSessionConfig, AnswerConfig}, types::{LlmConfig, LlmProvider}, }; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let config = ProjectManagerConfig::new("", ""); let cloud = OramaCloud::new(config).await?; // Create AI session with LLM config let session_config = CreateAiSessionConfig::new() .with_llm_config(LlmConfig { // [!code highlight] provider: LlmProvider::OpenAI, // [!code highlight] model: "gpt-4o-mini".to_string(), // [!code highlight] }); // [!code highlight] let ai_session = cloud.ai().create_ai_session().await?; // Stream an answer let answer_config = AnswerConfig::new("Explain quantum computing in simple terms"); let mut stream = ai_session.answer_stream(answer_config).await?; while let Some(chunk) = stream.next().await { match chunk { Ok(data) => print!("{}", data), Err(e) => eprintln!("Stream error: {}", e), } } Ok(()) } ``` # cloud: Introduction URL: /docs/cloud/context-engineering/introduction Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/context-engineering/introduction.mdx Learn how to use context engineering to improve RAG. *** title: Introduction description: Learn how to use context engineering to improve RAG. ----------------------------------------------------------------- Context engineering is a powerful set of techniques for improving the quality of your RAG system. Orama Cloud provides all the tools you need to customize and optimize it. ## RAG Stages After you inserted some data into your Orama Cloud project, you can start to perform [AI-Powered NLP Search](/docs/cloud/performing-search/search-modes/ai-powered-nlp-search) as well as [AI Sessions](/docs/cloud/ai-sessions/performing-ai-session). In both scenarios, Orama Cloud will go through a series of steps (stages) to retrieve the most relevant documents for your query. When engaging in context engineering, your goal is to optimize these stages to improve the quality and performance of each individual stage. There are several different ways to do this, including: * **Choosing the right embedding model** * **Choosing the right LLM** * **Adding the correct system prompt** * **Selecting/validating some training data** * **Adding JavaScript hooks** But we will look into that in the next section. ### The Context Engineering Page ![Context Engineering Section](/docs/cloud/context-engineering/page.png) You can access the context engineering configuration page by clicking on the "Context Engineering" tab in your project menu. From here, you can visualize the entire RAG flow and test out different large language models, add system prompts, validate training data, and add JavaScript hooks. ### Context Engineering Flow ![Context Engineering Section](/docs/cloud/context-engineering/flow.png) The context engineering flow allows you to visualize how Orama Cloud is working behind the scenes in real-time. You may notice that some flow steps contain some colorful icons. If you go over them with your mouse, you will see a tooltip with more information about each step: ![Context Engineering Section](/docs/cloud/context-engineering/ceflow.gif) Some flow steps in fact accepts the following configurations: * **Custom System Prompts**. Instruct Orama Cloud on how to behave in a specific step. * **Training Data**. Orama Cloud will generate some training data for you - you will just have to give feedback on its quality to help Orama Cloud improve. * **JavaScript Hooks**. Add custom JavaScript code to modify the behavior of Orama Cloud programmatically. All these steps will have more detailed explanation in their own sections. # cloud: Adding Your Data to Orama Cloud URL: /docs/cloud/data-sources/about Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/data-sources/about.mdx Connect Orama Cloud to a data source to index and search your data. *** title: Adding Your Data to Orama Cloud description: Connect Orama Cloud to a data source to index and search your data. -------------------------------------------------------------------------------- When creating a project on Orama Cloud, you can start inserting data by connecting to one or more data sources. A data source, as the name suggests, is a source of data that can be indexed and searched using Orama Cloud. There are four main types of data sources: 1. **REST APIs**: you can send data to your Orama instance using HTTP requests or the official SDKs. 2. **Files**: you can upload a JSON, JSONL, CSV, XML, or PDF file to your Orama instance. 3. **Crawler**: Orama Cloud comes with a crawler that can extract data from your website. 4. **Connectors**: you can use one of the pre-built connectors to periodically sync your data. ![Orama Cloud data sources](/docs/cloud/data-sources/empty-data-sources.png) There's also a fifth type of data source, that is a bit more different from the others, and serves a different purpose. It's called the **Knowledge Base**. ## The Orama Knowledge Base In the knowledge base, you can store documents that are not meant to be searched, but that can be relevant at RAG-time. So when performing an AI Session, Orama can access the documents stored in the knowledge base to provide relevant information to the AI model, but this information is not used for search purposes, hiding it effectively from the search results and from your users. You can read more about the Knowledge Base in the [Knowledge Base documentation](/docs/cloud/data-sources/knowledge-base). # cloud: Knowledge Base URL: /docs/cloud/data-sources/knowledge-base Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/data-sources/knowledge-base.mdx Store hidden documents to enhance your AI sessions with relevant information. *** title: Knowledge Base description: Store hidden documents to enhance your AI sessions with relevant information. ------------------------------------------------------------------------------------------ When performing AI Sessions - such as chatbots, virtual assistants, etc., you may want to access information that should not be publicly available. This is where the Knowledge Base comes in handy. When creating a new project, Orama Cloud will automatically create an empty Knowledge Base for you. ![Empty Knowledge Base](/docs/cloud/data-sources/empty-knowledge-base.png) The **Knowledge Base** is effectively an index, just like any other data source in Orama Cloud, and you can add documents by using the Orama Cloud dashboard. For example, let's say you want to add context to your AI session about Orama vs a competitor - for the sake of this example, let's assume the competitor is example.ai. You may want to avoid to publish content that specifically targets competitors, but you still want to instruct the AI to consider the competitor's strengths and weaknesses when compared to your product. This is a perfect use case for the Knowledge Base: ![Knowledge Base Edit Document](/docs/cloud/data-sources/knowledge-base-edit-document.png) Once you've added your documents, you will be able to edit or delete them at any time. If you write a lot of documents, you can always search them by keywork or by meaning. ![Knowledge Base Edit Document](/docs/cloud/data-sources/knowledge-base-with-docs.png) # cloud: Uploading a File URL: /docs/cloud/data-sources/uploading-a-file Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/data-sources/uploading-a-file.mdx Learn how to upload a JSON, CSV, or XML file to Orama Cloud. *** title: Uploading a File description: Learn how to upload a JSON, CSV, or XML file to Orama Cloud. ------------------------------------------------------------------------- Sometimes it is just easier to export your data from any system into a JSON, JSONL, CSV, or XML file, and then upload it to Orama Cloud. You can do so by heading to the **Data Sources** section of your project and clicking `Connect to Data Source`: ![Create Data Source](/docs/cloud/data-sources/create-data-source.png) Here you can choose which data source you want to connect to. For this specific guide, we will be uploading a JSON file. If you want to follow along, you can download the following [simple JSON file](/docs/cloud/data-sources/movies-database.json), which contains about 1000 movies in JSON format. Click on `Upload a File` to get started with the upload process. ![Create Data Source](/docs/cloud/data-sources/select-data-source.png) Once you're in the following page, you can simply drag and drop your file and choose a name and an optional description for your index: ![File Upload](/docs/cloud/data-sources/file-upload.png) After you've uploaded your file, you will notice that it will be queued for processing. Depending on the size of your file, this process can take a few minutes. ![File Queued](/docs/cloud/data-sources/file-queued.png) Once the file is processed, you can access it by clicking on its raw in the data sources table, and here you can see a few more important information: * The data source ID * The project ID * The original file name and its size * Who uploaded it * When it was uploaded In case you need support, remember to include the data source ID and the project ID in your message. A simple screenshot of this page will be helpful as well. ![File Info](/docs/cloud/data-sources/file-info.png) ## Uploading a new File In case you need to replace the file with a new one, you can simply click on `upload a new file`. You will be presented with the same file upload page you used to upload the original file, allowing you to upload a new one. Remember that uploading a new file will replace the existing one, and all data associated with it will be lost. This action cannot be undone. ![Replace File](/docs/cloud/data-sources/replace-file.png) # cloud: Introduction URL: /docs/cloud/performing-search/introduction Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/performing-search/introduction.mdx Learn how to perform fast and efficient search operations with Orama Cloud. *** title: Introduction description: Learn how to perform fast and efficient search operations with Orama Cloud. ---------------------------------------------------------------------------------------- Once you have imported your data into Orama Cloud, you can start performing search operations. Orama Cloud generates text embeddings automatically for your data using local, high-quality models. That means that you're able to perform full-text, vector, hybrid, and AI-powered NLP search operations right away as soon as you import your first document. ## Using the Official SDKs At the time of writing, the only way to perform search operations is through the official SDKs. As for today, Orama Cloud officially supports the following SDKs: * [JavaScript/TypeScript](https://github.com/oramasearch/oramacore-client-javascript) * [Python](https://github.com/oramasearch/oramacore-client-python) * [Rust](https://github.com/oramasearch/oramacore-client-rust) Although not publicly available yet, we are working on adding support for more languages and platforms. If you need a specific language or platform that is not yet supported, please reach out to us at [info@orama.com](mailto:info@orama.com) and we will do our best to accommodate your request. You can get started by installing the SDK of your choice using your preferred package manager: ```sh npm i @orama/core ``` ```sh pip install oramacore-client ``` ```toml [dependencies] oramacore-client = "1.2.0" tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } ``` Once you have your SDK installed, you're ready to get started searching with Orama Cloud. ## 90 Shades of Search When performing search operations, Orama Cloud gives you the flexibility to choose the search algorithm that best suits your needs. Here are some of the search algorithms that Orama Cloud supports: * Full-text search * Vector search * Hybrid search * AI-powered NLP search ### Full-Text Search Full-text search performs traditional keyword-based matching against your document content, using techniques like tokenization, stemming, and relevance scoring (TF-IDF/BM25). * **Pros:** Fast, predictable, excellent for exact keyword matches, and works well with structured queries. * **Cons:** Limited semantic understanding, struggles with synonyms or conceptually related terms, and requires users to know specific keywords. * **When to use:** Ideal for precise searches where users know exact terms, product catalogs, documentation searches, or when you need fast, deterministic results. ### Vector Search Vector search converts your content into high-dimensional mathematical representations (embeddings) that capture semantic meaning, allowing for similarity-based matching. * **Pros:** Understands context and meaning, finds semantically related content even without exact keyword matches, and excels at discovering conceptually similar documents. * **Cons:** Can be slower than full-text search, results may seem less predictable to users, and requires quality embeddings for optimal performance. * **When to use:** Perfect for recommendation systems, content discovery, finding documents with similar themes, or when users search with natural language descriptions. ### Hybrid Search Hybrid search combines the precision of full-text search with the semantic understanding of vector search, typically using weighted scoring to merge results from both approaches. * **Pros:** Balances exact keyword matching with semantic relevance, provides more comprehensive results, and reduces the weaknesses of using either method alone. * **Cons:** More complex to tune and optimize, potentially higher computational cost, and requires careful balance between the two scoring methods. * **When to use:** Ideal for most production search applications where you want both precise matches and semantic discovery, such as e-commerce, knowledge bases, or content platforms. ### AI-Powered NLP Search (Natural Language Query Translation) NLP-based search interprets natural language queries and translates them into structured Orama queries, allowing users to search conversationally. * **Pros:** Intuitive user experience, handles complex queries with filters and conditions, and bridges the gap between how users think and how databases work. * **Cons:** Slightly slower, may misinterpret complex or ambiguous queries, and adds several additional processing layers. * **When to use:** Excellent for user-facing applications where you want to provide a ChatGPT-like search experience, business intelligence dashboards, or when non-technical users need to perform complex searches without learning query syntax. # cloud: Create a Project URL: /docs/cloud/projects/create Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/projects/create.mdx Create a project in Orama Cloud *** title: Create a Project description: Create a project in Orama Cloud -------------------------------------------- Once you have signed up for Orama Cloud, you can create a project. A project is a set of data sources that you can use to bring data into Orama Cloud. If you're not familiar with the concept of collections, check out the [introduction to Orama Cloud](/docs/cloud#projects) for more information. ## Create a project The first time you log in to Orama Cloud, you will see an empty projects window. Click on the **Create your first project** button to create your first project: ![Empty Projects Window](/docs/cloud/projects/empty-project.png) When asked, insert a project name and click on **Create project**. You will be redirected to the "create project page", which will look like this: ![Create new Project Window](/docs/cloud/projects/new-project-settings.png) At this point, you can create your first project by giving it a name, and selecting the language and embedding model you want to use. You can also add an optional description to help you identify the project later. You can always change the project language and embedding model later. This will require a complete reindex of your data, which could take some time depending on the size of your dataset. Once you have created your new project, you will be redirected to the project overview page, which will look like this: ![New Project Overview](/docs/cloud/projects/new-project-overview.png) The overview page contains the basic information about the project (name, description) as well as two fundamental, automatically generated information: * **Project ID** \ This is the ID of the project you have just created. If you need to ask for assistance to the Orama team, always include this ID in your message. * **Read-Only API Key**: \ This is a public key that you can use to query the project via API or SDK. It is safe to share on the browser or in your code, as it only gives read access to the project. This page will also give you some basic information about the project, such as the number of data connected sources, the number of documents, as well as the project language and embedding model information. # cloud: Project Settings URL: /docs/cloud/projects/project-settings Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/projects/project-settings.mdx Changing language, embedding models, and more. *** title: Project Settings description: Changing language, embedding models, and more. ----------------------------------------------------------- The project settings page allows you to manage various aspects of your project, including language settings, model embedding, and more. ![Project Settings Page](/docs/cloud/projects/settings.png) ## Changing a Project Name You can change the project name at any time without causing any disruption to your project's functionality or data. Simply navigate to the project settings page and update the name as needed. ## NLP Configuration Here you can change the language used for natural language processing tasks. At the time of writing Orama supports \~30 languages out of the box. Changing the language will affect the way text is processed and indexed within your project, potentially impacting search results and relevance. If your project is multi-lingual, you may want to consider to keep the **English** language as the default. Also, please note that not all embedding models support all languages. Make sure to choose a model that supports the languages you need. As a rule of thumb, we recommend using a `BGE` model when your project is using English data, and either **E5 Multilingual** or **Paraphrase** for multi-lingual data. If your project is also hosting some code snippets, then the **Jina** embedding model is a good choice. It is optimized for both text and code embeddings and can handle a wide range of programming languages. ## Deleting a Project In the settings page, you can also delete your project. This action is irreversible and will permanently remove all data associated with your project. Please proceed with caution. # cloud: Manage Teams URL: /docs/cloud/teams/manage-teams Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/teams/manage-teams.mdx Learn how to manage teams in Orama Cloud. *** title: Manage Teams description: Learn how to manage teams in Orama Cloud. ------------------------------------------------------ When creating a new account on Orama Cloud, you will be automatically assigned to a new team, which takes your first name as its name. For instance, my name is "Michele", so I am automatically assigned to a team named "Michele's Team", and I am the owner. I can invite new team members to join my team at any time or create a new team. ![Team Page](/docs/cloud/teams/teams-page.png) ## Managing Teams You can switch between teams by clicking on the team name in the top left corner of the page: ![Change Team](/docs/cloud/teams/change-team.png) You will always have access to this select in all pages of the platform. ## Create a New Team You can create a new team by clicking on your current team name in the top left corner of the page, then selecting "Create New Team". A modal will appear, where you can enter the name of your new team: ![Create Team](/docs/cloud/teams/create-new-team.png) After creating the team, you will be automatically assigned as the admin of the new team, and you will be redirected to the new team's settings page, where you can manage your team's settings and members: ![Team Settings](/docs/cloud/teams/new-team-page.png) To invite a new team member, simply click on the "Invite Member" button and add their email address. They will receive an email with a link to join your team. If they have an Orama Cloud account, they will join your team automatically. Otherwise, they will be asked to create a new account first, then they will be automatically added to your team. # cloud: Components URL: /docs/cloud/ui-library/components Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/ui-library/components.mdx Learn how to compose Orama UI components for your projects. *** title: Components description: Learn how to compose Orama UI components for your projects. ------------------------------------------------------------------------ Orama UI provides a set of React components designed to build AI-driven interfaces powered by Orama. > **Note**: All components must be wrapped inside either `SearchRoot` or `ChatRoot`, depending on whether you are building a search or chat experience. These root providers handle state management and context required by the child components. Below is an overview of all available components, grouped by category. Each component links to its detailed documentation on GitHub. ## Core components * **[`SearchRoot`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/SearchRoot.md)** - Root provider for search functionality and state management * **[`ChatRoot`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/ChatRoot.md)** - Root provider for chat/conversation interfaces * **[`SearchResults`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/SearchResults.md)** - Displays search results with customizable rendering * **[`ChatInteractions`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/ChatInteractions.md)** - Renders chat messages and user actions * **[`Suggestions`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/Suggestions.md)** - Displays prompt suggestions ### Form Components * **[`PromptTextArea`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/PromptTextArea.md)** - Textarea for chat prompts * **[`SearchInput`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/SearchInput.md)** - Input field for search queries with built-in search logic ### Navigation & Filtering * **[`FacetTabs`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/FacetTabs.md)** - Tab-based filtering for search facets * **[`Tabs`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/Tabs.md)** - Generic tab navigation component ### Layout Components * **[`Modal`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/Modal.md)** - Accessible modal dialog with focus management * **[`SlidingPanel`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/components/SlidingPanel.md)** - Slide-in panel for sidebars and overlays # cloud: Context URL: /docs/cloud/ui-library/context Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/ui-library/context.mdx Learn how to use Orama UI context providers for your projects. *** title: Context description: Learn how to use Orama UI context providers for your projects. --------------------------------------------------------------------------- # Contexts Orama UI provides React contexts that enable direct control over search and chat state management. These contexts are the foundation upon which the components like `SearchRoot` and `ChatRoot` are built. ## When to Use Contexts **You typically don't need to use these contexts directly.** The `SearchRoot` and `ChatRoot` components provide all the functionality most applications need, with simplified APIs and built-in state management. However, contexts become useful when you need fine-grained control over state, want to build custom components, or have specific requirements that the root components don't cover. *** ## Available Contexts ### Search Context **[`SearchContext`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/context/SearchContext.md)** - Provides search state and functionality to child components The SearchContext manages all search-related state including: * Search client instance * Current search terms and results * Facet selections and counts * Search dispatch actions ### Chat Context **[`ChatContext`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/context/ChatContext.md)** - Manages chat state, conversation history, and AI responses The ChatContext handles all chat-related functionality including: * Chat client instance * User prompts and interactions * Answer sessions and streaming * Conversation history management *** Both contexts follow a similar pattern using React's `useReducer` pattern. Each context exposes: * State context: access to current state * Dispatch context: Functions to update state * Custom hooks: Convenient access to both state and dispatch * Reducer: State management logic * Initial state: Default values This architecture ensures predictable state updates and makes it easy to reason about how data flows through your application. # cloud: Hooks URL: /docs/cloud/ui-library/hooks Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/ui-library/hooks.mdx Learn how to use Orama UI hooks for your projects. *** title: Hooks description: Learn how to use Orama UI hooks for your projects. --------------------------------------------------------------- ## Overview Orama UI provides a set of custom hooks to manage state and side effects in your components. These hooks are designed to work seamlessly with the Orama UI components and context. You don't necessarily need to import and use the hooks directly, as they are utilized by the Orama UI components themselves to function properly. However, you may need to use them for custom interfaces or advanced usage. Below is an overview of all available hooks. Each hook links to its detailed documentation on GitHub. ## Available Hooks ### Primary Hooks * **[`useSearch`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/hooks/useSearch.md)** - Access search state, query, results, and search functions * **[`useChat`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/hooks/useChat.md)** - Manage chat conversations, messages, and AI interactions The primary hooks must be used within the context of `SearchRoot` or `ChatRoot`, depending on whether you are building a search or chat experience. These root providers handle state management and context required by the hooks. ### Utility Hooks * **[`useArrowKeyNavigation`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/hooks/useArrowKeyNavigation.md)** - Keyboard navigation for lists and results * **[`useClipboard`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/hooks/useClipboard.md)** - Copy text to clipboard with feedback * **[`useScrollableContainer`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/hooks/useScrollableContainer.md)** - Manage scrollable content areas * **[`useLastInteractionMinHeight`](https://github.com/oramasearch/orama-ui/blob/main/packages/ui/docs/hooks/useLastInteractionMinHeight.md)** - Dynamic height management for chat interactions These utility hooks can be used independently of the `SearchRoot` or `ChatRoot` context, but they are often useful in building AI-driven interfaces. # cloud: Introduction URL: /docs/cloud/ui-library/introduction Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/cloud/ui-library/introduction.mdx Learn how to leverage Orama UI for your projects. *** title: Introduction description: Learn how to leverage Orama UI for your projects. -------------------------------------------------------------- **Orama UI** is a composable, unstyled React component library designed for building AI-driven interfaces powered by Orama. Whether you’re adding a **chat assistant**, **smart search**, or **custom AI-powered workflows**, this library gives you the headless building blocks you need. No styling or UI lock-in. It provides: * **⚡ Headless components**: unstyled primitives ready for your design system. * **🪝 Hooks**: utilities for managing AI queries, chat state, and search functionality. * **🌐 Context providers**: to handle global state across assistants, messages, or search results. Use it with your favorite UI framework (Tailwind, shadcn, Material UI, custom CSS) while focusing on functionality, not boilerplate. # Philosophy Orama UI follows these core principles: * **🧩 Composable:** Components are designed to be combined and nested as building blocks. Mix and match to create your perfect interface. * **🎨 Unstyled:** Zero default styles mean you have complete control over the visual design. No CSS conflicts or overrides needed. * **⚡ Flexible:** Use only what you need. Each component works independently or as part of a larger system. * **♿ Accessible:** Built with accessibility in mind, following ARIA best practices and keyboard navigation standards. * **🔧 Developer-friendly:** TypeScript support, comprehensive documentation, and intuitive APIs. *** ## Get started Orama UI is designed to work with [Orama Cloud](https://app.orama.com) projects. You'll need an active account and project set up on the Orama Cloud platform before using the library. First time with Orama Cloud? Read the documentation to [create a project in Orama Cloud](/docs/cloud/projects/create). Once you have created your project and uploaded your data, you can grab the **Project ID** and **Read-Only API Key** from the Orama Cloud dashboard. These credentials will be used to configure the Orama UI components, so keep them handy and let’s get started! ## Installation Install the required packages: ```bash npm install @orama/ui @orama/core # or yarn add @orama/ui @orama/core # or pnpm add @orama/ui @orama/core ``` ## Set up Import the Orama JavaScript client and create your instance using your Cloud project configuration: ```javascript import { OramaCloud } from '@orama/core'; const orama = new OramaCloud({ projectId: '', apiKey: '', }) ``` ## Usage Import and use the components: ```javascript import { SearchRoot, SearchInput, SearchResults, } from "@orama/ui/components"; {(result) => (

{result.title}

{result.description}

)}
``` ## Styling Style as you wish! Orama UI components are unstyled by default. Use your own CSS, Tailwind, styled-components, or any styling solution. > Please note that, while Orama UI components are unstyled, some layout components may require minimal styling to function properly. If you choose to use a layout component (i.e. `SlidingPanel`), ensure you import the `styles.css` file provided in the library for basic layout support. ```javascript import "@orama/ui/styles.css" ``` ## Supported frameworks This library currently supports **React 19 with TypeScript only**, due to its strong ecosystem and TypeScript integration. Support for other frameworks may be considered in the future. # orama-js: Components URL: /docs/orama-js/internals/components Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/internals/components.mdx Learn how to customize Orama by using its components architecture. *** title: Components description: Learn how to customize Orama by using its components architecture. ------------------------------------------------------------------------------- Orama can be completely customized and extended by using its components architecture. Depending on the case, a component can be a simple as a function or a slightly more complex interface. All components can be synchronous or return a promise and Orama will make sure everything is handled correctly. When no components are specified, an Orama database is created with some defaults components which satisfies most common use cases: * English tokenizer with stemming disabled. * BM25 and Radix-Tree based index. * In memory documents store. ## Providing your own components It's very easy to provide a custom component when creating a database: simply pass the `components` option when calling `create`. For instance, this code: ```javascript import { create, insert, search } from "@orama/orama"; const movieDB = create({ schema: { title: "string", director: "string", plot: "string", year: "number", isFavorite: "boolean", }, components: { afterInsert() { console.log("INSERTED"); }, }, }); insert(movieDB, { title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", plot: "Harry Potter, an eleven-year-old orphan, discovers that he is a wizard and is invited to study at Hogwarts. Even as he escapes a dreary life and enters a world of magic, he finds trouble awaiting him.", year: 2001, isFavorite: false, }); const results = search(movieDB, { term: "Harry" }); console.log(results.count); ``` Will lead to this output: ``` INSERTED 2 ``` ## Supported components ### `tokenizer` The tokenizer is used to tokenize documents fields and search terms. To customize the tokenizer used by Orama, provide an object which has at least the following properties: * `tokenize`: A function that accepts the content to tokenize (string), the language (string) and the property name (string) and returns a list of tokens. * `language` (string): The language supported by the tokenizer. * `normalizationCache` (Map): It can used to cache tokens normalization. The `tokenize` function **CANNOT** be async. In other words, a tokenizer must satisfy the following interface: ```typescript interface Tokenizer { language: string; normalizationCache: Map; tokenize: ( raw: string, language?: string, prop?: string ) => string[] | Promise; } ``` For instance, with the following configuration only the first character of each string will be indexed and only the first character of a term will be searched: ```javascript import { create } from "@orama/orama"; const movieDB = create({ schema: { title: "string", director: "string", }, components: { tokenizer: { language: "english", normalizationCache: new Map(), tokenize(raw) { return raw[0]; }, }, }, }); ``` The Orama's default tokenizer is exported via `@orama/orama/components` and can be customized: ```javascript import { create } from "@orama/orama"; import { tokenizer as defaultTokenizer } from "@orama/orama/components"; const movieDB = create({ schema: { title: "string", director: "string", }, components: { tokenizer: await defaultTokenizer.createTokenizer({ language: "english", stemming: false, }), }, }); ``` Optionally you can pass the customization options without using `createTokenizer`: ```javascript import { create } from "@orama/orama"; const movieDB = create({ schema: { title: "string", director: "string", }, components: { tokenizer: { language: "english", stemming: false }, }, }); ``` ### `index` The index component is used to perform the indexing and searching of documents in Orama. To customize the index used by Orama, provide an object which has at least the following properties: * `create`: A function that creates a new index. It receives the following arguments: * `orama`: The Orama instance. * `mapper`: The document IDs mapper (see Internal components section below). * `schema`: The documents schema. * `insert`: A function that inserts a new document in the index. It receives the following arguments: * `implementation`: The current index implementation. * `index`: The index. * `prop`: The property that it is currently considered. * `id`: The ID of the document being inserted. * `value`: The value of the property in the document. * `expectedType`: The type of the property in the document according with the schema. * `language`: The language of the document. * `tokenizer`: The tokenizer associated with the current database. * `docsCount`: The number of documents in the documents store before the action is performed. * `remove`: A function that removes a document from the index. It receives the same arguments as `insert`. * `insertDocumentScoreParameters`: A function that inserts document information into the index for future results score calculation. It should be typically invoked within `insert`. It receives the following arguments: * `index`: The index. * `prop`: The property that is currently considered. * `id`: The ID of the document being inserted. * `tokens`: The list of the tokens found in the document. * `docsCount`: The number of documents in the documents store before the action is performed. * `insertTokenScoreParameters`: A function that inserts token information into the index for future results score calculation. It should be typically invoked within `insert`. It receives the following arguments: * `index`: The index. * `prop`: The property that it is currently considered. * `id`: The ID of the document being inserted. * `token`: The token. * `tokens`: The list of the tokens found in the document. * `removeDocumentScoreParameters`: A function that removes document scores information from the index. It should be typically invoked within `remove`. It receives the following arguments: * `index`: The index. * `prop`: The property that is currently considered. * `id`: The ID of the document being inserted. * `docsCount`: The number of documents in the documents store before the action is performed. * `removeTokenScoreParameters`: A function that removes token score information from the index. It should be typically invoked within `remove`. It receives the following arguments: * `index`: The index. * `prop`: The property that is currently considered. * `id`: The ID of the document being inserted. * `token`: The token. * `calculateResultScores`: A function that calculates the score for the results of the current search. It should be typically invoked within `search`. It receives the following arguments: * `context`: A search context with various useful information about the search. * `index`: The index. * `prop`: The property search. * `term`: The term used to search. * `ids`: The list of document IDs matched by the search. * `search`: A function that searches documents in index data and returns matching IDs with scores. It receives the following arguments: * `context`: A search context with various useful information about the search. * `index`: The index. * `prop`: The property to search into. * `term`: The term to search for. * `searchByWhereClause`: A function that searches in boolean and numeric indexes and returns a list of matching IDs. It receives the following arguments: * `context`: A search context with various useful information about the search. * `index`: The index. * `filters`: An object where keys are the properties to match and the values are search operators as described in the [filters](/docs/orama-js/search/filters) page. * `getSearchableProperties`: A function that returns a list of all searchable properties in the index. It receives the index as the only argument. * `getSearchablePropertiesWithTypes`: A function that returns an object where keys are the searchable properties in the index and the values are the type of the index for a property. It receives the index as the only argument. * `load`: A function that deserializes an index from a JavaScript object. It receives The document IDs mapper and a JavaScript object as its only argument and must return an index. * `save`: A function that serializes the index into a JavaScript object. It receives the index as the only argument and must return a JavaScript object. The following functions are optional: * `beforeInsert` or `afterInsert`: Functions invoked before or after `insert`. They accept the same arguments as `insert` except the first one. * `beforeRemove` or `afterRemove`: Functions invoked before or after `remove`. They accept the same arguments as `remove` except the first one. For the more formal interface information, look for the `IIndex` interface in `src/types.ts` in Orama's source code. The Orama's default index is based on BM25, Radix Trees and AVL trees. All its functions are exported via `@orama/orama/components` and can be composed to create a custom index: ```javascript import { create } from "@orama/orama"; import { index as defaultIndex } from "@orama/orama/components"; const index = await defaultIndex.createIndex(); const movieDB = create({ schema: { title: "string", director: "string", }, components: { // This index will only customize the deserialization index: { ...index, load(documentsIdsMapper, raw) { // Do something here }, }, }, }); ``` ### `documentsStore` The documentsStore component is used to store the documents in Orama. To customize the documents store used by Orama, provide an object which has at least the following properties: * `create`: A function that creates a new document store. It receives the following arguments: * `orama`: The Orama instance. * `mapper`: The document IDs mapper (see Internal components section below). * `get`: A function that returns a document from the store. It receives the following arguments: * The documents store. * The ID of the document to get. * `getAll`: A function that returns all documents from the store. Note that the IDs in the returned object are the mapped IDs from the mapper component. It receives the following arguments: * The documents store. * `getMultiple`: A function that returns multiple documents from the store. It receives the following arguments: * The documents store. * A list of IDs of the documents to get. * `getAll`: A function that returns all the documents from the store. It receives the following arguments: * The documents store. * `store`: A function that stores a new document in the documents store. It receives the following arguments: * The documents store. * The ID of the new document to store. * The document to store. * `remove`: A function that removes a document from the documents store. It receives the following arguments: * The documents store. * The ID of the new document to remove. * `count`: A function that returns the count of the documents currently stored. It receives the current documents store as the only argument. * `load`: A function that deserializes a documents store from a JavaScript object. It receives The document IDs mapper and a JavaScript object as its only argument and must return a documents store. * `save`: A function that serializes the documents store into a JavaScript object. It receives the current documents store as the only argument and must return a JavaScript object. For the more formal interface information, look for the `IDocumentsStore` interface in `src/types.ts` in Orama's source code. The Orama's default documents store is based on simple JavaScript object. All its functions are exported via `@orama/orama/components` and can be composed to create a custom documents store: ```javascript import { create } from "@orama/orama"; import { documentsStore as defaultDocumentsStore } from "@orama/orama/components"; const store = await defaultDocumentsStore.createDocumentsStore(); const movieDB = create({ schema: { title: "string", director: "string", }, components: { // override partially the default documents store documentsStore: { ...store, remove(s, id) { // Apply custom logic return store.remove(s, id); }, }, }, }); ``` ### `sorter` The sorter component is used to store the documents in Orama. To customize the documents sort used by Orama, provide an object which has at least the following properties: * `create`: A function that creates a new sorter. It receives the following arguments: * `mapper`: The document IDs mapper (see Internal components section below). * `schema`: The documents schema. * `configuration`: The sorter configuration. * `insert`: A function that inserts a new document in the sorter. It receives the following arguments: * `sorter`: The sorter returned by the `create` function. * `prop`: The property that is currently considered. * `id`: The ID of the document being inserted. * `value`: The value of the property in the document. * `schemaType`: The type of the property in the document according with the sort schema. * `language`: The language of the document. * `remove`: A function that removes a document from the index. It receives the following arguments: * `sorter`: The sorter returned by the `create` function. * `prop`: The property that is currently considered. * `id`: The ID of the document being inserted. * `sortBy`: A function that inserts document information into the index for future results score calculation. It should be typically invoked within `insert`. It receives the following arguments: * `sorter`: The sorter returned by the `create` function. * `docIds`: A \[string, number] array contains for each id the weight. * `by`: The SortParameters specified during the search * `getSortableProperties`: A function that returns a list of all sortable properties in the sorter. It receives the index as the only argument. * `getSortablePropertiesWithTypes`: A function that returns an object where keys are the sortable properties in the sorter and the values are the type of the sort for a property. It receives the index as the only argument. * `load`: A function that deserializes a sorter from a JavaScript object. It receives The document IDs mapper and a JavaScript object as its only argument and must return a sorter. * `save`: A function that serializes the sorter into a JavaScript object. It receives the sorter as the only argument and must return a JavaScript object. ```javascript import { create } from "@orama/orama"; import { sorter as defaultSorter } from "@orama/orama/components.js"; const s = await defaultSort.createSorter(); const db = create({ schema: { number: "number", }, components: { sorter: { // override partially the default sorter ...s, async remove(sort, prop, id) { // Apply custom logic here return s.remove(sort, prop, id); }, }, }, }); ``` ### General purpose components The components in this category are simple functions which are internally used by Orama. Depending on the use case the component must return a value. Orama will await if a Promise is returned. ### `validateSchema` The component is used to validate a document against the schema. The function should return `undefined` if the document is valid according to the schema, the path of the invalid property otherwise. The function will receive two arguments: * The document that is being validated. * The schema provided to `create`. ```javascript import { create, insert } from "@orama/orama"; const movieDB = create({ schema: { title: "string", director: "string", }, components: { validateSchema(doc) { return typeof doc.name === "string" && typeof doc.director === "string"; }, }, }); // This will throw insert(movieDB, { title: "Harry Potter and the Philosopher's Stone", director: 42, }); ``` ### `getDocumentIndexId` The component is used to extract or generate a unique ID for a document. The returned value must be string. The function will receive one argument: * The document for which an ID is being generated. ```javascript import { create, insert } from "@orama/orama"; const movieDB = create({ schema: { title: "string", director: "string", }, components: { getDocumentIndexId(doc) { return doc.id ?? Date.now().toString(); }, afterInsert(_orama, _doc, id) { console.log(id); }, }, }); // This will print something like "1679476550629" insert(movieDB, { title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", }); ``` ### `getDocumentProperties` The component is used to extract indexable properties from a document. The function receives two arguments: * The document that is being read. * A list of properties paths (using dotted syntax) to extract. The function must return an object where the keys are the paths received as argument. ```javascript import { create } from "@orama/orama"; import { get } from "lodash/get"; const movieDB = create({ schema: { title: "string", director: "string", }, components: { getDocumentProperties(doc, paths) { return Object.fromEntries( paths.map((path) => { return [path, get(doc, path)]; }) ); }, }, }); ``` ### `formatElapsedTime` The component is used to format the `elapsed` property in the search results. The return value can be a `number`, a `string` or an `object`. The function receives a single argument: the search elapsed time as BigInt. ```javascript import { create, insert, search } from "@orama/orama"; const movieDB = create({ schema: { title: "string", director: "string", plot: "string", year: "number", isFavorite: "boolean", }, components: { formatElapsedTime(n) { return `${Number(n)} - custom`; }, }, }); insert(movieDB, { title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", plot: "Harry Potter, an eleven-year-old orphan, discovers that he is a wizard and is invited to study at Hogwarts. Even as he escapes a dreary life and enters a world of magic, he finds trouble awaiting him.", year: 2001, isFavorite: false, }); const results = search(movieDB, { term: "Harry" }); // This will print something like: 100 - custom console.log(results.elapsed); ``` ## Internal components ### Documents IDs mapper In order to improve performance, Orama uses an internal ID for each document. The documents IDs mapper component is used to maintain a between the user document ID and the Orama document ID. This component is internal and cannot be replaced by the developer. The component is passed to the customizable components (like `documentsStore`) and must be treated as an opaque object. When writing or reading documents in the `data` section of The Orama instance, such as `orama.data.index`, make sure you always use a internal ID. Orama exports two helpers which will help dealing with this operations: * `getInternalDocumentId`: A function that receives a documents mapper object and an external document ID and returns an internal document ID. * `getDocumentIdFromInternalId`: A function that receives a documents mapper object and an internal document ID and returns an external document ID. ## Extending Orama As Orama is an Open Source Software, we gladly accept proposal for new functionalities. This obviously include the definition of new components which are used internally by Orama and that can be customized by the users. ### Step 1: Define a new interface If you want to create a new component, you first have to define your component in the `ObjectComponents` or `FunctionComponents` interfaces in `src/types.ts`. In case of object components, the definition should be a new interface defined in the same file. As convention, start the interface with the letter `I`. Example: ```diff + interface IShiningDetector { + isShining(): SyncOrAsyncValue + } export interface ObjectComponents { tokenizer: Tokenizer | TokenizerConfig index: IIndex documentsStore: IDocumentsStore + shiningDetector: IShiningDetector } ``` Remember that all functions used in Orama's components can be async, so we advise to use the `SyncOrAsyncValue` for their return value. This also implies that when you invoke this function you should always use `await` to make sure the function is correctly handled whether it is async or not. ### Step 2: Define and store data in the database If your component needs to store some data in the orama database, you have to add a new field in the `Data` interface in `src/types.ts`. To avoid compatibility problems, only modify the data in the new field you define. Any component functions should accept the data as first argument instead of the full database if possible. Example: ```diff + export interface IShiningDetector { + isShining(shining: Record): SyncOrAsyncValue + } export interface ObjectComponents { tokenizer: Tokenizer | TokenizerConfig index: IIndex documentsStore: IDocumentsStore + shiningDetector: IShiningDetector } interface Data { index: I docs: D + shining: Record } ``` ### Step 3: Define your component default implementation Create a file (or folder if appropriate) under the `src/components` folder with appropriate exports. Remember to export both individual functions and the default object (or creation factory function) to allow users to compose different components. Example (`../components/shiningDetector.js`): ```diff + import { IShiningDetector } from '../src/types.js' + + export function isShining(shining: Record, subject: string): number { + return shining[subject] ?? 0 + } + + export function createShiningDetector(): IShiningDetector { + return { isShining } + } + ``` Note that exporting a `create*` factory function is not strictly needed but it helps to isolate initialization tasks. ### Step 4: Update the `create` method Update the `create` method in `src/methods/create.ts` to use the component provided in the options, or create a new one using the object or factory function defined in the previous step. If you added a field to the `Data` interface, also provide the initial value. Example: ```diff import { createShiningDetector } from '../components/shiningDetector.js' export async function create({ schema, language, components }: CreateArguments): Promise { // ... const orama = { data: {}, caches: {}, // ... + shiningDetector: components.shiningDetector ?? createShiningDetector() } as Orama orama.data = { index: await orama.index.create(orama, schema), docs: await orama.documentsStore.create(orama), shining: { Paolo: 10 } } ``` ### Step 5: Have fun! Call your component's functions where appropriate! Yes, we know the example below is completely useless and silly. 😁 Example: ```diff export async function search(orama: Orama, params: SearchParams, language?: string): Promise { + console.log('Shining level', await orama.shiningDetector.isShining(params.term)) // ... ``` # orama-js: Utilities URL: /docs/orama-js/internals/utilities Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/internals/utilities.mdx Orama exposes some of its internal utility functions. *** title: Utilities description: Orama exposes some of its internal utility functions. ------------------------------------------------------------------ Orama exposes some of its internal utility functions: ```javascript copy import { boundedLevenshtein, sprintf, formatBytes, formatNanoseconds, getNanosecondsTime, uniqueId, } from "@orama/orama/internals"; ``` Every exposed method comes with its own type definition. Each method is an async function. This is mandatory to support usage in CommonJS. ## `boundedLevenshtein` Computes the Levenshtein distance between two strings `(a, b)`, returning early with -1 if the distance is greater than the given tolerance. It assumes that `tolerance >= ||a| - |b|| >= 0`. ```javascript copy import { boundedLevenshtein } from "@orama/orama/internals"; await boundedLevenshtein("moon", "lions", 3); // { isBounded: true, distance: 3 } ``` ## `formatBytes` Takes a `BigInt` as input and returns a human-readable `string`. ```javascript copy import { formatBytes } from "@orama/orama/internals"; await formatBytes(1024); // '1 KB' ``` ## `formatNanoseconds` Takes a `BigInt` as input and returns a human-readable `string`. ```javascript copy import { formatNanoseconds } from "@orama/orama/internals"; await formatNanoseconds(30000n); // '30μs' ``` ## `getNanosecondsTime` Gets the current time with nanoseconds-precision. Returns a `BigInt`. ```javascript copy import { getNanosecondsTime } from "@orama/orama/internals"; await getNanosecondsTime(); // 1363500821581208n ``` ## `uniqueId` Returns a uniqueId for a document as a `string`. ```javascript copy import { uniqueId } from "@orama/orama/internals"; await uniqueId(); // 37149225-243 ``` # TypeScript If you cannot use set `moduleResolution` to `nodenext` or `node16` in the `tsconfig.json`, you can import internals directly from the main entrypoint: ```typescript import { internals } from "@orama/orama"; await internals.boundedLevenshtein(); ``` # orama-js: Plugin system URL: /docs/orama-js/plugins Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/index.mdx Learn how to extend Orama with plugins. *** title: Plugin system description: Learn how to extend Orama with plugins. ---------------------------------------------------- Since the beginning of the project, Orama has been developed thinking of a simple way to add new features via a plugin system, leaving the core as small as possible. As for `v2.0.0`, Orama introduces the concept of **Plugins** in the core of the project, so that anyone could interact with the core operations of the search engine by providing their own custom features and plugins. That said, the Orama core team is working on some official plugins to support specific features that might be useful for the general public. That way, you will be able to bring in only the features you really need. All the official plugins' source code is hosted under the [Orama](https://github.com/oramasearch/orama) monorepo, in the [`packages`](https://github.com/oramasearch/orama/tree/main/packages) directory. If you want to learn more about writing your own plugins, read the guide [available here](/docs/orama-js/plugins/writing-your-own-plugins). # orama-js: Plugin Analytics URL: /docs/orama-js/plugins/plugin-analytics Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-analytics.mdx Learn how to use the Analytics plugin in Orama. *** title: Plugin Analytics description: Learn how to use the Analytics plugin in Orama. ------------------------------------------------------------ This plugin relies on [Orama Cloud](https://app.orama.com) (free plan). ## Installation First of all, install it via npm (or any other package manager of your choice): ```bash npm install @orama/plugin-analytics ``` Then, add it to your Orama configuration: ```js import { Orama } from '@orama/core'; import { pluginAnalytics } from '@orama/plugin-analytics'; const db = create({ schema: { name: 'string' } as const, plugins: [ pluginAnalytics({ apiKey: 'your-api-key', indexId: 'your-index-id', }) ] }) insertMultiple(db, [ { name: 'foo' }, { name: 'bar' }, { name: 'baz' }, ]) // Collects anonymous analytics data and sends it to Orama Cloud search(db, { term: 'foo' }) ``` ## Disabling Analytics By default, Orama is shipped without analytics plugin. If you want to enable it, you need to explicitly add it to your configuration as described above. Anyway, you can disable it by passing `enabled: false` to the `pluginAnalytics` function in your configuration, like this: ```js import { Orama } from '@orama/core'; import { pluginAnalytics } from '@orama/plugin-analytics'; const db = create({ schema: { name: 'string' } as const, plugins: [ pluginAnalytics({ apiKey: 'your-api-key', indexId: 'your-index-id', enabled: false, // <--- disable analytics }) ] }) ``` This flag is useful when you want to disable analytics in development environment, for example. # orama-js: Plugin Astro URL: /docs/orama-js/plugins/plugin-astro Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-astro.mdx Learn how to use the Astro plugin in Orama. *** title: Plugin Astro description: Learn how to use the Astro plugin in Orama. -------------------------------------------------------- The plugin-astro plugin allows you to index the content of your Astro websites and offer text search to your visitors. ## Installation You can install the plugin using any major Node.js package manager. ```bash copy npm install @orama/plugin-astro ``` ## Usage There are two main aspects to consider when using this plugin: * DB generation * Loading DBs & performing searches ```js import orama from "@orama/plugin-astro"; // https://astro.build/config export default defineConfig({ integrations: [ orama({ // We can generate more than one DB, with different configurations mydb: { // Required. Only pages matching this path regex will be indexed pathMatcher: /blog\/[0-9]{4}\/[0-9]{2}\/[0-9]{2}\/.+$/, // Optional. 'english' by default language: "spanish", // Optional. ['body'] by default. Use it to constraint what is used to // index a page. contentSelectors: ["h1", "main"], }, }), ], }); ``` When running the `astro build` command, a new DB file will be persisted in the `dist/assets` directory. For the particular case of this example, it will be saved in the file `dist/assets/oramaDB_mydb.json`. ### Local development Running Astro with `astro dev` does not [bundle the assets](https://docs.astro.build/en/reference/cli-reference/#astro-dev), including Orama's DBs, causing a "404 - not found" error when trying to load them. In order to use Orama in local Astro development, you can run `astro build` to build your project and then `astro preview` to [serve the built files](https://docs.astro.build/en/reference/cli-reference/#astro-preview) in the `dist/` folder. ## Loading the DB on client-side To use the generated DBs in your pages, you can include a script in your `` section, as the following one: ```html copy ``` For now, the plugin only expose load & search functionality on the client side, but we might expose other Orama features as soon as we stabilise some internal details and public APIs. # orama-js: Plugin Data Persistence URL: /docs/orama-js/plugins/plugin-data-persistence Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-data-persistence.mdx Persist your Orama database to disk or in-memory and restore it later. *** title: Plugin Data Persistence description: Persist your Orama database to disk or in-memory and restore it later. ----------------------------------------------------------------------------------- The `plugin-data-persistence` plugin allows you to persist your Orama database to disk or in-memory and restore it later. ## Installation You can install the plugin using any major Node.js/Bun package manager: ```bash npm install @orama/plugin-data-persistence ``` ## Usage Plugin usage depends on the runtime that you are using, even though the goal is to expose the exact same APIs for browsers, Deno, and all the other JavaScript engines. Let's consider the following Orama instance as a common database source for both browsers and JavaScript engines: ```javascript copy import { create, insert } from "@orama/orama"; const originalInstance = create({ schema: { author: "string", quote: "string", }, }); insert(originalInstance, { quote: "He who is brave is free", author: "Seneca", }); insert(originalInstance, { quote: "Make each day your masterpiece", author: "John Wooden", }); insert(originalInstance, { quote: "You must be the change you wish to see in the world", author: "Mahatma Gandhi", }); ``` ## Persisting the database to disk (in-memory usage) Now we have a Orama instance containing three quotes. We can use the `plugin-data-persistence` plugin to save the database to a file: ```javascript copy import { persist } from "@orama/plugin-data-persistence"; const JSONIndex = await persist(originalInstance, "json"); ``` ## Restore the database from disk (in-memory usage) To restore the database from an in-memory snapshot (created via the `persist` function): ```javascript copy import { search } from "@orama/orama"; import { restore } from "@orama/plugin-data-persistence"; const newInstance = await restore("json", JSONIndex); search(newInstance, { term: "...", }); ``` ## Persisting the database to disk (server usage) The following methods are meant for server-side usage only and will throw an exception when used on browsers and runtimes without a Node.js-compatible `fs` module. Now we have a Orama instance containing three quotes. We can use the `plugin-data-persistence` plugin to save the database to a file: ```javascript copy import { persistToFile } from "@orama/plugin-data-persistence/server"; const filePath = await persistToFile( originalInstance, "binary", "./quotes.msp" ); ``` ## Restore the database from disk (server usage) To restore the database from the disk: ```javascript copy import { restoreFromFile } from "@orama/plugin-data-persistence/server"; const db = await restoreFromFile("binary", filePath); ``` # CommonJS Imports Orama plugins ship **ESM** modules by default. This allows us to move faster when providing new features and bug fixes, as well as using the `"exports"` field in `package.json` to provide a better developer experience. CommonJS imports are still supported, but we suggest you to migrate to ESM. ## TypeScript Set `moduleResolution` in the `compilerOptions` in your `tsconfig.json` to be either `Node16` or `NodeNext`. When importing types, always refer to the standard import: ```ts copy import type { persistToFile } from "@orama/plugin-data-persistence"; ``` # orama-js: Plugin Docusaurus URL: /docs/orama-js/plugins/plugin-docusaurus Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-docusaurus.mdx Learn how to connect Orama Cloud to your Docusaurus project. *** title: Plugin Docusaurus description: Learn how to connect Orama Cloud to your Docusaurus project. ------------------------------------------------------------------------- To guarantee the plugin's correct functionality, you need to have the `@docusaurus/core` package at least in version `3.2.0`. This plugin doesn't support Docusaurus v2. Use [`@orama/plugin-docusaurus`](https://www.npmjs.com/package/@orama/plugin-docusaurus) instead. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-docusaurus-v3 ``` ## Usage To use the plugin you will need to add it to your Docusaurus list of plugins. You can do this by adding the following code to your `docusaurus.config.js` file: ```js plugins: ["@orama/plugin-docusaurus-v3"]; ``` ### Configuration #### Orama Plugins Under the `plugins` Orama configuration object you can add some of the following plugins: ##### Analytics Simply add the following code to your `docusaurus.config.js` file: ```js plugins: [ [ "@orama/plugin-docusaurus-v3", { plugins: { analytics: { enabled: true, apiKey: process.env.ORAMA_ANALYTICS_API_KEY, indexId: process.env.ORAMA_ANALYTICS_INDEX_ID, }, }, }, ], ]; ``` For more information about the props you can use on the analytics plugin, check the [Analytics plugin](/docs/orama-js/plugins/plugin-analytics) section. #### Searchbox & Search Button This plugin uses the [Orama Searchbox](/docs/cloud/ui-components/search-box) for the search functionality. You pass props to the searchbox and/or the search button component by adding the following code to your `docusaurus.config.js` file: ```js plugins: [ [ "@orama/plugin-docusaurus-v3", { searchbox: { placeholder: "Search...", }, searchButton: { text: "Click here to search..." } }, ], ]; ``` For more information about the props you can use on the searchbox, check the [Searchbox](/docs/cloud/ui-components/search-box#usage) section and the [Search Button](/cloud/ui-components/search-button#usage) section. # orama-js: Plugin Embeddings URL: /docs/orama-js/plugins/plugin-embeddings Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-embeddings.mdx Generate embeddings for your documents offline and use them for vector search. *** title: Plugin Embeddings description: Generate embeddings for your documents offline and use them for vector search. ------------------------------------------------------------------------------------------- To perform vector and hybrid search, you need to convert your text data into embeddings. While this [is managed for you with Orama Cloud](/cloud/orama-ai/automatic-embeddings-generation#automatic-embeddings-generation), when using Orama open-source, you need to generate embeddings for your documents on your own. This plugin generates embeddings for your documents at insert and search time, allowing you to perform vector and hybrid searches on your documents. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-embeddings ``` Important note: to use this plugin, you'll also need to install one of the following TensorflowJS backend: * `@tensorflow/tfjs` * `@tensorflow/tfjs-node` * `@tensorflow/tfjs-backend-webgl` * `@tensorflow/tfjs-backend-cpu` * `@tensorflow/tfjs-node-gpu` * `@tensorflow/tfjs-backend-wasm` For example, if you're running Orama on the browser, we highly recommend using `@tensorflow/tfjs-backend-webgl`: ```bash npm install @tensorflow/tfjs-backend-webgl ``` If you're using Orama in Node.js, we recommend using `@tensorflow/tfjs-node`: ```bash npm install @tensorflow/tfjs-node ``` ## Usage Since this plugin will need to generate embeddings for your documents, the search and insert methods are now async. Make sure to use `await` when calling them. This plugin will generate text embeddings for you at insert and search time, allowing you to perform vector and hybrid searches on your documents. ```js import { create, insert, search } from '@orama/orama' import { pluginEmbeddings } from '@orama/plugin-embeddings' import '@tensorflow/tfjs-node' // Or any other appropriate TensorflowJS backend const plugin = await pluginEmbeddings({ embeddings: { // Property used to store generated embeddings. Must be defined in the schema. defaultProperty: 'embeddings', onInsert: { // Generate embeddings at insert-time. // Turn off if you're inserting documents with embeddings already generated. generate: true, // Properties to use for generating embeddings at insert time. // These properties will be concatenated and used to generate embeddings. properties: ['description'], verbose: true, } } }) const db = create({ schema: { description: 'string', // Orama generates 512-dimensions vectors. // When using this plugin, use `vector[512]` as a type. embeddings: 'vector[512]' }, plugins: [plugin] }) // When using this plugin, document insertion becomes async await insert(db, { description: 'The quick brown fox jumps over the lazy dog' }) await insert(db, { description: "I've seen a lazy dog dreaming of jumping over a quick brown fox" }) // When using this plugin, search becomes async const results = await search(db, { term: 'Dreaming of a quick brown fox', mode: 'vector' }) ``` # orama-js: Plugin Match Highlight URL: /docs/orama-js/plugins/plugin-match-highlight Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-match-highlight.mdx Learn how to use the match highlight plugin in Orama. *** title: Plugin Match Highlight description: Learn how to use the match highlight plugin in Orama. ------------------------------------------------------------------ This plugin is deprecated. Use [@orama/highlight](http://npmjs.com/package/@orama/highlight) instead. It's faster and does not increase the memory footprint of your database. The plugin-match-highlight plugin allows Orama to keep track of all token positions on each property of each document. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-match-highlight ``` ## Usage Plugin usage depends on the runtime that you are using, even though the goal is to expose the exact same APIs for browsers, Deno, and all the other JavaScript engines. The plugin exports `afterInsertHook`, which will be the hook used by Orama to add positions, and `searchWithHighlight` which wraps the original Orama's `search` function to return positions alongside docs. ```typescript copy import { create, insert } from "@orama/orama"; import { afterInsert as highlightAfterInsert, searchWithHighlight, } from "@orama/plugin-match-highlight"; // Create a new Orama instance const db = create({ schema: { text: "string", }, plugins: [ // Register the hook { name: "highlight", afterInsert: highlightAfterInsert, }, ], }); // Insert a document insert(db, { text: "hello world" }); // Use the plugin's searchWithHighlight function to query the database const results = searchWithHighlight(db, { term: "hello" }); ``` This will add the `positions` property to each `hit`: ```js copy { elapsed: {...}, count: ..., hits: [ { id: ..., score: ..., document: { text: 'hello world' }, positions: { text: { hello: [ { start: 0, length: 5 } ] } } } ] } ``` # Saving Database State with Highlights Orama exposes a `save` method, which is used to persist its state either in-memory or on disk. \ By default, it doesn't save the highlights exposed by this plugin. If you need them, you can easily handle this case by using the `saveWithHighlight` and `loadWithHighlight` functions: ```js import { create, insert } from "@orama/orama"; import { afterInsert as highlightAfterInsert, saveWithHighlight, loadWithHighlight, } from "@orama/plugin-match-highlight"; const db = create({ schema: { text: "string", }, plugins: [ // Register the hook { name: "highlight", afterInsert: highlightAfterInsert, }, ], }); insert(db, { text: "hello world" }); const savedDB = await saveWithHighlight(db); const restoredDB = await loadWithHighlight(savedDB); ``` # CommonJS Imports Orama plugins ship **ESM** modules by default. This allows us to move faster when providing new features and bug fixes, as well as using the `"exports"` field in `package.json` to provide a better developer experience. CommonJS imports are still supported, but we suggest you to migrate to ESM. ## TypeScript Set `moduleResolution` in the `compilerOptions` in your `tsconfig.json` to be either `Node16` or `NodeNext`. When importing types, always refer to the standard import: ```ts copy import { searchWithHighlight } from "@orama/plugin-match-highlight"; ``` # orama-js: Plugin Nextra URL: /docs/orama-js/plugins/plugin-nextra Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-nextra.mdx Learn how to use the Nextra plugin in Orama. *** title: Plugin Nextra description: Learn how to use the Nextra plugin in Orama. --------------------------------------------------------- [Nextra](https://nextra.site) is a static site generator for Next.js, which provides an easy and effective way to create a blog or a documentation website. Orama provides its own official plugin to be integrated with Nextra. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-nextra ``` ## Usage This plugin takes advantage of the built-in Nextra API to automatically generate the search index depending on the current locale. Therefore, multiple locales are supported out of the box. After the installation via the package manager of your choice, you can import the plugin in your `theme.config.jsx` file: ```js import { OramaSearch } from "@orama/plugin-nextra"; export default { // ... search: { component: OramaSearch, }, // ... }; ``` And that's it! The Orama plugin will do the rest for you. # orama-js: Plugin Parsedoc URL: /docs/orama-js/plugins/plugin-parsedoc Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-parsedoc.mdx Learn how to use the Parsedoc plugin in Orama. *** title: Plugin Parsedoc description: Learn how to use the Parsedoc plugin in Orama. ----------------------------------------------------------- The plugin-parsedoc plugin allows Orama to parse and create indexes from HTML and Markdown documents automatically. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-parsedoc ``` ## Usage Plugin usage depends on the runtime that you are using, even though the goal is to expose the exact same APIs for browsers, Deno, and all the other JavaScript engines. The plugin exports `defaultHtmlSchema` which will be the schema used in the index. Providing a glob pattern to the files containing the documents will index them. ```javascript copy import { create, insert } from "@orama/orama"; import { populateFromGlob, defaultHtmlSchema } from "@orama/plugin-parsedoc"; const db = create({ schema: defaultHtmlSchema, }); await populateFromGlob(db, "docs/**/*.html"); ``` ## API Reference #### `populateFromGlob` An asynchronous function that takes three arguments: * `db`: the database to populate. * `globPath`: a string representing a glob path to reading the files from. * `options`: an object containing the following properties: * `transformFn` (optional): a function that passes an object as its only argument. It contains the raw HTML/Markdown chunk, tag name, parsed content and html attributes. If the function adds a `additionalProperties` object to the trasformed node, it will be merged with the original node's properties. * `mergeStrategy` (optional): a value that defines how to handle consecutive chunks of the same tag. The default value is `merge`. Accepted values are: * `merge`: consecutive chunks with the same tag will be merged into one document for the index. * `split`: consecutive chunks with the same tag will be split into separate documents for the index. * `both`: consecutive chunks with the same tag will be split into separate documents for the index, and also merged into one document for the index. #### `populate` An asynchronous function that takes three arguments. Should be used internally by `populateFromGlob`: * `db`: the database to populate. Should use Orama's native `insert` or `insertBatch` methods internally. * `data`: raw HTML/Markdown string or Buffer. * `fileType`: a string representing the file type. Accepted values are `html` and `md`. * `options`: an object containing the following properties: * `transformFn` (optional): a function that passes an object as its only argument. It contains the raw HTML/Markdown chunk, tag name, parsed content and html attributes. If the function adds a `additionalProperties` object to the trasformed node, it will be merged with the original node's properties. * `mergeStrategy` (optional): a value that defines how to handle consecutive chunks of the same tag. The default value is `merge`. Accepted values are: * `merge`: consecutive chunks with the same tag will be merged into one document for the index. * `split`: consecutive chunks with the same tag will be split into separate documents for the index. * `both`: consecutive chunks with the same tag will be split into separate documents for the index, and also merged into one document for the index. * `basePath` (optional): a string representing the base path of the file. This is used to generate the `id` field in the index # CommonJS Imports Orama plugins ship **ESM** modules by default. This allows us to move faster when providing new features and bug fixes, as well as using the `"exports"` field in `package.json` to provide a better developer experience. CommonJS imports are still supported, but we suggest you to migrate to ESM. ## TypeScript Set `moduleResolution` in the `compilerOptions` in your `tsconfig.json` to be either `Node16` or `NodeNext`. When importing types, always refer to the standard import: ```ts copy import type { populateFromGlob } from "@orama/plugin-data-parsedoc"; ``` # orama-js: Plugin PT15 URL: /docs/orama-js/plugins/plugin-pt15 Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-pt15.mdx Boost your search results with the PT15 algorithm. *** title: Plugin PT15 description: Boost your search results with the PT15 algorithm. --------------------------------------------------------------- **PT15**, short for **Positional Token 15**, is an indexing and search algorithm that leverages token position within a document to enhance search relevance. It is inspired by [Thomas Wilkerling](https://github.com/ts-thomas)'s work on Flexsearch. The algorithm splits a document into tokens and stores them in 15 predefined positional buckets. Each bucket represents a relative position within the text, scaling based on the document length. When searching, the position of matching tokens is considered, giving higher scores to tokens found in earlier positions. This approach prioritizes the placement of key terms, making the search results more relevant for queries where token order and position matter. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-pt15 ``` ## Usage This plugin will replace the default scoring algorithm (BM25) with the PT15 algorithm, making search faster and the index size smaller. ```js import { create } from '@orama/orama' import { pluginPT15 } from '@orama/plugin-pt15' const db = create({ schema: { title: 'string', description: 'string', rating: 'number', }, plugins: [pluginPT15()], }) ``` And that's it! The Orama plugin will do the rest for you. ## PT15 vs BM25, pros and cons **QPS** and **BM25** are two very different scoring algorithms, and they offer pros and cons depending on the use case. ### PT15 Pros * **Position-Aware Scoring**: PT15 takes into account the position of tokens in a document, allowing for more contextually relevant search results, particularly in cases where the order and placement of words matter (e.g., titles or headlines). * **Efficient Token Search**: By storing tokens in predefined positional buckets, PT15 can quickly search through documents based on both token presence and position, providing fast and efficient search results. * **Boosts Key Positions**: Tokens in earlier or more prominent positions (e.g., the start of a sentence or document) can be given higher importance, which is useful for scoring queries where the position of a term plays a role in relevance. * **Handling of Large Documents**: PT15 scales token positions in documents longer than 15 tokens, ensuring that even long documents are mapped into the 15-position scheme, maintaining efficient indexing regardless of document size. * **Partial Token Matching**: PT15 can handle partial matches by storing token parts (prefixes) within each positional bucket, enabling the algorithm to support queries even if only a part of a token is present. ### PT15 Cons * **Limited to 15 Positions**: With a fixed maximum of 15 positional buckets, PT15 may oversimplify token positioning for longer documents, potentially losing precision for content where token positions beyond the 15th are important. * **Position Bias**: The algorithm inherently gives higher weight to tokens that appear earlier in the document. While beneficial in some cases, this can bias the search results toward the beginning of documents, which may not always reflect true relevance for longer or complex documents. ### How to choose When deciding between PT15 and BM25, consider the nature of your content and the importance of token position in your search queries. Use PT15 when: * You need to prioritize the position of tokens in your search results. * Token order and placement are crucial for relevance. * You want to boost the importance of tokens in specific positions. * You are working with shorter documents or queries where token position is a key factor. BM25 may be more suitable when: * Token position is less critical for relevance. * You are dealing with long-form content or technical documentation. * You need a more balanced approach to scoring that considers multiple relevance factors. * You want to maintain a more traditional scoring algorithm for search results. # orama-js: Plugin QPS URL: /docs/orama-js/plugins/plugin-qps Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-qps.mdx Boost your search results with the Quantum Proximity Scoring algorithm. *** title: Plugin QPS description: Boost your search results with the Quantum Proximity Scoring algorithm. ------------------------------------------------------------------------------------ **QPS**, short for **Quantum Proximity Scoring**, is a new algorithm developed for Orama that enhances document relevance by evaluating token proximity within a document. **QPS** tokenizes the content into distinct segments, or "quantums," and assigns importance to tokens based on their proximity within these quantums. The scoring algorithm boosts documents with closely located matching tokens, calculating a relevance score based on token frequency, proximity, and bitwise operations to improve the accuracy and speed of search results. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-qps ``` ## Usage This plugin will replace the default scoring algorithm (BM25) with the Quantum Proximity Scoring algorithm, making search faster and the index size smaller. ```js import { create } from '@orama/orama' import { pluginQPS } from '@orama/plugin-qps' const db = create({ schema: { title: 'string', description: 'string', rating: 'number', }, plugins: [pluginQPS()], }) ``` And that's it! The Orama plugin will do the rest for you. ## QPS vs BM25, pros and cons **QPS** and **BM25** are two very different scoring algorithms, and they offer pros and cons depending on the use case. ### QPS Pros * **Proximity-Based Scoring**: QPS places significant importance on how close tokens are to each other in a document, which enhances search relevance, especially for queries where context or proximity of terms is important (e.g., "machine learning" vs. "learning machine"). * **Improved Relevance**: By quantizing tokens and evaluating their positions within specific segments, QPS can prioritize documents with tightly related search terms, leading to more relevant search results. * **Efficient for Short, Focused Queries**: QPS can excel in scenarios where queries involve key terms that are contextually important in proximity, making it well-suited for short, specific searches. * **High Accuracy for Exact and Fuzzy Matches**: QPS can handle both exact and approximate matches by adjusting tolerance levels, allowing for fuzzy search capabilities while maintaining high accuracy in results. * **Smaller index size**: QPS doesn't store term frequencies and other metadata, which results in a smaller index size compared to BM25. This also reflects on the memory usage and the speed of the search. ### QPS Cons * **Proximity May Not Always Be Relevant**: In some cases, proximity between tokens may not significantly impact relevance (e.g., for long-form content or technical documentation). QPS might over-prioritize proximity when it's not critical for certain types of searches. * **Limited Effectiveness for Long Queries**: QPS may not perform as well for longer queries where token proximity becomes less important or when there is a need to balance proximity with other relevance factors. ### How to choose We believe that for most applications, **QPS** will provide better search results than **BM25**. However, it's essential to consider the nature of your content and the types of queries you expect to receive. BM25, being a more traditional scoring algorithm, might be more suitable for long-form content or when proximity is not a critical factor in search relevance. On the other hand, QPS can significantly enhance search results for short, focused queries, exact and fuzzy matches, and scenarios where token proximity is essential for relevance. We reccommend trying both algorithms and measuring the results to determine which one works best for your use case. # orama-js: Plugin Secure Proxy URL: /docs/orama-js/plugins/plugin-secure-proxy Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-secure-proxy.mdx Learn how to use the Secure Proxy plugin in Orama. *** title: Plugin Secure Proxy description: Learn how to use the Secure Proxy plugin in Orama. --------------------------------------------------------------- The **Orama Secure Proxy** plugin is an official Orama plugin that allows you to perform vector and hybrid search securely on your browser by masking OpenAI (and other services soon) API keys when generating embeddings. To use this plugin, you will need a free [Orama Cloud](https://app.orama.com) account. If you already have one, follow the [guide](/cloud/orama-ai/orama-secure-proxy) to enable the Orama Secure Proxy before continuing. ## Installation First of all, install it via npm (or any other package manager of your choice): ```bash npm install @orama/plugin-secure-proxy ``` ## Usage Now, when creating a new Orama Instance, make sure to install the plugin: ```js import { create } from "@orama/orama"; import { pluginSecureProxy } from "@orama/plugin-secure-proxy"; const secureProxy = secureProxyPlugin({ apiKey: "YOUR API KEY", defaultProperty: "embeddings", model: "openai/text-embedding-ada-002", }); const db = create({ schema: { title: "string", description: "string", embeddings: "vector[1536]", }, plugins: [secureProxy], }); ``` ## Available models Right now, the Orama Secure Proxy Plugin supports two different models for generating embeddings: | Model name | Provider | Dimensions | | ------------------------------- | -------- | ---------- | | `orama/gte-small` | Orama | 384 | | `orama/gte-medium` | Orama | 768 | | `orama/gte-large` | Orama | 1024 | | `openai/text-embedding-ada-002` | Openai | 1536 | | `openai/text-embedding-3-small` | Openai | 1536 | | `openai/text-embedding-3-large` | Openai | 3072 | ## Running queries Since the plugin needs to call the OpenAI API to generate embeddings, the search methods are now async. Make sure to use `await` when calling them. By telling on which property to perform search by default (in the example above, `'embeddings'`), the plugin will automatically translate your search term into a vector by calling the OpenAI API for you and setting the result into the `vector.value` property. This will finally allow you to perform hybrid and vector search with the exact same APIs used for full-text search. ```js import { search } from '@orama/orama' const resultsHybrid = await search(db, { mode: 'hybrid', term: 'Videogame for little kids with a passion about ice cream' }) const resultsVector = await search(db, { mode: 'vector', term: 'Videogame for little kids with a passion about ice cream' }) ``` ## Specifying the vector property If you have a more complex schema with multiple vector properties, you can always override the vector property to perform search on by using the default `vector` property: ```js const resultsVector = await search(db, { mode: 'vector', term: 'Videogame for little kids with a passion about ice cream', vector: { property: 'myAlternativeProperty' } }) ``` # orama-js: Plugin Vitepress URL: /docs/orama-js/plugins/plugin-vitepress Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/plugin-vitepress.mdx Learn how to use the Vitepress plugin in Orama. *** title: Plugin Vitepress description: Learn how to use the Vitepress plugin in Orama. ------------------------------------------------------------ Vitepress is a Vite & Vue powered static site generator. ## Installation You can install the plugin using any major Node.js package manager. ```bash npm install @orama/plugin-vitepress ``` ## Usage This plugin will look for all the `.md` files in your documentation directory and will automatically index them for you. After the installation via the package manager of your choice, you can import the plugin in your `.vitepress/config.js` file: ```js import { OramaPlugin } from "@orama/plugin-vitepress"; export default { // ... extends: { vite: { plugins: [OramaPlugin()], }, }, }; ``` And that's it! The Orama plugin will do the rest for you. # orama-js: Writing your own plugins URL: /docs/orama-js/plugins/writing-your-own-plugins Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/plugins/writing-your-own-plugins.mdx Learn how to write your own plugins in Orama. *** title: Writing your own plugins description: Learn how to write your own plugins in Orama. ---------------------------------------------------------- With Orama `v2.0.0-beta.5`, we introduced the new plugin system. This allows you to write your own plugins and use them in your Orama project. The Orama plugin system is meant to replace the old hooks system, which is now deprecated. If you had any custom hook, it would be incredibly easy to migrate it to a plugin, as the APIs are 100% backward compatible. ## Creating a plugin A plugin is essentially a JavaScript object with a `name` and a series of hook functions. An example plugin could look like this: ```js function loggerPlugin() { return { name: "logger", beforeSearch: (orama, query) => { console.log( `About to search for ${query.term} on Orama instance ${orama.id}` ); }, }; } ``` And you can then use it in your Orama instance like this: ```js import { create, insert, search } from "@orama/orama"; function loggerPlugin() { return { name: "logger", beforeSearch: (orama, query) => { console.log( `About to search for ${query.term} on Orama instance ${orama.id}` ); }, }; } const orama = create({ id: "my-instance", // Remember, IDs are automatically generated if not provided schema: { name: "string", }, plugins: [loggerPlugin()], }); insert(orama, { name: "John" }); search(orama, { term: "John" }); // Console logs: "About to search for john on Orama instance my-instance" ``` Every plugin should have: * A `name` property, which is a string (**mandatory**) * Any hook function you want to use (**optional**) When working with `async` hooks remember to always use the `async` keyword modifier. ```js async function afterInsertPluginExample() { return { name: "after-insert-plugin", afterInsert: async (orama, id, document) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); console.log("Document inserted: ", document); }, }; } ``` ## Plugin hooks With `v2.0.0-beta.5`, we essentially moved the hooks from the `components` property of the Orama instance to the `plugins` property. The available hooks are: * [`beforeInsert`](#beforeinsert) * [`afterInsert`](#afterinsert) * [`beforeRemove`](#beforeremove) * [`afterRemove`](#afterremove) * [`beforeUpdate`](#beforeupdate) * [`afterUpdate`](#afterupdate) * [`beforeSearch`](#beforesearch) * [`afterSearch`](#aftersearch) * [`beforeInsertMultiple`](#beforeinsertmultiple) * [`afterInsertMultiple`](#afterinsertmultiple) * [`beforeRemoveMultiple`](#beforeremovemultiple) * [`afterRemoveMultiple`](#afterremovemultiple) * [`beforeUpdateMultiple`](#beforeupdatemultiple) * [`afterUpdateMultiple`](#afterupdatemultiple) ### `beforeInsert` Runs before an insert operation. Receives the Orama instance, the document ID, and the entire document to be inserted as arguments. ```js function beforeInsertPluginExample() { return { name: "before-insert-plugin", beforeInsert: (orama, id, document) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); console.log("Document to be inserted: ", document); }, }; } ``` ### `afterInsert` Runs after an insert operation. Receives the Orama instance, the document ID, and the entire document as arguments. ```js function afterInsertPluginExample() { return { name: "after-insert-plugin", afterInsert: (orama, id, document) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); console.log("Document inserted: ", document); }, }; } ``` ### `beforeRemove` Runs before a remove operation. Receives the Orama instance and the document ID to be removed as arguments. ```js function beforeRemovePluginExample() { return { name: "before-remove-plugin", beforeRemove: (orama, id) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); }, }; } ``` ### `afterRemove` Runs after a remove operation. Receives the Orama instance and the document ID removed as arguments. ```js function afterRemovePluginExample() { return { name: "after-remove-plugin", afterRemove: (orama, id) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); }, }; } ``` ### `beforeUpdate` Runs before an update operation. Receives the Orama instance and the document ID to be updated as arguments. ```js function beforeUpdatePluginExample() { return { name: "before-update-plugin", beforeUpdate: (orama, id) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); }, }; } ``` ### `afterUpdate` Runs after an update operation. Receives the Orama instance and the document ID as arguments. ```js function afterUpdatePluginExample() { return { name: "after-update-plugin", afterUpdate: (orama, id) => { console.log("Orama instance: ", orama); console.log("Document id: ", id); }, }; } ``` ### `beforeSearch` Runs before a search operation. Receives the Orama instance and the query object as arguments. ```js function beforeSearchPluginExample() { return { name: "before-search-plugin", beforeSearch: (orama, query) => { console.log("Orama instance: ", orama); console.log("Query: ", query); }, }; } ``` ### `afterSearch` Runs after a search operation. Receives the Orama instance, the query object, and the search results as arguments. ```js function afterSearchPluginExample() { return { name: "after-search-plugin", afterSearch: (orama, query, result) => { console.log("Orama instance: ", orama); console.log("Query: ", query); console.log("Search result: ", result); }, }; } ``` ### `beforeInsertMultiple` Runs before an `insertMultiple` operation. Receives the Orama instance and the documents to be inserted as arguments. Since the `insertMultiple` operation is calling the `insert` operation underneath, the `beforeInsertMultiple` hook will be called for each document. \ If there's an existing `beforeInsert` hook in the plugin, it will be called for each document as well. ```js function beforeInsertMultiplePluginExample() { return { name: "before-insert-multiple-plugin", beforeInsertMultiple: (orama, documents) => { console.log("Orama instance: ", orama); console.log("Documents to be inserted: ", documents); }, }; } ``` ### `afterInsertMultiple` Runs after an `insertMultiple` operation. Receives the Orama instance and the documents inserted as arguments. Since the `insertMultiple` operation is calling the `insert` operation underneath, the `afterInsertMultiple` hook will be called for each document. \ If there's an existing `afterInsert` hook in the plugin, it will be called for each document as well. ```js function afterInsertMultiplePluginExample() { return { name: "after-insert-multiple-plugin", afterInsertMultiple: (orama, documents) => { console.log("Orama instance: ", orama); console.log("Documents inserted: ", documents); }, }; } ``` ### `beforeRemoveMultiple` Runs before a `removeMultiple` operation. Receives the Orama instance and the IDs of the documents to be removed as arguments. Since the `removeMultiple` operation is calling the `remove` operation underneath, the `beforeRemoveMultiple` hook will be called for each document. \ If there's an existing `beforeRemove` hook in the plugin, it will be called for each document as well. ```js function beforeRemoveMultiplePluginExample() { return { name: "before-remove-multiple-plugin", beforeRemoveMultiple: (orama, ids) => { console.log("Orama instance: ", orama); console.log("Document IDs to be removed: ", ids); }, }; } ``` ### `afterRemoveMultiple` Runs after a `removeMultiple` operation. Receives the Orama instance and the IDs of the documents removed as arguments. Since the `removeMultiple` operation is calling the `remove` operation underneath, the `afterRemoveMultiple` hook will be called for each document. \ If there's an existing `afterRemove` hook in the plugin, it will be called for each document as well. ```js function afterRemoveMultiplePluginExample() { return { name: "after-remove-multiple-plugin", afterRemoveMultiple: (orama, ids) => { console.log("Orama instance: ", orama); console.log("Document IDs removed: ", ids); }, }; } ``` ### `beforeUpdateMultiple` Runs before an `updateMultiple` operation. Receives the Orama instance and the IDs of the documents to be updated as arguments. Since the `updateMultiple` operation is calling the `update` operation underneath, the `beforeUpdateMultiple` hook will be called for each document. \ If there's an existing `beforeUpdate` hook in the plugin, it will be called for each document as well. Also, consider that the `update` operation is a shorthand for the `remove` and `insert` operations. \ This means that the `beforeRemove` and `beforeInsert` hooks will be called for each document as well. ```js function beforeUpdateMultiplePluginExample() { return { name: "before-update-multiple-plugin", beforeUpdateMultiple: (orama, ids) => { console.log("Orama instance: ", orama); console.log("Document IDs to be updated: ", ids); }, }; } ``` ### `afterUpdateMultiple` Runs after an `updateMultiple` operation. Receives the Orama instance and the IDs of the documents updated as arguments. Since the `updateMultiple` operation is calling the `update` operation underneath, the `afterUpdateMultiple` hook will be called for each document. \ If there's an existing `afterUpdate` hook in the plugin, it will be called for each document as well. Also, consider that the `update` operation is a shorthand for the `remove` and `insert` operations. \ This means that the `afterRemove` and `afterInsert` hooks will be called for each document as well. ```js function afterUpdateMultiplePluginExample() { return { name: "after-update-multiple-plugin", afterUpdateMultiple: (orama, ids) => { console.log("Orama instance: ", orama); console.log("Document IDs updated: ", ids); }, }; } ``` ## Migrating from hooks to plugins If you were using the old hooks system, you can easily migrate to the new plugin system by moving your hooks to a single plugin. For instance, if you had a `beforeInsert` hook, you can migrate it to a plugin like this: ```diff import { create } from '@orama/orama' const db = create({ schema: { name: 'string' }, - components: { - beforeInsert: (orama, id, document) => { - console.log('Orama instance: ', orama) - console.log('Document id: ', id) - console.log('Document to be inserted: ', document) - } - } + plugins: [ + { + name: 'before-insert-plugin', + beforeInsert: (orama, id, document) => { + console.log('Orama instance: ', orama) + console.log('Document id: ', id) + console.log('Document to be inserted: ', document) + } + } + ] }) ``` Types, behavior, and arguments are 100% backward compatible, so you don't need to change anything else. # orama-js: BM25 Algorithm URL: /docs/orama-js/search/bm25 Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/search/bm25.mdx Learn how Orama uses the BM25 algorithm to calculate the relevance of a document when searching. *** title: BM25 Algorithm description: Learn how Orama uses the BM25 algorithm to calculate the relevance of a document when searching. ------------------------------------------------------------------------------------------------------------- Orama uses the [BM25](https://en.wikipedia.org/wiki/Okapi_BM25) algorithm to calculate the relevance of a document when searching. The BM25 algorithm is a ranking function used in search engines to score and rank documents that are relevant to a given query. It is an improvement over the older TF-IDF algorithm, which also assigns weights to terms based on their frequency, but does not take into account the length of the document or the average length of documents in the corpus. BM25 uses a similar approach, but also incorporates the inverse document frequency of each term, as well as a set of adjustable parameters that can be tuned to improve performance. The result is a more accurate ranking of documents that are relevant to a given query. BM25 has become a popular algorithm for search engine ranking due to its flexibility and effectiveness. It can be adapted to different types of search tasks, from ad-hoc search to recommendation systems, and can be tuned to perform well on specific domains or languages. Additionally, it is computationally efficient and easy to implement, which makes it a practical choice for large-scale search systems. You can edit the BM25 parameters by using the `relevance` property in the `search` configuration object. ```javascript copy const searchResult = search(movieDB, { term: "Chris", properties: ["director"], relevance: { // Term frequency saturation parameter. // Default value: 1.2 // Recommended value: between 1.2 and 2 k: 1.2, // Length normalization parameter. // Default value: 0.75 // Recommended value: > 0.75 b: 0.75, // Frequency normalization lower bound. // Default value: 0.5 // Recommended value: between 0.5 and 1 d: 0.5, }, }); ``` You can learn more about the BM25 algorithm in the [Okapi BM25 Wikipedia](https://en.wikipedia.org/wiki/Okapi_BM25) page. # orama-js: Changing Default Search Algorithm URL: /docs/orama-js/search/changing-default-search-algorithm Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/search/changing-default-search-algorithm.mdx Choosing between BM25, QPS, and PT15 for your search needs. *** title: Changing Default Search Algorithm description: Choosing between BM25, QPS, and PT15 for your search needs. ------------------------------------------------------------------------ Since version `3.0.0`, Orama allows you to change the default search algorithm (**BM25**) with two new plugins: **QPS** (Quantum Proximity Scoring) and **PT15** (Positional Token 15). This guide will help you understand the differences between these algorithms and choose the best one for your search needs. To change the default search algorithm, you can install the **QPS** or **PT15** plugin in your Orama instance: ```js import { create } from '@orama/orama' import { pluginQPS } from '@orama/plugin-qps' import { pluginPT15 } from '@orama/plugin-pt15' const db = create({ schema: { title: 'string', description: 'string', rating: 'number', }, plugins: [ pluginQPS() // or pluginPT15() ], }) ``` And that's it! The Orama plugin will do the rest for you. ## BM25, QPS, and PT15: A Comparison Each algorithm has its unique approach to scoring and relevance, making them suitable for different search tasks. Here's a comparison of the three algorithms to help you choose the best one for your use case. ### BM25 (Best Matching 25) BM25 (Best Matching 25) is a probabilistic information retrieval algorithm developed by Stephen E. Robertson and Karen Spärck Jones in the 1990s. It's a ranking function that scores documents based on the frequency of query terms (term frequency), the rarity of terms across the document set (inverse document frequency), and the length of the document. BM25 is widely used in search engines and information retrieval systems due to its ability to provide relevant results by balancing term frequency and document length, ensuring that longer documents aren't unfairly penalized. * **Primary Focus**: Term frequency and document length normalization. * **Scoring Method**: Based on term frequency (TF) and inverse document frequency (IDF). * **Proximity Consideration**: No direct proximity calculation. * **Relevance Criteria**: Depends on term frequency, document length, and rarity of terms. * **Handling Long Documents**: Performs well, handles documents of any length. * **Token Matching**: Exact match by default, supports fuzzy matching via typo tolerance and stemming. * **Complexity**: Moderate; widely implemented in search systems. * **Memory Usage**: Low to moderate. * **Performance**: Fast and efficient, especially for large datasets. * **Ideal Use Case**: General-purpose search with term weighting. * **Strengths**: Simple, fast, and effective for most search tasks. * **Weaknesses**: Ignores token proximity, which may reduce relevance for proximity-sensitive searches. ### QPS (Quantum Proximity Scoring) QPS (Quantum Proximity Scoring) is a search algorithm developed by the Orama team in 2024. It was designed to improve search performance, particularly in browser-based environments and memory-constrained systems. QPS enhances relevance by focusing on the proximity of search terms within documents, assigning higher scores to terms that appear close together. By leveraging token proximity and efficient memory usage, QPS is optimized for fast and relevant search results in scenarios where resource limitations, such as memory and processing power, are a concernm like web browsers and edge networks. * **Primary Focus**: Token proximity and quantization. * **Scoring Method**: Based on the proximity of tokens and their occurrence within predefined "quantums." * **Proximity Consideration**: High priority on token proximity. * **Relevance Criteria**: Documents with closely located matching tokens are prioritized. * **Handling Long Documents**: Handles long documents while maintaining proximity-based relevance. * **Token Matching**: Supports exact and proximity-based fuzzy matching. * **Complexity**: High, due to quantum and bitmask operations. * **Memory Usage**: Higher, as it stores token proximity data. * **Performance**: Efficient for proximity-focused queries but may incur overhead. * **Ideal Use Case:** Proximity-sensitive searches where token location within text is crucial. * **Strengths**: High relevance for queries involving closely related tokens. * **Weaknesses**: May over-prioritize proximity when it's not critical, and could increase memory overhead. ### PT15 (Positional Token 15) PT15 (Positional Token 15) is a search algorithm developed by the Orama team in 2024, inspired by Thomas Wilkerling's work on Flexsearch. The algorithm prioritizes the position of tokens within a document, assigning higher relevance to terms that appear earlier or in key positions. PT15 uses a fixed set of 15 positional buckets to store tokens, allowing it to efficiently track token locations. Designed for performance in structured text searches, PT15 is particularly effective for cases where the order and placement of terms play a crucial role in determining relevance. * **Primary Focus**: Token position within a document. * **Scoring Method**: Tokens are stored and scored based on their relative position in 15 fixed buckets. * **Proximity Consideration**: Indirect; focuses on token positions rather than proximity between tokens. * **Relevance Criteria**: Tokens in earlier positions are given higher scores. * **Handling Long Documents**: Scales positions for documents longer than 15 tokens but loses some granularity. * **Token Matching**: Supports partial token matching (prefixes). * **Complexity**: Moderate, with fixed positional storage. * **Memory Usage**: Moderate due to the use of 15-position storage. * **Performance**: Fast for small to medium datasets, but may slow down with very long documents. * **Ideal Use Case** Position-sensitive searches where token placement is important (e.g., titles, structured text). * **Strengths**: Prioritizes key positions in a document, making it effective for structured queries. * **Weaknesses**: Limited to 15 positional buckets, which may oversimplify token positions in longer documents. ## How to choose At Orama, we developed **QPS** after a long process of evaluation and observation on the usage of **BM25**. We believe that it could be extremely beneficial for most applications, especially for product documentation, e-commerce, and content management systems. **PT-15**, inspired by [Thomas Wilkerling](https://github.com/ts-thomas)'s work on Flexsearch, is another good algorithm to consider for its efficiency and position-aware scoring. While **BM25** is an industry standard and a reliable choice for general-purpose search, **QPS** and **PT15** offer unique advantages especially when used in a browser or memory-constrained environment, where speed and memory usage are critical. Before taking a final decision on which algorithm to use, we recommend testing each one with your dataset and queries to see which one provides the best results for your specific use case. If you want to know what search queries do your users perform, you can install the [Search Analytics plugin](/docs/orama-js/plugins/plugin-analytics) to track and analyze search queries in your Orama instance, for free. # orama-js: Facets URL: /docs/orama-js/search/facets Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/search/facets.mdx Learn how to use facets in Orama search engine. *** title: Facets description: Learn how to use facets in Orama search engine. ------------------------------------------------------------ Facets are a powerful tool for filtering and narrowing down search results on the Orama search engine. With the Orama Faceted Search API, users can filter their search results by various criteria, such as category, price range, or other attributes, making it easier to find the information they need. Whether you're building a website, mobile app, or any other application, the Orama Faceted Search API is the perfect solution for adding faceted search functionality to your project. Given the following Orama schema: ```js import { create } from "@orama/orama"; const db = create({ schema: { title: "string", description: "string", categories: { primary: "string", secondary: "string", }, rating: "number", isFavorite: "boolean", }, }); ``` Orama will be able to generate facets at search-time based on the schema. To do so, we need to specify the `facets` property in the `search` configuration: ```js const results = search(db, { term: "Movie about cars and racing", properties: ["description"], facets: { "categories.primary": { limit: 3, order: "DESC", }, "categories.secondary": { limit: 2, order: "DESC", }, rating: { ranges: [ { from: 0, to: 3 }, { from: 3, to: 7 }, { from: 7, to: 10 }, ], }, isFavorite: { true: true, false: true, }, }, }); ``` This will generate the following result: ```js { elapsed: ..., count: ..., hits: { ... }, facets: { 'categories.first': { count: 14, values: { 'Action': 4, 'Adventure': 3, 'Comedy': 2, } }, 'categories.second': { count: 14, values: { 'Cars': 4, 'Racing': 3, } }, rating: { count: 3, values: { '0-3': 5, '3-7': 15, '7-10': 80, } }, isFavorite: { count: 2, values: { 'true': 5, 'false': 95, } }, } } ``` As you may have noticed, the `facets` property is an `object` that contains different configurations depending on the property type specified in the schema. ## String facets If a property is specified as `string` in the schema, the facet will accept the following configuration: | Property | Type | Default | Description | | -------- | -------- | ------- | --------------------------------------------------- | | `order` | `string` | `DESC` | Order of the values. Can be either `ASC` or `DESC`. | | `limit` | `number` | `10` | Maximum number of values to return. | | `offset` | `number` | `0` | Number of values to skip. | In the search result, `string` facets will be returned as an `object` with the following properties: ```js { count: 14, // Total number of values, now limited to 3 (size) values: { 'Action': 4, // Number of documents that have this value 'Adventure': 3, // Number of documents that have this value 'Comedy': 2, // Number of documents that have this value } } ``` ## Number facets If a property is specified as `number` in the schema, the facet will accept the following configuration: | Property | Type | Default | Description | | -------- | ------- | ------- | ---------------------------- | | `ranges` | `array` | `[]` | Array of ranges to consider. | Each range is an `object` with the following properties: | Property | Type | Description | | -------- | -------- | --------------------------- | | `from` | `number` | Minimum value of the range. | | `to` | `number` | Maximum value of the range. | In the search result, `number` facets will be returned as an `object` with the following properties: ```js { count: 3, // Total number of ranges values: { '0-3': 5, // Number of documents that have a value between 0 and 3 (inclusive) '3-7': 15, // Number of documents that have a value between 3 and 7 (inclusive) '7-10': 80, // Number of documents that have a value between 7 and 10 (inclusive) } } ``` Please note that the `from` and `to` values are **inclusive**. Note also that the order of the ranges is guaranteed as specified in the configuration. ## Boolean facets If a property is specified as `boolean` in the schema, the facet will accept the following configuration: | Property | Type | Default | Description | | -------- | --------- | ------- | ----------------------------------- | | `true` | `boolean` | `true` | Whether to consider `true` values. | | `false` | `boolean` | `true` | Whether to consider `false` values. | In the search result, `boolean` facets will be returned as an `object` with the following properties: ```js { count: 2, // Total number of values values: { 'true': 5, // Number of documents that have a `true` value 'false': 95, // Number of documents that have a `false` value } } ``` ## Enum facets If a property is specified as `enum` in the schema, no configuration is required. In the search result, `enum` facets will be returned as an `object` with the following properties: ```js { count: 9, // Total number of values values: { 'Action': 4, // Number of documents that have this value 'Adventure': 3, // Number of documents that have this value 'Comedy': 2, // Number of documents that have this value } } ``` ## How facets works on array fields Orama treats each array element as a single element of the facet: ```javascript copy const db = create({ schema: { name: "string[]", }, }); insert(db, { name: ["Albus", "Percival Wulfric Brian"], }); const results = search(db, { facets: { name: {}, }, }); ``` `result.facets` is the following object: ```json { "count": 2, "values": { "Albus": 1, "Percival Wulfric Brian": 1 } } ``` # orama-js: Fields Boosting URL: /docs/orama-js/search/fields-boosting Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/search/fields-boosting.mdx Learn how to boost the importance of a field in the search results. *** title: Fields Boosting description: Learn how to boost the importance of a field in the search results. -------------------------------------------------------------------------------- You can use the `boost` interface to boost the importance of a field in the search results. ```javascript copy const searchResult = search(movieDB, { term: "Harry", properties: "*", boost: { title: 2, }, }); ``` In this example, we are boosting the `title` field by `2`. That means that any match of `'Harry'` in the `title` field will be considered twice as important as a match in any other field. You can boost multiple fields: ```javascript copy const searchResult = search(movieDB, { term: "Harry", properties: "*", boost: { title: 2, director: 1.5, }, }); ``` In this example, we are boosting the `title` field by `2` and the `director` field by `1.5`. # orama-js: Filters URL: /docs/orama-js/search/filters Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/search/filters.mdx Learn how to use filters in Orama search. *** title: Filters description: Learn how to use filters in Orama search. ------------------------------------------------------ You can use the `filters` interface to filter the search results. Filters are available for numeric, boolean, string, enum, and geopoint properties. Depending on the type of the property, you can use different operators. ## String operators On string properties it performs an exact matching on tokens so it is advised to disable stemming for the properties you want to use filters on (when using the default tokenizer you can provide the `stemmerSkipProperties` configuration property). If we consider the following schema: ```javascript copy const db = create({ schema: { title: "string", tag: "string", }, components: { tokenizer: { stemming: true, stemmerSkipProperties: ["tag"], }, }, }); const results = search(db, { term: "prestige", where: { tag: "new", }, }); ``` The `results` will contain all documents that contain the word `prestige` in the `title` property and have `tags` property equal to `new`. You can also specify a list of string, in this case it will return all documents that contain at least one of the values provided: ```javascript copy const results = search(db, { term: "prestige", where: { tag: ["favorite", "new"], }, }); ``` ## Number operators The number properties support the following operators: | Operator | Description | Example | | --------- | ------------------------------ | --------------------------------- | | `gt` | Greater than | `year: { gt: 2000 }` | | `gte` | Greater than or equal to | `year: { gte: 2000 }` | | `lt` | Less than | `year: { lt: 2000 }` | | `lte` | Less than or equal to | `year: { lte: 2000 }` | | `eq` | Equal to | `year: { eq: 2000 }` | | `between` | Between two values (inclusive) | `year: { between: [2000, 2008] }` | ```javascript copy const db = create({ schema: { id: "string", title: "string", year: "number", meta: { rating: "number", length: "number", favorite: "boolean", tags: "string", }, }, components: { tokenizer: { stemming: true, stemmerSkipProperties: ["meta.tags"], }, }, }); const results = search(db, { term: "prestige", where: { year: { gte: 2000, }, "meta.rating": { between: [5, 10], }, "meta.length": { lte: 60, }, }, }); ``` ## Boolean operators For boolean properties, you can simply set the property to `true` or `false`: ```javascript copy const results = search(db, { term: "prestige", where: { "meta.favorite": true, }, }); ``` ## String\[] | Number\[] | Boolean\[] operators The available operators depend on the type (string, number of boolean) as described in the previous sections. A document matches if at least one of the array elements matches the filter condition. ```javascript copy const db = create({ schema: { title: "string", tags: "string[]", editions: "number[]", limited: "boolean[]", } }); insertMultiple(db, [ {title: "a", tags: ["foo", "bar"], editions: [1990, 2024], limited: [false, false]}, {title: "b", tags: ["foo"], editions: [1942, 2024], limited: [false, true]}, {title: "c", tags: ["bar"], editions: [2020], limited: [false]}, ]) // Books with tag foo search(db, {where: {tags: "foo"}}); // returns a, b // Books tagged either as foo or bar search(db, {where: {tags: ["foo", "bar"]}}); // returns a, b, c // Books with a 2024 edition search(db, {where: {editions: {eq: 2024}}}); // returns a, b // Books with a limited edition search(db, {where: {limited: true}}); // returns b ``` ## Enum operators The enum properties support the following operators: | Operator | Description | Example | | -------- | -------------------------------- | ------------------------------------ | | `eq` | Equal to | `genre: { eq: 'drama' }` | | `in` | Contained in the given array | `genre: { in: ['drama', 'horror'] }` | | `nin` | Not contained in the given array | `genre: { nin: ['comedy'] }` | ## Enum\[] operators The enum properties support the following operators: | Operator | Description | Example | | ------------- | -------------------------------- | -------------------------------------------------------- | | `containsAll` | Contains all the given values | `genre: { containsAll: ['comedy', 'action'] }` | | `containsAny` | Contains any of the given values | `genre: { containsAny: ['comedy', 'action', 'horror'] }` | ## Geosearch Starting from Orama `v2.0.0`, you can perform geosearch queries. Even though the APIs are very simple, we decided to dedicate a separate section for them. This lets us explain the concepts behind the geosearch and how it works with more details. [Read more about geosearch](/docs/orama-js/search/geosearch) # orama-js: Geosearch URL: /docs/orama-js/search/geosearch Source: https://raw.githubusercontent.com/oramasearch/docs/refs/heads/main/content/docs/orama-js/search/geosearch.mdx Learn how to perform geosearch queries in Orama. *** title: Geosearch description: Learn how to perform geosearch queries in Orama. ------------------------------------------------------------- import { Iframe } from "@/lib/components/iframe"; Geosearch is a feature that allows you to filter your search results by distance from a given location, or by bounding box. To perform geosearch queries, you first have to define a new `geopoint` property inside your schema definition when creating your Orama instance: ```javascript import { create } from "@orama/orama"; const db = create({ schema: { name: "string", location: "geopoint", }, }); ``` ## What are geopoints A geopoint is an object with two properties: `lat` and `lon`, which are both numbers. For reference, the following is a valid geopoint: ```javascript { lat: 45.46409, lon: 9.19192 } ``` As you may guess, `lat` stands for latitude, and `lon` for longitude. The values are expressed in degrees, and can be positive or negative. ## Inserting documents with geopoints You can insert documents with geopoints as you would with any other property: ```javascript import { create, insert } from "@orama/orama"; const db = create({ schema: { name: "string", location: "geopoint", }, }); insert(db, { name: "Duomo di Milano", location: { lat: 45.46409, lon: 9.19192 }, }); insert(db, { name: "Piazza Duomo", location: { lat: 45.46416, lon: 9.18945 }, }); insert(db, { name: "Piazzetta Reale", location: { lat: 45.46339, lon: 9.19092 }, }); ``` Given the points above, we can picture them on a map as follows: