Back to the Couchbase homepageCouchbase logo
Couchbase Developer

  • Docs

    • Integrations
    • SDKs
    • Mobile SDKs

    • AI Developer
    • Backend
    • Full-stack
    • Mobile
    • Ops / DBA

    • Data Modeling
    • Scalability

  • Tutorials

    • Developer Community
    • Ambassador Program
  • Sign In
  • Try Free

RAG with DeepSeek using Couchbase Hyperscale and Composite Vector Index

  • Learn how to build a semantic search engine using Couchbase and OpenRouter with Deepseek using Hyperscale and Composite Vector Index.
  • This tutorial demonstrates how to integrate Couchbase's vector search capabilities with OpenRouter Deepseek as both embeddings and language model provider.
  • You'll understand how to perform Retrieval-Augmented Generation (RAG) using LangChain and Couchbase.

View Source

Introduction

In this guide, we will walk you through building a powerful semantic search engine using Couchbase as the backend database and Deepseek V3 as the language model provider (via OpenRouter or direct API) and OpenAI for embeddings. Semantic search goes beyond simple keyword matching by understanding the context and meaning behind the words in a query, making it an essential tool for applications that require intelligent information retrieval. This tutorial is designed to be beginner-friendly, with clear, step-by-step instructions that will equip you with the knowledge to create a fully functional semantic search system using Couchbase Hyperscale and Composite Vector Index from scratch. Alternatively if you want to perform semantic search using the Search Vector Index, please take a look at this.

How to run this tutorial

This tutorial is available as a Jupyter Notebook (.ipynb file) that you can run interactively. You can access the original notebook here.

You can either download the notebook file and run it on Google Colab or run it on your system by setting up the Python environment.

Before you start

Get Credentials for OpenRouter and Deepseek

  • Sign up for an account at OpenRouter to get your API key
  • OpenRouter provides access to Deepseek models, so no separate Deepseek credentials are needed
  • Store your OpenRouter API key securely as it will be used to access the models
  • For Deepseek models, you can use the default models provided by OpenRouter

Create and Deploy Your Free Tier Operational cluster on Capella

To get started with Couchbase Capella, create an account and use it to deploy a forever free tier operational cluster. This account provides you with an environment where you can explore and learn about Capella with no time constraint.

To learn more, please follow the instructions.

Note: To run this tutorial, you will need Capella with Couchbase Server version 8.0 or above as Hyperscale and Composite Vector Index search is supported only from version 8.0

Couchbase Capella Configuration

When running Couchbase using Capella, the following prerequisites need to be met.

  • Create the database credentials to access the required bucket (Read and Write) used in the application.
  • Allow access to the Cluster from the IP on which the application is running.

Setting the Stage: Installing Necessary Libraries

To build our semantic search engine, we need a robust set of tools. The libraries we install handle everything from connecting to databases to performing complex machine learning tasks.

%pip install --no-user --quiet datasets==3.6.0 langchain-couchbase==1.0.1 langchain-deepseek==1.0.1 langchain-openai==1.1.8 python-dotenv==1.2.1
[notice] A new release of pip is available: 25.0.1 -> 26.0.1
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.

Importing Necessary Libraries

The script starts by importing a series of libraries required for various tasks, including handling JSON, logging, time tracking, Couchbase connections, embedding generation, and dataset loading.

import getpass
import json
import logging
import os
import time
from datetime import timedelta

from couchbase.auth import PasswordAuthenticator
from couchbase.cluster import Cluster
from couchbase.exceptions import (CouchbaseException,
                                InternalServerFailureException)
from couchbase.management.buckets import CreateBucketSettings
from couchbase.options import ClusterOptions
from datasets import load_dataset
from dotenv import load_dotenv
from langchain_core.globals import set_llm_cache
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_couchbase.cache import CouchbaseCache
from langchain_couchbase.vectorstores import CouchbaseQueryVectorStore
from langchain_couchbase.vectorstores import DistanceStrategy
from langchain_couchbase.vectorstores import IndexType
from langchain_openai import OpenAIEmbeddings
/Users/kaustavghosh/Desktop/vector-search-cookbook/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

Setup Logging

Logging is configured to track the progress of the script and capture any errors or warnings.

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', force=True)

# Suppress httpx logging
logging.getLogger('httpx').setLevel(logging.CRITICAL)

Environment Variables and Configuration

This section handles loading and validating environment variables and configuration settings:

  1. API Keys:
    • Supports either direct Deepseek API or OpenRouter API access
    • Prompts for API key input if not found in environment
    • Requires OpenAI API key for embeddings

  1. Couchbase Settings:
    • Connection details (host, username, password)
    • Bucket, scope and collection names
    • Vector search index configuration
    • Cache collection settings

The code validates that all required credentials are present before proceeding. It allows flexible configuration through environment variables or interactive prompts, with sensible defaults for local development.

# Load environment variables from .env file if it exists
load_dotenv(override= True)

# API Keys
# Allow either Deepseek API directly or via OpenRouter
DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')
OPENROUTER_API_KEY = os.getenv('OPENROUTER_API_KEY')

if not DEEPSEEK_API_KEY and not OPENROUTER_API_KEY:
    api_choice = input('Choose API (1 for Deepseek direct, 2 for OpenRouter): ')
    if api_choice == '1':
        DEEPSEEK_API_KEY = getpass.getpass('Enter your Deepseek API Key: ')
    else:
        OPENROUTER_API_KEY = getpass.getpass('Enter your OpenRouter API Key: ')

OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') or getpass.getpass('Enter your OpenAI API Key: ')

# Couchbase Settings
CB_HOST = os.getenv('CB_HOST') or input('Enter your Couchbase host (default: couchbase://localhost): ') or 'couchbase://localhost'
CB_USERNAME = os.getenv('CB_USERNAME') or input('Enter your Couchbase username (default: Administrator): ') or 'Administrator'
CB_PASSWORD = os.getenv('CB_PASSWORD') or getpass.getpass('Enter your Couchbase password (default: password): ') or 'password'
CB_BUCKET_NAME = os.getenv('CB_BUCKET_NAME') or input('Enter your Couchbase bucket name (default: query-vector-search-testing): ') or 'query-vector-search-testing'
SCOPE_NAME = os.getenv('SCOPE_NAME') or input('Enter your scope name (default: shared): ') or 'shared'
COLLECTION_NAME = os.getenv('COLLECTION_NAME') or input('Enter your collection name (default: deepseek): ') or 'deepseek'
CACHE_COLLECTION = os.getenv('CACHE_COLLECTION') or input('Enter your cache collection name (default: cache): ') or 'cache'

# Check if required credentials are set
required_creds = {
    'OPENAI_API_KEY': OPENAI_API_KEY,
    'CB_HOST': CB_HOST,
    'CB_USERNAME': CB_USERNAME,
    'CB_PASSWORD': CB_PASSWORD,
    'CB_BUCKET_NAME': CB_BUCKET_NAME
}

# Add the API key that was chosen
if DEEPSEEK_API_KEY:
    required_creds['DEEPSEEK_API_KEY'] = DEEPSEEK_API_KEY
elif OPENROUTER_API_KEY:
    required_creds['OPENROUTER_API_KEY'] = OPENROUTER_API_KEY
else:
    raise ValueError("Either Deepseek API Key or OpenRouter API Key must be provided")

for cred_name, cred_value in required_creds.items():
    if not cred_value:
        raise ValueError(f"{cred_name} is not set")

Connecting to the Couchbase Cluster

Connecting to a Couchbase cluster is the foundation of our project. Couchbase will serve as our primary data store, handling all the storage and retrieval operations required for our semantic search engine. By establishing this connection, we enable our application to interact with the database, allowing us to perform operations such as storing embeddings, querying data, and managing collections. This connection is the gateway through which all data will flow, so ensuring it's set up correctly is paramount.

try:
    auth = PasswordAuthenticator(CB_USERNAME, CB_PASSWORD)
    options = ClusterOptions(auth)
    cluster = Cluster(CB_HOST, options)
    cluster.wait_until_ready(timedelta(seconds=5))
    logging.info("Successfully connected to Couchbase")
except Exception as e:
    raise ConnectionError(f"Failed to connect to Couchbase: {str(e)}")
2026-02-12 10:37:06,755 - INFO - Successfully connected to Couchbase

Setting Up Collections in Couchbase

