# Vector Similarity Search - HNSW

### <mark style="color:purple;">Introduction to Vector Similarity Search</mark>&#x20;

Vector similarity search is a fundamental problem in computer science, with applications spanning across various domains such as information retrieval, recommendation systems, image and video search, and machine learning.&#x20;

The goal of vector similarity search is to find the most similar vectors to a given query vector from a large collection of vectors, based on a similarity metric such as <mark style="color:blue;">**Euclidean distance**</mark> or <mark style="color:blue;">**cosine similarity**</mark>.

In many real-world applications, the number of vectors in the collection can be in the millions or billions, and the dimensionality of the vectors can be high (hundreds or thousands).&#x20;

This makes exact similarity search computationally expensive and often infeasible.&#x20;

Therefore, there is a need for efficient algorithms that can perform <mark style="color:blue;">**approximate nearest neighbor (ANN)**</mark> search, trading off a small amount of accuracy for significant speedups.

<mark style="color:blue;">**Hierarchical Navigable Small World (HNSW)**</mark> graphs have emerged as a leading technology in vector similarity searches, acclaimed for unparalleled speed and accuracy.&#x20;

{% embed url="<https://arxiv.org/abs/1603.09320>" %}
HNSW
{% endembed %}

### <mark style="color:purple;">Approximate Nearest Neighbor (ANN) Algorithms</mark>&#x20;

ANN algorithms are a class of algorithms that aim to find the approximate nearest neighbours of a query vector in a large collection of vectors.&#x20;

The key idea behind ANN algorithms is to *<mark style="color:yellow;">**build an index structure that allows for fast searching**</mark>*, while <mark style="color:yellow;">**sacrificing some accuracy**</mark> compared to exact search methods.

There are several categories of ANN algorithms, each with its own strengths and weaknesses:

<mark style="color:purple;">**Tree-based methods:**</mark> These algorithms build a tree-like index structure to partition the vector space and perform a branch-and-bound search. Examples include KD-trees, ball trees, and VP-trees.

<mark style="color:purple;">**Hashing-based methods:**</mark> These algorithms use locality-sensitive hashing (LSH) functions to map similar vectors to the same hash buckets, allowing for fast retrieval of candidate neighbours. Examples include LSH Forest and Multi-Probe LSH.

<mark style="color:purple;">**Graph-based methods:**</mark> These algorithms build a graph structure where &#x65;*<mark style="color:yellow;">ach node represents a vector, and edges connect similar vectors</mark>*. The search is performed by traversing the graph starting from a set of initial nodes. Examples include Hierarchical Navigable Small World (HNSW) and Proximity Graph.

### <mark style="color:purple;">Understanding HNSW's Core</mark>

#### <mark style="color:green;">Proximity Graphs</mark>

* HNSW belongs to the class of proximity graphs, where each node represents a vector, and edges connect similar vectors based on a similarity metric (e.g., Euclidean distance).
* The key idea is that nearby vectors in the graph are likely to be the nearest neighbours of a query vector.

#### <mark style="color:green;">Probabilistic Skip Lists</mark>

* Probabilistic skip lists are a *<mark style="color:yellow;">**data structure that allows for efficient searching and insertion**</mark>* in a sorted list.
* The main idea is to create *<mark style="color:yellow;">**multiple layers of linked lists**</mark>*, with each layer skipping over some elements in the layer below.
* This allows for *<mark style="color:yellow;">**fast searching by starting at the top layer and moving down to lower layers**</mark>* when necessary.

#### <mark style="color:green;">Navigable Small World Graphs</mark>

* Navigable small world graphs are a type of graph structure that *<mark style="color:yellow;">**exhibits both local connectivity and long-range links**</mark>*.
* The local connectivity ensures that *<mark style="color:yellow;">**similar vectors are connected**</mark>*, while the long-range links allow for *<mark style="color:yellow;">**efficient navigation between distant parts of the graph**</mark>*.
* The search is performed by starting from a set of entry nodes and greedily traversing the graph towards the query vector.

Hierarchical NSW (HNSW) combines the ideas of proximity graphs, probabilistic skip lists, and navigable small world graphs to create a multi-layer graph structure that allows for efficient and scalable approximate nearest neighbor search.

To illustrate how they all work together, consider the following analogy:

Imagine you are in a large library, and you want to find a specific book (the <mark style="color:blue;">**query vector**</mark>). The books are organised on shelves based on their similarity (<mark style="color:blue;">**proximity graph**</mark>).&#x20;

To quickly locate the book, you start at the library's entrance and follow signs that direct you to the most relevant section (<mark style="color:blue;">**navigable small world graph**</mark>).  Once you reach the section, you can efficiently search for the book by skipping some shelves and only examining the most promising ones (<mark style="color:blue;">**probabilistic skip list**</mark>).  By combining these strategies, you can find the desired book much faster than searching through every shelf in the library.

Similarly, HNSW *<mark style="color:yellow;">**combines these concepts to create a multi-layer graph structure**</mark>* that allows for efficient navigation and search in high-dimensional vector spaces.

### <mark style="color:purple;">Graph Construction and Parameter Tuning in HNSW</mark>

The graph construction process in HNSW is governed by a probability function that determines the insertion layer for each new vector.&#x20;

The probability of inserting a vector at layer l is given by:

$$
P(l) = 1 / (m\_L ^ l)
$$

where $$m\_L$$is the <mark style="color:blue;">**level multiplier**</mark>, a hyperparameter that *<mark style="color:yellow;">**controls the growth rate of the graph**</mark>*.

The insertion process works as follows:

1. Randomly select a layer $$l$$ based on the probability function $$v$$.
2. Find the nearest neighbours of the new vector at layer $$l$$ using a <mark style="color:blue;">**greedy search**</mark>.
3. Create bi-directional edges between the new vector and its nearest neighbours.
4. Repeat steps 1-3 for all layers up to $$l$$.

The graph construction process is affected by several key parameters:

* $$m\_L$$: The level multiplier that controls the growth rate of the graph. A larger $$m\_L$$ leads to a more compact graph with fewer layers, while a smaller $$m\_L$$ leads to a taller graph with more layers.
* $$M$$: The maximum number of outgoing edges per node. A larger $$M$$ leads to a more connected graph, which can improve recall but also increases memory usage.
* <mark style="color:yellow;">**`efConstruction`**</mark>: The size of the dynamic candidate list during graph construction. A larger <mark style="color:yellow;">**`efConstruction`**</mark> leads to a more thorough search for nearest neighbours, which can improve the quality of the graph but also increases construction time.

#### <mark style="color:green;">Graph Construction and Parameter Tuning in HNSW</mark>&#x20;

Let's consider a small example to demonstrate how the probability function affects the distribution of vectors across different layers and how this impacts the graph's performance.

Suppose we have a *<mark style="color:yellow;">**dataset of 10 vectors**</mark>* and set the level multiplier $$(m\_L)$$to <mark style="color:yellow;">**2**</mark>. The probability of a vector being inserted at layer $$l$$ is given by:

$$P(l) = 1 / (2^l)$$

<mark style="color:purple;">**Layer 0:**</mark> $$P(0) = 1 / (2^0) = 1$$

<mark style="color:purple;">**Layer 1:**</mark> $$P(1) = 1 / (2^1) = 0.5$$

<mark style="color:purple;">**Layer 2:**</mark> $$P(2) = 1 / (2^2) = 0.25$$

In this case:

1. all 10 vectors will be inserted at layer 0
2. 5 vectors (on average) will be inserted at layer 1
3. and 2-3 vectors (on average) will be inserted at layer 2.

This *<mark style="color:yellow;">**distribution of vectors across layers creates a hierarchical structure**</mark>* that allows for efficient search.

If we increase $$m\_L$$ to <mark style="color:yellow;">**4**</mark>, the *<mark style="color:yellow;">**probability distribution changes**</mark>*:

<mark style="color:purple;">**Layer 0:**</mark> $$P(0) = 1 / (4^0) = 1$$

<mark style="color:purple;">**Layer 1:**</mark> $$P(1) = 1 / (4^1) = 0.25$$

<mark style="color:purple;">**Layer 2:**</mark> $$P(2) = 1 / (4^2) = 0.0625$$

With $$m\_L$$ = <mark style="color:yellow;">**4**</mark>