The setup_collection() function handles creating and configuring the hierarchical data organization in Couchbase:

  1. Bucket Creation:

    • Checks if specified bucket exists, creates it if not
    • Sets bucket properties like RAM quota (1024MB) and replication (disabled)
    • Note: If you are using Capella, create a bucket manually called vector-search-testing(or any name you prefer) with the same properties.
  2. Scope Management:

    • Verifies if requested scope exists within bucket
    • Creates new scope if needed (unless it's the default "_default" scope)
  3. Collection Setup:

    • Checks for collection existence within scope
    • Creates collection if it doesn't exist
    • Waits 2 seconds for collection to be ready

Additional Tasks:

  • Clears any existing documents for clean state
  • Implements comprehensive error handling and logging

The function is called twice to set up:

  1. Main collection for vector embeddings
  2. Cache collection for storing results
def setup_collection(cluster, bucket_name, scope_name, collection_name):
    try:
        # Check if bucket exists, create if it doesn't
        try:
            bucket = cluster.bucket(bucket_name)
            logging.info(f"Bucket '{bucket_name}' exists.")
        except Exception as e:
            logging.info(f"Bucket '{bucket_name}' does not exist. Creating it...")
            bucket_settings = CreateBucketSettings(
                name=bucket_name,
                bucket_type='couchbase',
                ram_quota_mb=1024,
                flush_enabled=True,
                num_replicas=0
            )
            cluster.buckets().create_bucket(bucket_settings)
            time.sleep(2)  # Wait for bucket creation to complete and become available
            bucket = cluster.bucket(bucket_name)
            logging.info(f"Bucket '{bucket_name}' created successfully.")

        bucket_manager = bucket.collections()

        # Check if scope exists, create if it doesn't
        scopes = bucket_manager.get_all_scopes()
        scope_exists = any(scope.name == scope_name for scope in scopes)
        
        if not scope_exists and scope_name != "_default":
            logging.info(f"Scope '{scope_name}' does not exist. Creating it...")
            bucket_manager.create_scope(scope_name)
            logging.info(f"Scope '{scope_name}' created successfully.")

        # Check if collection exists, create if it doesn't
        collection_exists = any(
            scope.name == scope_name and collection_name in [col.name for col in scope.collections]
            for scope in scopes
        )

        if not collection_exists:
            logging.info(f"Collection '{collection_name}' does not exist. Creating it...")
            bucket_manager.create_collection(scope_name, collection_name)
            logging.info(f"Collection '{collection_name}' created successfully.")
        else:
            logging.info(f"Collection '{collection_name}' already exists. Skipping creation.")

        # Wait for collection to be ready
        collection = bucket.scope(scope_name).collection(collection_name)
        time.sleep(2)  # Give the collection time to be ready for queries

        # Clear all documents in the collection
        try:
            query = f"DELETE FROM `{bucket_name}`.`{scope_name}`.`{collection_name}`"
            cluster.query(query).execute()
            logging.info("All documents cleared from the collection.")
        except Exception as e:
            logging.warning(f"Error while clearing documents: {str(e)}. The collection might be empty.")

        return collection
    except Exception as e:
        raise RuntimeError(f"Error setting up collection: {str(e)}")
    
setup_collection(cluster, CB_BUCKET_NAME, SCOPE_NAME, COLLECTION_NAME)
setup_collection(cluster, CB_BUCKET_NAME, SCOPE_NAME, CACHE_COLLECTION)
2026-02-12 10:37:06,766 - INFO - Bucket 'vector-search-testing' exists.
2026-02-12 10:37:06,769 - INFO - Collection 'deepseek' does not exist. Creating it...
2026-02-12 10:37:06,802 - INFO - Collection 'deepseek' created successfully.
2026-02-12 10:37:08,882 - INFO - All documents cleared from the collection.
2026-02-12 10:37:08,882 - INFO - Bucket 'vector-search-testing' exists.
2026-02-12 10:37:08,884 - INFO - Collection 'cache' already exists. Skipping creation.
2026-02-12 10:37:10,891 - INFO - All documents cleared from the collection.





<couchbase.collection.Collection at 0x15ae6dca0>

Creating Embeddings

This section creates an OpenAI embeddings client using the OpenAI API key. The embeddings client is configured to use the "text-embedding-3-small" model, which converts text into numerical vector representations. These vector embeddings are essential for semantic search and similarity matching. The client will be used by the vector store to generate embeddings for documents.

try:
    embeddings = OpenAIEmbeddings(
        api_key=OPENAI_API_KEY,
        model="text-embedding-3-small"
    )
    logging.info("Successfully created OpenAI embeddings client")
except Exception as e:
    raise ValueError(f"Error creating OpenAI embeddings client: {str(e)}")
2026-02-12 10:37:11,001 - INFO - Successfully created OpenAI embeddings client

Understanding Hyperscale and Composite Vector Search

Optimizing Vector Search with Hyperscale and Composite Vector Index

With Couchbase 8.0+, you can leverage the power of query-based vector search, which offers significant performance improvements over Search Vector Index approaches for vector-first workloads. Hyperscale and Composite Vector Index search provides high-performance vector similarity search with advanced filtering capabilities and is designed to scale to billions of vectors.

Hyperscale/Composite vs Search Vector Index: Choosing the Right Approach

Feature Hyperscale/Composite Vector Index Search Vector Index
Best For Vector-first workloads, complex filtering, high QPS performance Hybrid search and high recall rates
Couchbase Version 8.0.0+ 7.6+
Filtering Pre-filtering with WHERE clauses (Composite) or post-filtering (Hyperscale) Pre-filtering with flexible ordering
Scalability Up to billions of vectors (Hyperscale) Up to 10 million vectors
Performance Optimized for concurrent operations with low memory footprint Good for mixed text and vector queries

Query-Based Vector Index Types

Couchbase offers two distinct query-based vector index types, each optimized for different use cases:

Hyperscale Vector Indexes
  • Best for: Pure vector searches like content discovery, recommendations, and semantic search
  • Use when: You primarily perform vector-only queries without complex scalar filtering
  • Features:
    • High performance with low memory footprint
    • Optimized for concurrent operations
    • Designed to scale to billions of vectors
    • Supports post-scan filtering for basic metadata filtering
Composite Vector Indexes
  • Best for: Filtered vector searches that combine vector similarity with scalar value filtering
  • Use when: Your queries combine vector similarity with scalar filters that eliminate large portions of data
  • Features:
    • Efficient pre-filtering where scalar attributes reduce the vector comparison scope
    • Best for well-defined workloads requiring complex filtering using Hyperscale and Composite Vector Index features
    • Supports range lookups combined with vector search

Index Type Selection for This Tutorial

In this tutorial, we'll demonstrate creating a Hyperscale index and running vector similarity queries using Hyperscale and Composite Vector Index. Hyperscale is ideal for semantic search scenarios where you want:

  1. High-performance vector search across large datasets
  2. Low latency for real-time applications
  3. Scalability to handle growing vector collections
  4. Concurrent operations for multi-user environments

The Hyperscale index will provide optimal performance for our OpenAI embedding-based semantic search implementation.

Alternative: Composite Vector Index

If your use case requires complex filtering with scalar attributes, you may want to consider using a Composite Vector Index instead:

## Alternative: Create a Composite index for filtered searches
vector_store.create_index(
    index_type=IndexType.COMPOSITE,
    index_description="IVF,SQ8",
    distance_metric=DistanceStrategy.COSINE,
    index_name="deepseek_composite_index",
)

Use Composite indexes when:

  • You need to filter by document metadata or attributes before vector similarity
  • Your queries combine vector search with WHERE clauses
  • You have well-defined filtering requirements that can reduce the search space

Note: Composite indexes enable pre-filtering with scalar attributes, making them ideal for applications where you need to search within specific categories, date ranges, or user-specific data segments.

Understanding Index Configuration (Couchbase 8.0 Feature)

Before creating our Hyperscale index, it's important to understand the configuration parameters that optimize vector storage and search performance. The index_description parameter controls how Couchbase optimizes vector storage through centroids and quantization.

Index Description Format: 'IVF[<centroids>],{PQ|SQ}<settings>'
Centroids (IVF - Inverted File)
  • Controls how the dataset is subdivided for faster searches
  • More centroids = faster search, slower training time
  • Fewer centroids = slower search, faster training time
  • If omitted (like IVF,SQ8), Couchbase auto-selects based on dataset size
Quantization Options

Scalar Quantization (SQ):

  • SQ4, SQ6, SQ8 (4, 6, or 8 bits per dimension)
  • Lower memory usage, faster search, slightly reduced accuracy

Product Quantization (PQ):

  • Format: PQ<subquantizers>x<bits> (e.g., PQ32x8)
  • Better compression for very large datasets
  • More complex but can maintain accuracy with smaller index size
Common Configuration Examples
  • IVF,SQ8 - Auto centroids, 8-bit scalar quantization (good default)
  • IVF1000,SQ6 - 1000 centroids, 6-bit scalar quantization
  • IVF,PQ32x8 - Auto centroids, 32 subquantizers with 8 bits

For detailed configuration options, see the Quantization & Centroid Settings.

For more information on query-based vector indexes, see Couchbase Vector Index Documentation.

Our Configuration Choice

In this tutorial, we use IVF,SQ8 which provides:

  • Auto-selected centroids optimized for our dataset size
  • 8-bit scalar quantization for good balance of speed, memory usage, and accuracy
  • COSINE distance metric ideal for semantic similarity search
  • Optimal performance for most semantic search use cases

Setting Up the Couchbase Query Vector Store

A vector store is where we'll keep our embeddings. The vector store is specifically designed to handle embeddings and perform similarity searches. When a user inputs a query, the search engine converts the query into an embedding and compares it against the embeddings stored in the vector store. This allows the engine to find documents that are semantically similar to the query, even if they don't contain the exact same words. By setting up the vector store in Couchbase, we create a powerful tool that enables our search engine to understand and retrieve information based on the meaning and context of the query, rather than just the specific words used.

try:
    vector_store = CouchbaseQueryVectorStore(
        cluster=cluster,
        bucket_name=CB_BUCKET_NAME,
        scope_name=SCOPE_NAME,
        collection_name=COLLECTION_NAME,
        embedding = embeddings,
        distance_metric=DistanceStrategy.COSINE
    )
    logging.info("Successfully created vector store")
except Exception as e:
    raise ValueError(f"Failed to create vector store: {str(e)}")
2026-02-12 10:37:12,390 - INFO - Successfully created vector store

Load the BBC News Dataset

To build a search engine, we need data to search through. We use the BBC News dataset from RealTimeData, which provides real-world news articles. This dataset contains news articles from BBC covering various topics and time periods. Loading the dataset is a crucial step because it provides the raw material that our search engine will work with. The quality and diversity of the news articles make it an excellent choice for testing and refining our search engine, ensuring it can handle real-world news content effectively.

The BBC News dataset allows us to work with authentic news articles, enabling us to build and test a search engine that can effectively process and retrieve relevant news content. The dataset is loaded using the Hugging Face datasets library, specifically accessing the "RealTimeData/bbc_news_alltime" dataset with the "2024-12" version.

try:
    news_dataset = load_dataset(
        "RealTimeData/bbc_news_alltime", "2024-12", split="train"
    )
    print(f"Loaded the BBC News dataset with {len(news_dataset)} rows")
    logging.info(f"Successfully loaded the BBC News dataset with {len(news_dataset)} rows.")
except Exception as e:
    raise ValueError(f"Error loading the BBC News dataset: {str(e)}")
Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
2026-02-12 10:37:12,894 - WARNING - Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads.
2026-02-12 10:37:15,730 - INFO - Successfully loaded the BBC News dataset with 2687 rows.


Loaded the BBC News dataset with 2687 rows

Cleaning up the Data

We will use the content of the news articles for our RAG system.

The dataset contains a few duplicate records. We are removing them to avoid duplicate results in the retrieval stage of our RAG system.

news_articles = news_dataset["content"]
unique_articles = set()
for article in news_articles:
    if article:
        unique_articles.add(article)
unique_news_articles = list(unique_articles)
print(f"We have {len(unique_news_articles)} unique articles in our database.")
We have 1749 unique articles in our database.

Saving Data to the Vector Store

To efficiently handle the large number of articles, we process them in batches of articles at a time. This batch processing approach helps manage memory usage and provides better control over the ingestion process.

We first filter out any articles that exceed 50,000 characters to avoid potential issues with token limits. Then, using the vector store's add_texts method, we add the filtered articles to our vector database. The batch_size parameter controls how many articles are processed in each iteration.

This approach offers several benefits:

  1. Memory Efficiency: Processing in smaller batches prevents memory overload
  2. Progress Tracking: Easier to monitor and track the ingestion progress
  3. Resource Management: Better control over CPU and network resource utilization

We use a conservative batch size of 50 to ensure reliable operation. The optimal batch size depends on many factors including:

  • Document sizes being inserted
  • Available system resources
  • Network conditions
  • Concurrent workload

Consider measuring performance with your specific workload before adjusting.

batch_size = 50

# Automatic Batch Processing
articles = [article for article in unique_news_articles if article and len(article) <= 50000]

try:
    vector_store.add_texts(
        texts=articles,
        batch_size=batch_size
    )
    logging.info("Document ingestion completed successfully.")
except Exception as e:
    raise ValueError(f"Failed to save documents to vector store: {str(e)}")
2026-02-12 10:37:52,133 - INFO - Document ingestion completed successfully.

Setting Up the LLM Model

In this section, we set up the Large Language Model (LLM) for our RAG system. We're using the Deepseek model, which can be accessed through two different methods:

  1. Deepseek API Key: This is obtained directly from Deepseek's platform (https://deepseek.ai) by creating an account and subscribing to their API services. With this key, you can access Deepseek's models directly using the ChatDeepSeek class from the langchain_deepseek package.

  2. OpenRouter API Key: OpenRouter (https://openrouter.ai) is a service that provides unified access to multiple LLM providers, including Deepseek. You can obtain an API key by creating an account on OpenRouter's website. This approach uses the ChatOpenAI class from langchain_openai but with a custom base URL pointing to OpenRouter's API endpoint.

The key difference is that OpenRouter acts as an intermediary service that can route your requests to various LLM providers, while the Deepseek API gives you direct access to only Deepseek's models. OpenRouter can be useful if you want to switch between different LLM providers without changing your code significantly.

In our implementation, we check for both keys and prioritize using the Deepseek API directly if available, falling back to OpenRouter if not. The model is configured with temperature=0 to ensure deterministic, focused responses suitable for RAG applications.

from langchain_deepseek import ChatDeepSeek
from langchain_openai import ChatOpenAI

if DEEPSEEK_API_KEY:
    try:
        llm = ChatDeepSeek(
            api_key=DEEPSEEK_API_KEY,
            model_name="deepseek-chat",
            temperature=0
        )
        logging.info("Successfully created Deepseek LLM client")
    except Exception as e:
        raise ValueError(f"Error creating Deepseek LLM client: {str(e)}")
elif OPENROUTER_API_KEY:
    try:
        llm = ChatOpenAI(
            api_key=OPENROUTER_API_KEY,
            base_url="https://openrouter.ai/api/v1",
            model="deepseek/deepseek-chat-v3.1", 
            temperature=0,
        )
        logging.info("Successfully created Deepseek LLM client through OpenRouter")
    except Exception as e:
        raise ValueError(f"Error creating Deepseek LLM client: {str(e)}")
else:
    raise ValueError("Either Deepseek API Key or OpenRouter API Key must be provided")
2026-02-12 10:37:52,156 - INFO - Successfully created Deepseek LLM client through OpenRouter

Understanding Semantic Search in Couchbase

Semantic search goes beyond traditional keyword matching by understanding the meaning and context behind queries. Here's how it works in Couchbase:

How Semantic Search Works

  1. Vector Embeddings: Documents and queries are converted into high-dimensional vectors using an embeddings model (in our case, OpenAI's text-embedding-3-small model)

  2. Similarity Calculation: When a query is made, Couchbase compares the query vector against stored document vectors using the COSINE distance metric

  3. Result Ranking: Documents are ranked by their vector distance (lower distance = more similar meaning)

  4. Flexible Configuration: Different distance metrics (cosine, euclidean, dot product) and embedding models can be used based on your needs

The similarity_search_with_score method performs this entire process, returning documents along with their similarity scores. This enables you to find semantically related content even when exact keywords don't match.

Now let's see semantic search in action and measure its performance with different optimization strategies.

Vector Search Performance Testing

Now let's see semantic search in action and measure its performance with different optimization strategies.

Phase 1: Baseline Performance (No Hyperscale Index)

First, we'll run a semantic search without using a Hyperscale index to establish our baseline performance. This search scans all document vectors directly, which works well for smaller datasets but may be slower for larger collections.

query = "What were Luke Littler's key achievements and records in his recent PDC World Championship match?"

try:
    # Perform the semantic search
    start_time = time.time()
    search_results = vector_store.similarity_search_with_score(query, k=10)
    baseline_time = time.time() - start_time

    logging.info(f"Semantic search completed in {baseline_time:.2f} seconds")

    # Display search results
    print(f"\nSemantic Search Results (completed in {baseline_time:.2f} seconds):")
    print("-" * 80)

    for doc, score in search_results:
        print(f"Distance: {score:.4f}, Text: {doc.page_content[:200]}...")
        print("-" * 80)

except CouchbaseException as e:
    raise RuntimeError(f"Error performing semantic search: {str(e)}")
except Exception as e:
    raise RuntimeError(f"Unexpected error: {str(e)}")
2026-02-12 10:37:53,200 - INFO - Semantic search completed in 1.04 seconds



Semantic Search Results (completed in 1.04 seconds):
--------------------------------------------------------------------------------
Distance: 0.3697, Text: The Littler effect - how darts hit the bullseye

Teenager Luke Littler began his bid to win the 2025 PDC World Darts Championship with a second-round win against Ryan Meikle. Here we assess Littler's ...
--------------------------------------------------------------------------------
Distance: 0.3901, Text: Luke Littler has risen from 164th to fourth in the rankings in a year

A tearful Luke Littler hit a tournament record 140.91 set average as he started his bid for the PDC World Championship title with...
--------------------------------------------------------------------------------
Distance: 0.4020, Text: Luke Littler is one of six contenders for the 2024 BBC Sports Personality of the Year award.

Here BBC Sport takes a look at the darts player's year in five photos....
--------------------------------------------------------------------------------
Distance: 0.4411, Text: Littler is Young Sports Personality of the Year

This video can not be played To play this video you need to enable JavaScript in your browser.

Darts player Luke Littler has been named BBC Young Spor...
--------------------------------------------------------------------------------
Distance: 0.4586, Text: Wright is the 17th seed at the World Championship

Two-time champion Peter Wright won his opening game at the PDC World Championship, while Ryan Meikle edged out Fallon Sherrock to set up a match agai...
--------------------------------------------------------------------------------
Distance: 0.4594, Text: Second seed Smith knocked out of Worlds by Doets

Michael Smith was 2-1 ahead but fell to a shock exit

Former champion Michael Smith has been sensationally knocked out of the PDC World Championship b...
--------------------------------------------------------------------------------
Distance: 0.4671, Text: Cross loses as record number of seeds out of Worlds

Rob Cross has suffered three second-round exits in his eight World Championships

Former champion Rob Cross became the latest high-profile casualty...
--------------------------------------------------------------------------------
Distance: 0.4883, Text: Michael van Gerwen has made just one major ranking event final in 2024

Michael van Gerwen enjoyed a comfortable 3-0 victory over English debutant James Hurrell in his opening match of the PDC World D...
--------------------------------------------------------------------------------
Distance: 0.4886, Text: Gary Anderson was the fifth seed to be beaten on Sunday

Two-time champion Gary Anderson has been dumped out of the PDC World Championship on his 54th birthday by Jeffrey de Graaf. The Scot, winner in...
--------------------------------------------------------------------------------
Distance: 0.4894, Text: Christian Kist was sealing his first televised nine-darter

Christian Kist hit a nine-darter but lost his PDC World Championship first-round match to Madars Razma. The Dutchman became the first player...
--------------------------------------------------------------------------------

Creating the Hyperscale Index

Now we'll create a Hyperscale index to significantly improve query performance. The index uses IVF (Inverted File) with SQ8 (8-bit Scalar Quantization) for optimal balance between speed and accuracy.

vector_store.create_index(index_type=IndexType.HYPERSCALE, index_name="openrouterdeepseek_hyperscale_index",index_description="IVF,SQ8")

Phase 2: Hyperscale-Optimized Performance

Now let's run the same similarity search using the Hyperscale index we just created. You'll notice significantly improved performance as the index efficiently retrieves data.

Note: In Hyperscale and Composite Vector Index search, the distance represents the vector distance between the query and document embeddings. Lower distance indicates higher similarity, while higher distance indicates lower similarity.

query = "What were Luke Littler's key achievements and records in his recent PDC World Championship match?"

try:
    # Perform the semantic search
    start_time = time.time()
    search_results = vector_store.similarity_search_with_score(query, k=10)
    optimized_time = time.time() - start_time

    logging.info(f"Semantic search completed in {optimized_time:.2f} seconds")

    # Display search results
    print(f"\nSemantic Search Results (completed in {optimized_time:.2f} seconds):")
    print("-" * 80)

    for doc, score in search_results:
        print(f"Distance: {score:.4f}, Text: {doc.page_content}")
        print("-" * 80)

except CouchbaseException as e:
    raise RuntimeError(f"Error performing semantic search: {str(e)}")
except Exception as e:
    raise RuntimeError(f"Unexpected error: {str(e)}")
2026-02-12 10:37:58,776 - INFO - Semantic search completed in 0.31 seconds



Semantic Search Results (completed in 0.31 seconds):
--------------------------------------------------------------------------------
Distance: 0.3697, Text: The Littler effect - how darts hit the bullseye

Teenager Luke Littler began his bid to win the 2025 PDC World Darts Championship with a second-round win against Ryan Meikle. Here we assess Littler's impact after a remarkable rise which saw him named BBC Young Sports Personality of the Year and runner-up in the main award to athlete Keely Hodgkinson.

One year ago, he was barely a household name in his own home. Now he is a sporting phenomenon. After emerging from obscurity aged 16 to reach the World Championship final, the life of Luke Littler and the sport he loves has been transformed. Viewing figures, ticket sales and social media interest have rocketed. Darts has hit the bullseye. This Christmas more than 100,000 children are expected to be opening Littler-branded magnetic dartboards as presents. His impact has helped double the number of junior academies, prompted plans to expand the World Championship and generated interest in darts from Saudi Arabian backers.

Just months after taking his GCSE exams and ranked 164th in the world, Littler beat former champions Raymond van Barneveld and Rob Cross en route to the PDC World Championship final in January, before his run ended with a 7-4 loss to Luke Humphries. With his nickname 'The Nuke' on his purple and yellow shirt and the Alexandra Palace crowd belting out his walk-on song, Pitbull's tune Greenlight, he became an instant hit. Electric on the stage, calm off it. The down-to-earth teenager celebrated with a kebab and computer games. "We've been watching his progress since he was about seven. He was on our radar, but we never anticipated what would happen. The next thing we know 'Littlermania' is spreading everywhere," PDC president Barry Hearn told BBC Sport. A peak TV audience of 3.7 million people watched the final - easily Sky's biggest figure for a non-football sporting event. The teenager from Warrington in Cheshire was too young to legally drive or drink alcohol, but earned £200,000 for finishing second - part of £1m prize money in his first year as a professional - and an invitation to the elite Premier League competition. He turned 17 later in January but was he too young for the demanding event over 17 Thursday nights in 17 locations? He ended up winning the whole thing, and hit a nine-dart finish against Humphries in the final. From Bahrain to Wolverhampton, Littler claimed 10 titles in 2024 and is now eyeing the World Championship.

As he progressed at the Ally Pally, the Manchester United fan was sent a good luck message by the club's former midfielder and ex-England captain David Beckham. In 12 months, Littler's Instagram followers have risen from 4,000 to 1.3m. Commercial backers include a clothing range, cereal firm and train company and he will appear in a reboot of the TV darts show Bullseye. Google say he was the most searched-for athlete online in the UK during 2024. On the back of his success, Littler darts, boards, cabinets, shirts are being snapped up in big numbers. "This Christmas the junior magnetic dartboard is selling out, we're talking over 100,000. They're 20 quid and a great introduction for young children," said Garry Plummer, the boss of sponsors Target Darts, who first signed a deal with Littler's family when he was aged 12. "All the toy shops want it, they all want him - 17, clean, doesn't drink, wonderful."

Littler beat Luke Humphries to win the Premier League title in May

The number of academies for children under the age of 16 has doubled in the last year, says Junior Darts Corporation chairman Steve Brown. There are 115 dedicated groups offering youngsters equipment, tournaments and a place to develop, with bases including Australia, Bulgaria, Greece, Norway, USA and Mongolia. "We've seen so many inquiries from around the world, it's been such a boom. It took us 14 years to get 1,600 members and within 12 months we have over 3,000, and waiting lists," said Brown. "When I played darts as a child, I was quite embarrassed to tell my friends what my hobby was. All these kids playing darts now are pretty popular at school. It's a bit rock 'n roll and recognised as a cool thing to do." Plans are being hatched to extend the World Championship by four days and increase the number of players from 96 to 128. That will boost the number of tickets available by 25,000 to 115,000 but Hearn reckons he could sell three times as many. He says Saudi Arabia wants to host a tournament, which is likely to happen if no-alcohol regulations are relaxed. "They will change their rules in the next 12 months probably for certain areas having alcohol, and we'll take darts there and have a party in Saudi," he said. "When I got involved in darts, the total prize money was something like £300,000 for the year. This year it will go to £20m. I expect in five years' time, we'll be playing for £40m."

Former electrician Cross charged to the 2018 world title in his first full season, while Adrian Lewis and Michael van Gerwen were multiple victors in their 20s and 16-time champion Phil ‘The Power’ Taylor is widely considered the greatest of all time. Littler is currently fourth in the world rankings, although that is based on a two-year Order of Merit. There have been suggestions from others the spotlight on the teenager means world number one Humphries, 29, has been denied the coverage he deserves, but no darts player has made a mark at such a young age as Littler. "Luke Humphries is another fabulous player who is going to be around for years. Sport is a very brutal world. It is about winning and claiming the high ground. There will be envy around," Hearn said. "Luke Littler is the next Tiger Woods for darts so they better get used to it, and the only way to compete is to get better." World number 38 Martin Lukeman was awestruck as he described facing a peak Littler after being crushed 16-3 in the Grand Slam final, with the teenager winning 15 consecutive legs. "I can't compete with that, it was like Godly. He was relentless, he is so good it's ridiculous," he said. Lukeman can still see the benefits he brings, adding: "What he's done for the sport is brilliant. If it wasn't for him, our wages wouldn't be going up. There's more sponsors, more money coming in, all good." Hearn feels future competition may come from players even younger than Littler. "I watched a 10-year-old a few months ago who averaged 104.89 and checked out a 4-3 win with a 136 finish. They smell the money, the fame and put the hard work in," he said. How much better Littler can get is guesswork, although Plummer believes he wants to reach new heights. "He never says 'how good was I?' But I think he wants to break records and beat Phil Taylor's 16 World Championships and 16 World Matchplay titles," he said. "He's young enough to do it." A version of this article was originally published on 29 November.
• None Know a lot about Littler? Take our quiz
--------------------------------------------------------------------------------
Distance: 0.3901, Text: Luke Littler has risen from 164th to fourth in the rankings in a year

A tearful Luke Littler hit a tournament record 140.91 set average as he started his bid for the PDC World Championship title with a dramatic 3-1 win over Ryan Meikle. The 17-year-old made headlines around the world when he reached the tournament final in January, where he lost to Luke Humphries. Starting this campaign on Saturday, Littler was millimetres away from a nine-darter when he missed double 12 as he blew Meikle away in the fourth and final set of the second-round match. Littler was overcome with emotion at the end, cutting short his on-stage interview. "It was probably the toughest game I've ever played. I had to fight until the end," he said later in a news conference. "As soon as the question came on stage and then boom, the tears came. It was just a bit too much to speak on stage. "It is the worst game I have played. I have never felt anything like that tonight." Admitting to nerves during the match, he told Sky Sports: "Yes, probably the biggest time it's hit me. Coming into it I was fine, but as soon as [referee] George Noble said 'game on', I couldn't throw them." Littler started slowly against Meikle, who had two darts for the opening set, but he took the lead by twice hitting double 20. Meikle did not look overawed against his fellow Englishman and levelled, but Littler won the third set and exploded into life in the fourth. The tournament favourite hit four maximum 180s as he clinched three straight legs in 11, 10 and 11 darts for a record set average, and 100.85 overall. Meanwhile, two seeds crashed out on Saturday night – five-time world champion Raymond van Barneveld lost to Welshman Nick Kenny, while England's Ryan Joyce beat Danny Noppert. Australian Damon Heta was another to narrowly miss out on a nine-darter, just failing on double 12 when throwing for the match in a 3-1 win over Connor Scutt. Ninth seed Heta hit four 100-plus checkouts to come from a set down against Scutt in a match in which both men averaged more than 97.

Littler was hugged by his parents after victory over Meikle

Littler returned to Alexandra Palace to a boisterous reception from more than 3,000 spectators and delivered an astonishing display in the fourth set. He was on for a nine-darter after his opening two throws in both of the first two legs and completed the set in 32 darts - the minimum possible is 27. The teenager will next play after Christmas against European Championship winner Ritchie Edhouse, the 29th seed, or Ian White, and is seeded to meet Humphries in the semi-finals. Having entered last year's event ranked 164th, Littler is up to fourth in the world and will go to number two if he reaches the final again this time. He has won 10 titles in his debut professional year, including the Premier League and Grand Slam of Darts. After reaching the World Championship final as a debutant aged just 16, Littler's life has been transformed and interest in darts has rocketed. Google say he was the most searched-for athlete online in the UK during 2024. This Christmas, more than 100,000 children are expected to be opening Littler-branded magnetic dartboards as presents. His impact has helped double the number of junior academies and has prompted plans to expand the World Championship. Littler was named BBC Young Sports Personality of the Year on Tuesday and was runner-up to athlete Keely Hodgkinson for the main award.

Nick Kenny will play world champion Luke Humphries in round three after Christmas

Barneveld was shocked 3-1 by world number 76 Kenny, who was in tears after a famous victory. Kenny, 32, will face Humphries in round three after defeating the Dutchman, who won the BDO world title four times and the PDC crown in 2007. Van Barneveld, ranked 32nd, became the sixth seed to exit in the second round. His compatriot Noppert, the 13th seed, was stunned 3-1 by Joyce, who will face Ryan Searle or Matt Campbell next, with the winner of that tie potentially meeting Littler in the last 16. Elsewhere, 15th seed Chris Dobey booked his place in the third round with a 3-1 win over Alexander Merkx. Englishman Dobey concluded an afternoon session which started with a trio of 3-0 scorelines. Northern Ireland's Brendan Dolan beat Lok Yin Lee to set up a meeting with three-time champion Michael van Gerwen after Christmas. In the final two first-round matches of the 2025 competition, Wales' Rhys Griffin beat Karel Sedlacek of the Czech Republic before Asia number one Alexis Toylo cruised past Richard Veenstra.
--------------------------------------------------------------------------------
Distance: 0.4020, Text: Luke Littler is one of six contenders for the 2024 BBC Sports Personality of the Year award.

Here BBC Sport takes a look at the darts player's year in five photos.
--------------------------------------------------------------------------------
Distance: 0.4411, Text: Littler is Young Sports Personality of the Year

This video can not be played To play this video you need to enable JavaScript in your browser.

Darts player Luke Littler has been named BBC Young Sports Personality of the Year 2024. The 17-year-old has enjoyed a breakthrough year after finishing runner-up at the 2024 PDC World Darts Championship in January. The Englishman, who has won 10 senior titles on the Professional Darts Corporation tour this year, is the first darts player to claim the award. "It shows how well I have done this year, not only for myself, but I have changed the sport of darts," Littler told BBC One. "I know the amount of academies that have been brought up in different locations, tickets selling out at Ally Pally in hours and the Premier League selling out - it just shows how much I have changed it."

He was presented with the trophy by Harry Aikines-Aryeetey - a former sprinter who won the award in 2005 - and ex-rugby union player Jodie Ounsley, both of whom are stars of the BBC television show Gladiators. Skateboarder Sky Brown, 16, and Para-swimmer William Ellard, 18, were also shortlisted for the award. Littler became a household name at the start of 2024 by reaching the World Championship final aged just 16 years and 347 days. That achievement was just the start of a trophy-laden year, with Littler winning the Premier League Darts, Grand Slam and World Series of Darts Finals among his haul of titles. Littler has gone from 164th to fourth in the world rankings and earned more than £1m in prize money in 2024. The judging panel for Young Sports Personality of the Year included Paralympic gold medallist Sammi Kinghorn, Olympic silver medal-winning BMX freestyler Keiran Reilly, television presenter Qasa Alom and Radio 1 DJ Jeremiah Asiamah, as well as representatives from the Youth Sport Trust, Blue Peter and BBC Sport.
--------------------------------------------------------------------------------
Distance: 0.4586, Text: Wright is the 17th seed at the World Championship

Two-time champion Peter Wright won his opening game at the PDC World Championship, while Ryan Meikle edged out Fallon Sherrock to set up a match against teenage prodigy Luke Littler. Scotland's Wright, the 2020 and 2022 winner, has been out of form this year, but overcame Wesley Plaisier 3-1 in the second round at Alexandra Palace in London. "It was this crowd that got me through, they wanted me to win. I thank you all," said Wright. Meikle came from a set down to claim a 3-2 victory in his first-round match against Sherrock, who was the first woman to win matches at the tournament five years ago. The 28-year-old will now play on Saturday against Littler, who was named BBC Young Sports Personality of the Year and runner-up in the main award to athlete Keely Hodgkinson on Tuesday night. Littler, 17, will be competing on the Ally Pally stage for the first time since his rise to stardom when finishing runner-up in January's world final to Luke Humphries. Earlier on Tuesday, World Grand Prix champion Mike de Decker – the 24th seed - suffered a surprise defeat to Luke Woodhouse in the second round. He is the second seed to exit following 16th seed James Wade's defeat on Monday to Jermaine Wattimena, who meets Wright in round three. Kevin Doets recovered from a set down to win 3-1 against Noa-Lynn van Leuven, who was making history as the first transgender woman to compete in the tournament.

Sherrock drew level at 2-2 but lost the final set to Meikle

The 54-year-old Wright only averaged 89.63 to his opponent's 93.77, but did enough to progress. Sporting a purple mohawk and festive outfit, crowd favourite 'Snakebite' showed glimpses of his best to win the first set and survived eight set darts to go 2-0 ahead. He lost the next but Dutchman Plaisier missed two more set darts in the fourth and Wright seized his opportunity. "Wesley had his chances but he missed them and I took them," he said. "He's got his tour card and he's going to be a dangerous player next year for all the players playing against him." Sherrock, 30, fought back from 2-1 down to force a decider against her English compatriot Meikle. She then narrowly missed the bull to take out 170 in the fourth leg before left-hander Meikle held his nerve to hit double 18 for a 96 finish to seal a hard-fought success. "I felt under pressure from the start and to come through feels unbelievable," said Meikle. "It's an unbelievable prize to play Luke here on this stage. It's the biggest stage of them all. I'm so happy." World number 81 Jeffrey de Graaf, who was born in the Netherlands but now represents Sweden, looked in trouble against Rashad Sweeting before prevailing 3-1. Sweeting, who was making history as the first player from the Bahamas to compete in the tournament, took the first set, but De Graaf fought back to clinch a second-round meeting with two-time champion Gary Anderson Germany's Ricardo Pietreczko, ranked 34, beat China's Xiaochen Zong 3-1 and will face Gian van Veen next.
--------------------------------------------------------------------------------
Distance: 0.4594, Text: Second seed Smith knocked out of Worlds by Doets

Michael Smith was 2-1 ahead but fell to a shock exit

Former champion Michael Smith has been sensationally knocked out of the PDC World Championship by Kevin Doets. Englishman Smith, seeded second, went down 3-2 after a pulsating second-round duel at Alexandra Palace in London. Dutchman Doets prevailed 6-4 in the deciding set, despite checkouts of 123, 84, 94 and 76 from 2023 champion Smith. "This was the most stressful game of my life and I've won it, yes," said world number 51 Doets. "I felt if I can keep my focus, I won't lose this. It was so very tight, to get over the line was amazing." Doets, 26, took the first set and fought back after going 2-1 down to avenge his narrow defeat to Smith at the same stage last year. Having lost in the second round of the tournament for the first time since 2020, the 34-year-old Smith will now drop to at least 15 in the rankings.

Doets won in the first round against Noa-Lynn van Leuven, who was the first transgender woman to play in the tournament

England's Scott Williams, who made a shock run to the semi-finals in the 2024 tournament before losing to eventual champion Luke Humphries, overcame Niko Springer 3-1 in a thriller. German debutant Springer, second on this year's development tour, won all three legs in the opening set before the match exploded into life. Williams hit back, showing his old swagger as he went ahead after a sensational third set which featured seven 180s. The 34-year-old edged the deciding leg in the fourth and will meet 2018 champion Rob Cross in round two on Monday. Nick Kenny delighted the Ally Pally crowd with a fabulous 170 finish to seal a 3-0 victory in round one over American Stowe Buntz. The Welshman, 31, will face five-time world champion Raymond Barneveld on Saturday evening on a bill which also features teenage star Luke Littler against Ryan Meikle. Canadian Matt Campbell set up a second-round match against Ryan Searle with a 3-2 defeat of Austrian Mensur Suljovic.

England's Callan Rydz averaged 107.06 to book his place in the second round, before Gabriel Clemens was knocked out by Wales' Robert Owen on Thursday afternoon. Rydz beat Croatian Romeo Grbavac 3-0, recording the tournament's highest average first-round match average in its current 96-player format. It was the competition's 26th highest match average overall and comfortably the best so far at the 2025 event. The previous record was held by teenager Luke Littler, who scored 106.12 at this stage last year. Rydz, from Bedlington in Northumberland, meets Germany's Martin Schindler in the second round on Sunday evening. The afternoon session concluded with Germany's 27th seed Clemens being beaten by Owen, who is ranked 50 places below him. Owen recorded a 3-1 victory, his second in a matter of days, to reach the third round, which begins on 27 December. Hong Kong's Lok Yin Lee came from a set down to beat Chris Landman 3-1 after winning nine straight legs. Lee will face Northern Ireland's Brendan Dolan in round two on Saturday afternoon. Meanwhile, 2024 Grand Slam of Darts runner-up Martin Lukeman came from a set down to beat Indian qualifier Nitin Kumar 3-1. Lukeman meets 21st seed Andrew Gilding on Monday afternoon for a place in the last 32.
--------------------------------------------------------------------------------
Distance: 0.4671, Text: Cross loses as record number of seeds out of Worlds

Rob Cross has suffered three second-round exits in his eight World Championships

Former champion Rob Cross became the latest high-profile casualty as a record-breaking 14th seed exited the PDC World Darts Championship in the second round. The number five seed was beaten 3-1 by close friend Scott Williams, who will face Germany's Ricardo Pietreczko in round three. Cross, who won the event on his debut in 2018, took the opening set but failed to reach anywhere near his best as he suffered his third second-round exit. He was joined by number six seed David Chisnall, who was beaten 3-2 in a sudden-death leg by Ricky Evans, who came into the tournament 46th in the PDC's Order of Merit. The 2021 semi-finalist won the opening set, but then found himself 2-1 down to an inspired Evans, who was cheered on relentlessly by the Alexandra Palace crowd. He forced the game into a deciding set and faced match dart but Evans missed bullseye by the width of the wire. Chisnall then missed his own match dart on double tops, before he made a miscalculation when attempting to checkout 139 at 5-4 down. No real harm was done with a sudden-death leg forced but he was unable to hold off Evans, who reaches the third round for the third time in the last five years. "It's not even what it is, again I've played a world-class darts player. I've played quite well and won," Evans told Sky Sports. "Look at this [the crowd], wow. I don't understand it, why are they cheering me on? "I don't get this reception in my household. Thank you very much. You've made a very fat guy very happy." Evans will face unseeded Welshman Robert Owen when the third round starts after the three-day Christmas break.

World youth champion Gian van Veen had become the 12th seed to be knocked out when he lost 3-1 to Pietreczko. The 28th seed lost the opening set, having missed nine darts at double, but levelled. However, the Dutchman was unable to match Pietreczko, who closed out a comfortable win with a checkout percentage of 55.6%. Pietreczko said: "I am over the moon to win. It is very important for me to be in the third round after Christmas. I love the big stage." The 26th seed trailed 1-0 and 2-1, and both players went on to miss match darts, before Gurney won the final set 3-1 on legs.

Jonny Clayton is into the third round of the PDC World Darts Championship for a sixth consecutive year

In the afternoon session, Welsh number seven seed Jonny Clayton also needed sudden death to pull off a sensational final-set comeback against Mickey Mansell in. He was a leg away from defeat twice to his Northern Irish opponent, but came from behind to win the final set 6-5 in a sudden-death leg to win 3-2. Clayton, who will play Gurney in round three, lost the opening set of the match, but fought back to lead 2-1, before being pegged back again by 51-year-old Mansell, who then missed match darts on double tops in the deciding set. "I was very emotional. I've got to be honest, that meant a lot," said Clayton, who is in the favourable half of the draw following shock second-round exits for former world champions Michael Smith and Gary Anderson. "I had chances before and Mickey definitely had chances before. It wasn't great to play in, not the best - I wouldn't wish that on my worst enemy. "There is a lot of weight off my shoulders after that. I know there is another gear or two in the bank, but I'll be honest that meant a lot to me, it is a tester and will try and make me believe again." Clayton was 2-0 down in the fifth set after consecutive 136 and 154 checkouts from Mansell, but won three legs on the trot in 15, 12 and 10 darts to wrestle a 3-2 lead. He missed three darts for the match, before his unseeded opponent held and broke Clayton's throw to lead 4-3. Mansell missed a match dart at double 20, before Clayton won on double five after two missed checkouts. Elsewhere, Northern Ireland's Josh Rock booked his place in the third round against England's Chris Dobey with a 3-0 win over Wales' Rhys Griffin. Martin Lukeman, runner-up to Luke Littler at the Grand Slam of Darts last month, is out after a 3-1 loss to number 21 seed Andrew Gilding. The final day before the Christmas break started with Poland's number 31 seed Krzysztof Ratajski recording a 3-1 win over Alexis Toylo of the Philippines.

All times are GMT and subject to change. Two fourth-round matches will also be played
--------------------------------------------------------------------------------
Distance: 0.4883, Text: Michael van Gerwen has made just one major ranking event final in 2024

Michael van Gerwen enjoyed a comfortable 3-0 victory over English debutant James Hurrell in his opening match of the PDC World Darts Championship. The three-time world champion has had a tough year by his standards, having fallen behind Luke Littler and Luke Humphries, so a relatively stress-free opening match at Alexandra Palace was just what was needed. Hurrell, 40, offered some resistance early on when taking the opening leg of the match, but he would win just two more as Van Gerwen proved far too strong. The third-seeded Dutchman averaged 94.85, took out two three-figure checkouts and hit 50% of his doubles - with six of his nine misses coming in one scrappy leg. Van Gerwen, 35, will now face either Brendan Dolan or Lok Yin Lee in the third round.

"I think I played OK," Van Gerwen told Sky Sports after his match. "Of course, I was a bit nervous. Like everyone knows it's been a tough year for me. "Overall, it was a good performance. I was confident. I won the game, that's the main thing." Also on Friday night, Germany's Florian Hempel showed why he loves playing on the Alexandra Palace stage with a thrilling 3-1 victory in a high-quality contest against Jeffrey de Zwaan. Both men hit seven 180s in a match played at a fast and furious pace, but 34-year-old Hempel's superior doubles gave him a fourth straight first-round victory in the competition. Hempel moves on to a tie with 26th seed Daryl Gurney but it was a damaging loss for De Zwaan, 28, who came through a late qualifier in November and needed a good run here to keep his PDC tour card for next season. Mickey Mansell earned a second-round date with world number seven Jonny Clayton after a scrappy 3-1 win over Japan's Tomoya Goto, while Dylan Slevin came through an all-Irish tie against William O'Connor to progress to a meeting with Dimitri van den Bergh.

Stephen Bunting is in the third round of the PDC World Darts Championship for a third consecutive year

In the afternoon session, Stephen Bunting came from behind to beat Kai Gotthardt 3-1 and book his place in the third round. Englishman Bunting, ranked eighth in the world, dropped the first set and almost went 2-0 down in the match before staging an impressive recovery. Tournament debutant Gotthardt missed three darts at double eight to win the second set, allowing Bunting to take out double 10 to level the match before powering away to victory by winning the third and fourth sets without losing a leg. Victory for "The Bullet" sets up a last 32 meeting with the winner of Dirk van Duijvenbode's meeting with Madars Razma after Christmas. Should Bunting progress further, he is seeded to face world number one and defending world champion Luke Humphries in the quarter-finals on New Year's Day. Elsewhere in Friday afternoon's session, the Dutch duo of Alexander Merkx and Wessel Nijman advanced to the second round with wins over Stephen Burton and Cameron Carolissen respectively. England's Ian White was handed a walkover victory against Sandro Eric Sosing of the Philippines. Sosing withdrew from the competition on medical grounds and was taken to hospital following chest pains.
--------------------------------------------------------------------------------
Distance: 0.4886, Text: Gary Anderson was the fifth seed to be beaten on Sunday

Two-time champion Gary Anderson has been dumped out of the PDC World Championship on his 54th birthday by Jeffrey de Graaf. The Scot, winner in 2015 and 2016, lost 3-0 to the Swede in a second-round shock at Alexandra Palace in London. "Gary didn't really show up as he usually does. I'm very happy with the win," said De Graaf, 34, who had a 75% checkout success and began with an 11-dart finish. "It's a dream come true for me. He's been my idol since I was 14 years old." Anderson, ranked 14th, became the 11th seed to be knocked out from the 24 who have played so far, and the fifth to fall on Sunday.

He came into the competition with the year's highest overall three-dart average of 99.66 but hit just three of his 20 checkout attempts to lose his opening match of the tournament for the first time. De Graaf will now meet Filipino qualifier Paolo Nebrida after he stunned England's Ross Smith, the 19th seed, in straight sets. Ritchie Edhouse, Dirk van Duijvenbode and Martin Schindler were the other seeds beaten on day eight. England's Callan Rydz, who hit a record first-round average of 107.06 on Thursday, followed up with a 3-0 win over 23rd seed Schindler on Sunday. The German missed double 12 for a nine-darter in the first set – the third player to do so in 24 hours after Luke Littler and Damon Heta – and ended up losing the leg. Rydz next meets Belgian Dimitri van den Bergh, who hit six 180s and averaged 96 in a 3-0 win over Irishman Dylan Slevin.

England's Joe Cullen abruptly left his post-match news conference and accused the media of not showing him respect after his 3-0 win over Dutchman Wessel Nijman. Nijman, who has previously served a ban for breaching betting and anti-corruption rules, had been billed as favourite beforehand to beat 23rd seed Cullen. "Honestly, the media attention that Wessel's got, again this is not a reflection on him," Cullen said. "He seems like a fantastic kid, he's been caught up in a few things beforehand, but he's served his time and he's held his hands up, like a lot haven't. "I think the way I've been treated probably with the media and things like that - I know you guys have no control over the bookies - I've been shown no respect, so I won't be showing any respect to any of you guys tonight. "I'm going to go home. Cheers." Ian 'Diamond' White beat European champion and 29th seed Edhouse 3-1 and will face teenage star Littler in the next round. White, born in the same Cheshire town as the 17-year-old, acknowledged he would need to up his game in round three. Asked if he knew who was waiting for him, White joked: "Yeah, Runcorn's number two. I'm from Runcorn and I'm number one." Ryan Searle started Sunday afternoon's action off with a 10-dart leg and went on to beat Matt Campbell 3-0, while Latvian Madars Razma defeated 25th seed Van Duijvenbode 3-1. Seventh seed Jonny Clayton and 2018 champion Rob Cross are among the players in action on Monday as the second round concludes. The third round will start on Friday after a three-day break for Christmas.
--------------------------------------------------------------------------------
Distance: 0.4894, Text: Christian Kist was sealing his first televised nine-darter

Christian Kist hit a nine-darter but lost his PDC World Championship first-round match to Madars Razma. The Dutchman became the first player to seal a perfect leg in the tournament since Michael Smith did so on the way to beating Michael van Gerwen in the 2023 final. Kist, the 2012 BDO world champion at Lakeside, collects £60,000 for the feat, with the same amount being awarded by sponsors to a charity and to one spectator inside Alexandra Palace in London. The 38-year-old's brilliant finish sealed the opening set, but his Latvian opponent bounced back to win 3-1. Darts is one of the few sports that can measure perfection; snooker has the 147 maximum break, golf has the hole-in-one, darts has the nine-dart finish. Kist scored two maximum 180s to leave a 141 checkout which he completed with a double 12, to the delight of more than 3,000 spectators. The English 12th seed, who has been troubled by wrist and back injuries, could next play Andrew Gilding in the third round - which begins on 27 December - should Gilding beat the winner of Martin Lukeman's match against qualifier Nitin Kumar. Aspinall faces a tough task to reach the last four again, with 2018 champion Rob Cross and 2024 runner-up Luke Littler both in his side of the draw.

Kist - who was knocked out of last year's tournament by teenager Littler - will still earn a bigger cheque than he would have got for a routine run to the quarter-finals. His nine-darter was the 15th in the history of the championship and first since the greatest leg in darts history when Smith struck, moments after Van Gerwen just missed his attempt. Darts fan Kris, a railway worker from Sutton in south London, was the random spectator picked out to receive £60,000, with Prostate Cancer UK getting the same sum from tournament sponsors Paddy Power. "I'm speechless to be honest. I didn't expect it to happen to me," Kris said. "This was a birthday present so it makes it even better. My grandad got me tickets. It was just a normal day - I came here after work." Kist said: "Hitting the double 12 felt amazing. It was a lovely moment for everyone and I hope Kris enjoys the money. Maybe I will go on vacation next month." Earlier, Jim Williams was favourite against Paolo Nebrida but lost 3-2 in an epic lasting more than an hour. The Filipino took a surprise 2-1 lead and Williams only went ahead for the first time in the opening leg of the deciding set. The Welshman looked on course for victory but missed five match darts. UK Open semi-finalist Ricky Evans set up a second-round match against Dave Chisnall, checking out on 109 to edge past Gordon Mathers 3-2.
--------------------------------------------------------------------------------
# Performance Analysis Summary
print("=" * 80)
print("VECTOR SEARCH PERFORMANCE OPTIMIZATION SUMMARY")
print("=" * 80)
print(f"Phase 1 - Baseline Search (No Hyperscale):     {baseline_time:.4f} seconds")
print(f"Phase 2 - Hyperscale-Optimized Search:         {optimized_time:.4f} seconds")

print()
print("-" * 80)
print("VECTOR SEARCH OPTIMIZATION IMPACT:")
print("-" * 80)

if optimized_time < baseline_time:
    speedup = baseline_time / optimized_time
    improvement = ((baseline_time - optimized_time) / baseline_time) * 100
    print(f"Hyperscale Index Benefit:      {speedup:.2f}x faster ({improvement:.1f}% improvement)")
else:
    print(f"Note: Hyperscale index did not show improvement in this run.")
    print(f"This may occur if the index is still training or the dataset is small.")
    print(f"Re-run Phase 2 after the index is fully built for accurate results.")

print()
print("Key Insights for Vector Search Performance:")
print("• Hyperscale indexes provide significant performance improvements for vector similarity search")
print("• Performance gains are most dramatic for large datasets and complex semantic queries")
print("• Hyperscale vector index optimization is particularly effective for high-dimensional embeddings")
print("• Combined with proper quantization (SQ8), Hyperscale delivers production-ready performance")
print("• Consider Composite indexes when you need to combine vector search with scalar filtering")
================================================================================
VECTOR SEARCH PERFORMANCE OPTIMIZATION SUMMARY
================================================================================
Phase 1 - Baseline Search (No Hyperscale):     1.0393 seconds
Phase 2 - Hyperscale-Optimized Search:         0.3088 seconds

--------------------------------------------------------------------------------
VECTOR SEARCH OPTIMIZATION IMPACT:
--------------------------------------------------------------------------------
Hyperscale Index Benefit:      3.37x faster (70.3% improvement)

Key Insights for Vector Search Performance:
• Hyperscale indexes provide significant performance improvements for vector similarity search
• Performance gains are most dramatic for large datasets and complex semantic queries
• Hyperscale vector index optimization is particularly effective for high-dimensional embeddings
• Combined with proper quantization (SQ8), Hyperscale delivers production-ready performance
• Consider Composite indexes when you need to combine vector search with scalar filtering

Setting Up LLM Response Cache

To further optimize our system, we set up a Couchbase-based cache. A cache is a temporary storage layer that holds data that is frequently accessed, speeding up operations by reducing the need to repeatedly retrieve the same information from the database. In our setup, the cache will help us accelerate repetitive tasks, such as looking up similar documents. By implementing a cache, we enhance the overall performance of our search engine, ensuring that it can handle high query volumes and deliver results quickly.

Caching is particularly valuable in scenarios where users may submit similar queries multiple times or where certain pieces of information are frequently requested. By storing these in a cache, we can significantly reduce the time it takes to respond to these queries, improving the user experience.

try:
    cache = CouchbaseCache(
        cluster=cluster,
        bucket_name=CB_BUCKET_NAME,
        scope_name=SCOPE_NAME,
        collection_name=CACHE_COLLECTION,
    )
    logging.info("Successfully created cache")
    set_llm_cache(cache)
except Exception as e:
    raise ValueError(f"Failed to create cache: {str(e)}")
2026-02-12 10:37:58,792 - INFO - Successfully created cache

Retrieval-Augmented Generation (RAG) with Couchbase and LangChain

Couchbase and LangChain can be seamlessly integrated to create RAG (Retrieval-Augmented Generation) chains, enhancing the process of generating contextually relevant responses. In this setup, Couchbase serves as the vector store, where embeddings of documents are stored. When a query is made, LangChain retrieves the most relevant documents from Couchbase by comparing the query’s embedding with the stored document embeddings. These documents, which provide contextual information, are then passed to a generative language model within LangChain.

The language model, equipped with the context from the retrieved documents, generates a response that is both informed and contextually accurate. This integration allows the RAG chain to leverage Couchbase’s efficient storage and retrieval capabilities, while LangChain handles the generation of responses based on the context provided by the retrieved documents. Together, they create a powerful system that can deliver highly relevant and accurate answers by combining the strengths of both retrieval and generation.

# Create RAG prompt template
rag_prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant that answers questions based on the provided context."),
    ("human", "Context: {context}\n\nQuestion: {question}")
])

# Create RAG chain
rag_chain = (
    {"context": vector_store.as_retriever(), "question": RunnablePassthrough()}
    | rag_prompt
    | llm
    | StrOutputParser()
)
logging.info("Successfully created RAG chain")
2026-02-12 10:37:58,796 - INFO - Successfully created RAG chain
try:
    start_time = time.time()
    rag_response = rag_chain.invoke(query)
    rag_elapsed_time = time.time() - start_time

    print(f"RAG Response: {rag_response}")
    print(f"RAG response generated in {rag_elapsed_time:.2f} seconds")
except InternalServerFailureException as e:
    if "query request rejected" in str(e):
        print("Error: Search request was rejected due to rate limiting. Please try again later.")
    else:
        print(f"Internal server error occurred: {str(e)}")
except Exception as e:
    print(f"Unexpected error occurred: {str(e)}")
RAG Response: Based on the provided context, Luke Littler's key achievements and records in his recent PDC World Championship second-round match against Ryan Meikle were:

1.  **Tournament Record Set Average:** He hit a tournament record average of **140.91** in the fourth and final set of the match.
2.  **Near Nine-Darter:** He was "millimetres away" from hitting a nine-dart finish, narrowly missing double 12.
3.  **Dominant Final Set:** He won the fourth set in spectacular fashion, clinching three consecutive legs in 11, 10, and 11 darts.
4.  **High Overall Average:** For the entire match, he maintained a very high average of **100.85**.
5.  **Emotional Victory:** The match was emotionally charged for Littler, who described it as "probably the toughest game I've ever played" and was overcome with emotion, cutting short his on-stage interview.
RAG response generated in 13.44 seconds

Demonstrating Cache Benefits

Couchbase can be effectively used as a caching mechanism for RAG (Retrieval-Augmented Generation) responses by storing and retrieving precomputed results for specific queries. This approach enhances the system's efficiency and speed, particularly when dealing with repeated or similar queries. When a query is first processed, the RAG chain retrieves relevant documents, generates a response using the language model, and then stores this response in Couchbase, with the query serving as the key.

For subsequent requests with the same query, the system checks Couchbase first. If a cached response is found, it is retrieved directly from Couchbase, bypassing the need to re-run the entire RAG process. This significantly reduces response time because the computationally expensive steps of document retrieval and response generation are skipped. Couchbase's role in this setup is to provide a fast and scalable storage solution for caching these responses, ensuring that frequently asked queries can be answered more quickly and efficiently.

try:
    queries = [
        "What happened in the match between Fullham and Liverpool?",
        "What were Luke Littler's key achievements and records in his recent PDC World Championship match?", # Repeated query
        "What happened in the match between Fullham and Liverpool?", # Repeated query
    ]

    for i, query in enumerate(queries, 1):
        print(f"\nQuery {i}: {query}")
        start_time = time.time()

        response = rag_chain.invoke(query)
        elapsed_time = time.time() - start_time
        print(f"Response: {response}")
        print(f"Time taken: {elapsed_time:.2f} seconds")

except InternalServerFailureException as e:
    if "query request rejected" in str(e):
        print("Error: Search request was rejected due to rate limiting. Please try again later.")
    else:
        print(f"Internal server error occurred: {str(e)}")
except Exception as e:
    print(f"Unexpected error occurred: {str(e)}")
Query 1: What happened in the match between Fullham and Liverpool?
Response: In the Premier League match between Fulham and Liverpool, the game ended in a 2-2 draw. Liverpool played the majority of the match with 10 men after Andy Robertson received a red card in the 17th minute. Despite being at a numerical disadvantage, Liverpool twice came from behind to equalize, with Diogo Jota scoring the final equalizer in the 86th minute. The match was characterized by Liverpool's resilient performance, with the team maintaining over 60% possession and leading in attacking metrics. Fulham's Antonee Robinson praised Liverpool, stating it didn't feel like they had a player short. Liverpool manager Arne Slot described his team's efforts as "outstanding" and "impressive" given the circumstances.
Time taken: 5.70 seconds

Query 2: What were Luke Littler's key achievements and records in his recent PDC World Championship match?
Response: Based on the provided context, Luke Littler's key achievements and records in his recent PDC World Championship second-round match against Ryan Meikle were:

1.  **Tournament Record Set Average:** He hit a tournament record average of **140.91** in the fourth and final set of the match.
2.  **Near Nine-Darter:** He was "millimetres away" from hitting a nine-dart finish, narrowly missing double 12.
3.  **Dominant Final Set:** He won the fourth set in spectacular fashion, clinching three consecutive legs in 11, 10, and 11 darts.
4.  **High Overall Average:** For the entire match, he maintained a very high average of **100.85**.
5.  **Emotional Victory:** The match was emotionally charged for Littler, who described it as "probably the toughest game I've ever played" and was overcome with emotion, cutting short his on-stage interview.
Time taken: 0.31 seconds

Query 3: What happened in the match between Fullham and Liverpool?
Response: In the Premier League match between Fulham and Liverpool, the game ended in a 2-2 draw. Liverpool played the majority of the match with 10 men after Andy Robertson received a red card in the 17th minute. Despite being at a numerical disadvantage, Liverpool twice came from behind to equalize, with Diogo Jota scoring the final equalizer in the 86th minute. The match was characterized by Liverpool's resilient performance, with the team maintaining over 60% possession and leading in attacking metrics. Fulham's Antonee Robinson praised Liverpool, stating it didn't feel like they had a player short. Liverpool manager Arne Slot described his team's efforts as "outstanding" and "impressive" given the circumstances.
Time taken: 0.26 seconds

Conclusion

You've built a high-performance semantic search engine using Couchbase Hyperscale/Composite indexes with OpenRouter DeepSeek and LangChain. For the Search Vector Index alternative, see the search_based tutorial.


This tutorial is part of a Couchbase Learning Path:
Contents
Couchbase home page link

3155 Olsen Drive
Suite 150, San Jose
CA 95117, United States

  • Company
  • About
  • Leadership
  • News & Press
  • Careers
  • Events
  • Legal
  • Contact us
  • Support
  • Developer Portal
  • Documentation
  • Forums
  • Professional Services
  • Support Login
  • Support Policy
  • Training
  • Quicklinks
  • Blog
  • Downloads
  • Online Training
  • Resources
  • Why NoSQL
  • Pricing
  • Follow us
  • Social Media Link for TwitterTwitter
  • Social Media Link for LinkedInLinkedIn
  • Social Media Link for YoutubeYouTube
  • Social Media Link for FacebookFacebook
  • Social Media Link for GitHubGitHub
  • Social Media Link for Stack OverflowStack Overflow
  • Social Media Link for DiscordDiscord

© 2026 Couchbase, Inc. Couchbase and the Couchbase logo are registered trademarks of Couchbase, Inc. All third party trademarks (including logos and icons) referenced by Couchbase, Inc. remain the property of their respective owners.

  • Terms of Use
  • Privacy Policy
  • Cookie Policy
  • Support Policy
  • Do Not Sell My Personal Information
  • Marketing Preference Center
  • Trust Center