1. all 10 vectors will still be inserted at layer 0
2. but only 2-3 vectors (on average) will be inserted at layer 1
3. and likely no vectors will be inserted at layer 2.&#x20;

This results in a flatter graph with fewer layers, which can reduce search time but may also reduce recall.

<figure><img src="https://1839612753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpV8SlQaC976K9PPsjApL%2Fuploads%2FUK8EZrMpUmAFq7P6ptjQ%2Fimage.png?alt=media&#x26;token=2d2e3213-52d4-43c1-a70e-8e047b1db432" alt=""><figcaption><p>The search process through a NSW graph. Starting at a pre-defined entry point, the algorithm greedily traverses to connected vertices that are nearer to the query vector</p></figcaption></figure>

<figure><img src="https://1839612753-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FpV8SlQaC976K9PPsjApL%2Fuploads%2Fwxtjnhz7rQ950e9tTUmq%2Fimage.png?alt=media&#x26;token=848f77dd-0045-4291-8ce0-c9b5535f9aa8" alt=""><figcaption><p>Layered graph of HNSW, the top layer is our entry point and contains only the longest links, as we move down the layers, the link lengths become shorter and more numerous.</p></figcaption></figure>

### <mark style="color:purple;">Hierarchical Navigable Small World (HNSW) benefits</mark>&#x20;

The Hierarchical Navigable Small World (HNSW) algorithm offers several benefits over the basic Navigable Small World (NSW) approach, addressing some of its limitations and improving the state-of-the-art in <mark style="color:blue;">**K-Approximate Nearest Neighbor (K-ANN)**</mark> search.&#x20;

<mark style="color:green;">**Here are the key advantages of HNSW**</mark>

<mark style="color:purple;">**Excellent performance:**</mark> HNSW outperforms other open-source algorithms on a wide variety of datasets, particularly in the case of high-dimensional data. Even on datasets where NSW struggled, HNSW was able to achieve the best results.

<mark style="color:purple;">**Robustness:**</mark> HNSW is applicable in generalised metric spaces and performs well on any dataset tested in the paper.  This eliminates the need to select a specific algorithm for a particular problem, making HNSW attractive for practical applications. The algorithm's robustness is particularly important when dealing with data that has a complex structure with varying effective dimensionality across different scales.

<mark style="color:purple;">**Continuous incremental indexing:**</mark> HNSW supports continuous incremental indexing, allowing for efficient updates to the index as new data points are added.

<mark style="color:purple;">**Approximation of k-NN and relative neighbourhood graphs:**</mark> As a by-product of the index construction, HNSW can efficiently approximate k-NN and relative neighbourhood graphs, which can be useful for various applications.

<mark style="color:purple;">**Logarithmic scalability:**</mark> Due to its hierarchical structure and the use of navigable small world graphs, HNSW achieves logarithmic scalability in search complexity, making it more efficient than NSW, which has polylogarithmic complexity.

#### <mark style="color:green;">Limitations to HNSW compared to NSW</mark>

<mark style="color:purple;">**Loss of distributed search capability**</mark>

The search in HNSW always starts from the top layer, which makes it difficult to distribute the structure using the same techniques as in NSW.&#x20;

This is due to the potential congestion of the higher layer elements. While simple workarounds like data partitioning can be used to distribute the structure, the total parallel throughput of the system may not scale well with the number of computer nodes.

Despite this limitation, there are potential ways to make HNSW distributed, similar to the techniques used in skip lists. This could lead to even better distributed performance compared to NSW due to HNSW's logarithmic scalability and ideally uniform load on the nodes.

### <mark style="color:purple;">**Implementing HNSW with FAISS**</mark>

FAISS, a library developed by Facebook AI, provides a practical framework for implementing HNSW.

{% embed url="<https://faiss.ai/index.html>" %}
FAISS Documentation for your reference
{% endembed %}

It simplifies the complex process of setting up an HNSW index, allowing for straightforward customisation of key parameters such as the number of neighbours $$M$$, the construction efficiency (<mark style="color:yellow;">**`efConstruction`**</mark>), and the search efficiency (<mark style="color:yellow;">**`efsearch`**</mark>).&#x20;

Through Faiss, users can readily experiment with these settings to find the optimal configuration for their specific needs.

### <mark style="color:purple;">**The Impact of Parameters on Performance**</mark>

Implementing HNSW with FAISS FAISS is a library for efficient similarity search and clustering of dense vectors, developed by Facebook AI Research. It provides an implementation of HNSW that can be easily integrated into Python projects.

Here's a step-by-step guide on how to set up an HNSW index using FAISS:

#### <mark style="color:green;">**Install FAISS**</mark>

```bash
pip install faiss-cpu
```

#### <mark style="color:green;">Import the necessary modules</mark>

```python
import numpy as np
import faiss
```

#### <mark style="color:green;">Prepare your data</mark>

```python
# d: dimensionality of vectors
# nb: number of vectors in the database
# nq: number of query vectors
d = 128
nb = 100000
nq = 10000
xb = np.random.random((nb, d)).astype('float32')
xq = np.random.random((nq, d)).astype('float32')
```

#### <mark style="color:green;">Create an HNSW index</mark>

```python
# M: maximum number of outgoing edges per node
# ef_construction: size of the dynamic candidate list during construction
M = 16
ef_construction = 200
index = faiss.IndexHNSWFlat(d, M)
index.hnsw.efConstruction = ef_construction
```

#### <mark style="color:green;">Add vectors to the index</mark>

```python
index.add(xb)
```

#### <mark style="color:green;">Search for nearest neighbours</mark>

```python
# k: number of nearest neighbors to retrieve
# ef_search: size of the dynamic candidate list during searching
k = 10
ef_search = 100
index.hnsw.efSearch = ef_search
distances, indices = index.search(xq, k)
```

<mark style="color:green;">The key parameters to tune are:</mark>

* <mark style="color:yellow;">**`M`**</mark>: A larger <mark style="color:yellow;">**`M`**</mark> leads to a more connected graph, which can improve recall but also increases memory usage.
* <mark style="color:yellow;">**`ef_construction`**</mark>: A larger <mark style="color:yellow;">**`ef_construction`**</mark> leads to a more thorough search for nearest neighbours during construction, which can improve the quality of the graph but also increases construction time.
* <mark style="color:yellow;">**`ef_search`**</mark>: A larger <mark style="color:yellow;">**`ef_search`**</mark> leads to a more thorough search for nearest neighbours during querying, which can improve recall but also increases search time.

### <mark style="color:purple;">Enhancing HNSW's Performance</mark>

<mark style="color:blue;">**Product Quantization (PQ)**</mark> is a technique that compresses high-dimensional vectors into compact codes by *<mark style="color:yellow;">**dividing each vector into**</mark>* $$m$$ *<mark style="color:yellow;">**subvectors and quantizing each subvector independently**</mark>* using a small codebook.&#x20;

This compression allows for reduced memory usage but introduces some approximation error, which can lead to a small loss in recall.

The *<mark style="color:yellow;">**trade-off between memory usage and accuracy**</mark>* can be controlled by <mark style="color:blue;">**adjusting the parameter**</mark><mark style="color:blue;">s</mark> $$m$$ (number of subvectors) and bits (number of bits per subvector).&#x20;

Increasing $$m$$ and bits leads to more accurate quantization but also increases memory usage.&#x20;

Conversely, decreasing m and bits reduces memory usage but may result in a larger loss in recall.

For example, suppose we have a dataset of 1 million 128-dimensional vectors.&#x20;

Using PQ with $$m=8$$and $$bits=8$$, *<mark style="color:yellow;">**each vector is compressed**</mark>* from 128 × 4 bytes (512 bytes) to 8 bytes, achieving a <mark style="color:blue;">**64× reduction in memory usage**</mark>.&#x20;

However, this compression may result in a *<mark style="color:yellow;">**small loss in recall**</mark>*, e.g., from 0.95 to 0.93. By increasing m and bits, we can improve recall at the cost of higher memory usage.

### <mark style="color:purple;">Product Quantization (PQ) and Inverted File System (IVF)</mark>

<mark style="color:blue;">**Product Quantization (PQ)**</mark> is a technique for compressing high-dimensional vectors into compact codes, which can significantly reduce memory usage at the cost of a small loss in accuracy.&#x20;

In PQ, each vector is divided into m subvectors, and each subvector is quantized independently using a codebook of size $$2^b$$. The resulting code for each vector is a concatenation of the indices of the nearest codewords for each subvector.

To use PQ with HNSW in FAISS, you can create an <mark style="color:yellow;">**`IndexHNSWPQ`**</mark> object instead of <mark style="color:yellow;">**`IndexHNSWFlat`**</mark>:

```python
# m: number of subvectors
# bits: number of bits per subvector
m = 8
bits = 8
index = faiss.IndexHNSWPQ(d, M, m, bits)
```

#### <mark style="color:green;">Inverted File System (IVF)</mark>

An <mark style="color:blue;">**Inverted File System (IVF)**</mark> is a technique that speeds up the search process in HNSW by partitioning the vector space into <mark style="color:blue;">**Voronoi cells**</mark> and maintaining an inverted list of vectors for each cell.&#x20;

During search, only the most relevant cells are visited, reducing the number of distance computations required.

<details>

<summary><mark style="color:green;"><strong>Voronoi Cells in IVF</strong></mark></summary>

Each cell encompasses all points closer to its centroid than to any other centroid.  These cells facilitate the organisation of vectors into a structured format that allows for efficient querying:

* <mark style="color:purple;">**Quantization:**</mark> The process of mapping vectors to the nearest Voronoi centroid reduces the continuous vector space into a discrete set of cells.
* <mark style="color:purple;">**Efficiency**</mark><mark style="color:purple;">:</mark> By allocating vectors to these cells, IVF reduces the search space to the most relevant cells, based on proximity to the query vector's centroid.
* <mark style="color:purple;">**Impact on Search**</mark><mark style="color:purple;">:</mark> The granularity of the partitioning (determined by the number of cells) directly influences search accuracy and speed. More cells mean higher precision but potentially slower searches due to increased index overhead.

</details>

<details>

<summary><mark style="color:green;">Centroid Vector</mark></summary>

A **centroid vector** represents the centre of a Voronoi cell in the context of an Inverted File System (IVF).

It is the average (mean) of all the vectors within a cell and serves as the reference point for that cell. When partitioning the vector space, each vector is associated with the nearest centroid, effectively organising the vectors into distinct, non-overlapping groups.

**Key Points:**

* <mark style="color:purple;">**Role in IVF:**</mark> Serves as the basis for partitioning the vector space and efficiently searching by narrowing down the target search area to the most relevant cells.
* <mark style="color:purple;">**Determination:**</mark> Typically determined using clustering algorithms such as k-means, where the centroid is the mean of all vectors assigned to a cell.
* <mark style="color:purple;">**Functionality:**</mark> Enables a fast approximation of proximity by comparing a query vector primarily with centroid vectors instead of all vectors in the dataset, thereby speeding up the search process.

</details>

Here's how IVF works:

1. The vector space is partitioned into a set of <mark style="color:blue;">**Voronoi cells**</mark> using a quantizer (e.g., k-means clustering). Each cell is represented by a <mark style="color:blue;">**centroid vector**</mark>.
2. For each vector in the dataset, the nearest centroid is found, and the vector is added to the corresponding inverted list.
3. During search, the query vector is first compared to the centroids of the Voronoi cells to identify the most relevant cells.
4. The search is then performed only within the inverted lists of the selected cells, significantly reducing the number of distance computations.

By limiting the search to a small subset of the dataset, IVF can significantly speed up the search process, especially for large datasets.&#x20;

The number of Voronoi cells (nlist) is a key parameter that affects the trade-off between search speed and accuracy. Increasing nlist leads to smaller cells and more focused searches but also increases the overhead of comparing the query vector to the centroids.

In summary, IVF is an effective technique for speeding up HNSW searches by partitioning the vector space and focusing the search on the most relevant regions, reducing the number of distance computations required.

### <mark style="color:purple;">Using IVF with HNSW in FAISS</mark>

To use IVF with HNSW in FAISS, you can create an <mark style="color:yellow;">**`IndexHNSWSQ`**</mark> object and wrap it with an <mark style="color:yellow;">**`IndexIVFFlat`**</mark> object:

```python
# nlist: number of Voronoi cells
nlist = 1024
quantizer = faiss.IndexHNSWSQ(d, M)
index = faiss.IndexIVFFlat(quantizer, d, nlist)
```

Here's an example that demonstrates the effectiveness of using PQ and IVF with HNSW:

```python
import numpy as np
import faiss

d = 128
nb = 1000000
nq = 10000
xb = np.random.random((nb, d)).astype('float32')
xq = np.random.random((nq, d)).astype('float32')

# HNSW without PQ and IVF
index_hnsw = faiss.IndexHNSWFlat(d, M)
index_hnsw.add(xb)
distances_hnsw, indices_hnsw = index_hnsw.search(xq, k)

# HNSW with PQ
index_hnsw_pq = faiss.IndexHNSWPQ(d, M, m, bits)
index_hnsw_pq.add(xb)
distances_hnsw_pq, indices_hnsw_pq = index_hnsw_pq.search(xq, k)

# HNSW with IVF
quantizer = faiss.IndexHNSWSQ(d, M)
index_ivf_hnsw = faiss.IndexIVFFlat(quantizer, d, nlist)
index_ivf_hnsw.train(xb)
index_ivf_hnsw.add(xb)
distances_ivf_hnsw, indices_ivf_hnsw = index_ivf_hnsw.search(xq, k)

print("HNSW without PQ and IVF:")
print("Recall@10:", faiss.eval_recall_at_k(index_hnsw, xq, xb, k))
print("Memory usage:", index_hnsw.ntotal * d * 4 / 1024 / 1024, "MB")

print("HNSW with PQ:")
print("Recall@10:", faiss.eval_recall_at_k(index_hnsw_pq, xq, xb, k))
print("Memory usage:", index_hnsw_pq.ntotal * (m * bits / 8) / 1024 / 1024, "MB")

print("HNSW with IVF:")
print("Recall@10:", faiss.eval_recall_at_k(index_ivf_hnsw, xq, xb, k))
print("Memory usage:", index_ivf_hnsw.ntotal * d * 4 / 1024 / 1024, "MB")
```

In this example, we compare the performance of HNSW without PQ and IVF, HNSW with PQ, and HNSW with IVF.&#x20;

The results show that using *<mark style="color:yellow;">**PQ can significantly reduce memory usage**</mark>* (by a factor of $$m \* bits / 32$$) at the cost of a small loss in recall, while using *<mark style="color:yellow;">**IVF can significantly speed up the search process**</mark>* (by a factor of $$nlist$$) without affecting memory usage.

### <mark style="color:purple;">Conclusion</mark>

In conclusion, Hierarchical Navigable Small World (HNSW) graphs have emerged as a powerful and efficient solution for vector similarity search, offering significant advantages over traditional approaches.&#x20;

By combining the concepts of proximity graphs, probabilistic skip lists, and navigable small world graphs, HNSW creates a multi-layer graph structure that enables fast and accurate nearest neighbor search, even in high-dimensional spaces.

The documentation provided an in-depth exploration of HNSW's core concepts, graph construction process, and parameter tuning, along with practical examples of its implementation using the FAISS library.&#x20;

The benefits of HNSW, such as excellent performance, robustness, and logarithmic scalability, make it an attractive choice for a wide range of applications, including information retrieval, recommendation systems, and machine learning.

Moreover, the documentation discussed advanced techniques like Product Quantization (PQ) and Inverted File System (IVF), which can further enhance HNSW's performance by reducing memory usage and speeding up the search process.&#x20;

The examples and code snippets provided throughout the documentation serve as valuable resources for practitioners looking to implement HNSW in their projects.

As the field of vector similarity search continues to evolve, HNSW has the potential to play a crucial role in enabling fast and accurate similarity search in large-scale datasets.&#x20;

Future developments in HNSW may include further optimizations, extensions to handle dynamic datasets, and integration with other machine learning techniques to enable more advanced applications.

In summary, the comprehensive documentation on HNSW provides a solid foundation for understanding and implementing this powerful algorithm in various domains.&#x20;

By leveraging the insights and techniques presented in this documentation, practitioners can harness the full potential of HNSW to build efficient and effective vector similarity search systems.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://training.continuumlabs.ai/knowledge/vector-databases/vector-similarity-search-hnsw.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
