# Implementing Semantic Search with Gemini API

## Introduction

In today's data-driven world, the sheer volume of information available can be overwhelming. Traditional keyword-based search methods often fall short when it comes to understanding the context or intent behind a query. This is where [Semantic Search](https://www.elastic.co/what-is/semantic-search) comes into play.

Semantic Search goes beyond simple keyword matching by understanding the meaning behind the words. It leverages advanced algorithms to interpret the context, making it possible to deliver more accurate and relevant search results.

With Cosmocloud's services, integrating Semantic Search into your applications is simple. This tutorial will show you how to use the Gemini API and MongoDB Atlas Vector Search to create vector embeddings and enabling intelligent search that understands user intent and delivers relevant results.

## Our Agenda

In this tutorial, we will cover:

* Implementing Semantic Search using [Vector Search Index](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-overview/) from Cosmocloud and [Vector Embeddings generated from Gemini](https://ai.google.dev/gemini-api/docs/embeddings) to implement a Semantic Search.

## Prerequisites

* Our tutorial assumes that you have already [created a project](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/getting-started) on Cosmocloud, picking the cloud of your choice.
* [Connecting your database to your Cosmocloud Project](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/getting-started/3.-connect-your-database).

## Creating Semantic Search

To implement semantic search, you will need to store vector embeddings of your data and query these embeddings using a Vector Search Index.

We will use the following sample data generated by the Gemini chatbot for hotel information -

{% code title="Hotel Sample Data" %}

```json
[
  {
    "hotelName": "Desert Oasis",
    "city": "Phoenix",
    "rating": 4.4,
    "ammenities": [
      "Spa",
      "Golf course",
      "Free breakfast"
    ],
    "price": 210.5,
    "details": "Desert Oasis lived up to its name. The spa treatments were incredibly relaxing and professionally done. The golf course was well-maintained and enjoyable to play on. The free breakfast had a good variety of choices. The hotel grounds were beautiful and well-kept. We had a very relaxing and enjoyable stay.",
  },
  // ...
]
```

{% endcode %}

The complete dataset can be found here: [Hotel's Data](https://api.npoint.io/848c8b755df98cd1b4e7)

We will create the following two APIs:

1. `POST /hotels`: This endpoint will generate vector embeddings for each record inserted into the database, based on the `details` field. The vector embeddings will be generated using the Gemini API.
2. `GET /hotels/_search`: This endpoint will allow you to search for hotels based on a query describing the hotel. The search will use the [Vector Search Index](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/resources/vector-search/create-a-vector-search-index) created in Cosmocloud Platform.

### Creating Hotel DB Model

The first step in Cosmocloud is to build your database models for the entities defined in your system. We'll start by creating our `Hotels` model.

{% hint style="info" %}
The name of the model should match the collection name (table name) in your database. Keeping it Hotels would create a collection named Hotels in your MongoDB database.
{% endhint %}

* Navigate to **Application Layer** -> **DB Models** to create new database models in Cosmocloud.
* Click on **New Model** button.
* Enter `Hotels` as the model name, provide a description, and click `Create`.
* Switch to the **Schema** tab at the top to define the schema for the `Hotels` model.

**Defining the Schema of our Hotels Model**

As Cosmocloud is connected to your MongoDB database, we can define a Document Model based schema for our `Hotels` model.

* Click on **JSON to Schema** button to quickly generate schema from a sample `Hotel` record.
* Copy the snippet below as the sample record in our `Hotels` model.

{% hint style="info" %}
We will not add `_id` as it is going to be auto-generated by our database (MongoDB) for us

The `detailsEmbeddings` field will store the vector embeddings of the `details` field which will be generated from Gemini API. We'll see this later below.
{% endhint %}

```json
{
    "hotelName": "Desert Oasis",
    "city": "Phoenix",
    "rating": 4.4,
    "ammenities": [
      "Spa",
      "Golf course",
      "Free breakfast"
    ],
    "price": 210.5,
    "details": "Desert Oasis lived up to its name. The spa treatments were incredibly relaxing and professionally done. The golf course was well-maintained and enjoyable to play on. The free breakfast had a good variety of choices. The hotel grounds were beautiful and well-kept. We had a very relaxing and enjoyable stay.",
    "detailsEmbeddings": [1.23, 1.23]
}
```

* Click on Save button to instantly generate the schema of your `Hotels` model as well as save it in the system. If the schema doesn't look as the image given below, do necessary changes manually.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FAlLqHj9bs76SOWva71gD%2Fhotels_schema.png?alt=media&#x26;token=246a4ece-15ce-4a48-b394-928dcc49cd98" alt=""><figcaption><p>Hotels Schema</p></figcaption></figure>

### Store Secret of Gemini API

After creating your Gemini API key from [Google AI Studio](https://aistudio.google.com/app/apikey), you'll need to store it as a [Custom Secret](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/resources/secrets) in Cosmocloud.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FifpScSHaQUpVx72qR9UK%2Fimage.png?alt=media&#x26;token=fa53a729-17b9-430a-ba56-f6ba3bfaff3c" alt=""><figcaption></figcaption></figure>

### Creating Subflow to Generate Embeddings

Next, we'll create a [Subflow](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/examples-how-to/reusable-flows-subflows) to generate embeddings from a text using the Gemini API. We are creating a Subflow as it will allow us to quickly reuse this flow in multiple APIs such as storing data in the database and when querying it.

1. Head over to **Application Layer** -> **Subflows**.
2. Name the subflow as `create_embeddings`.
3. Set the argument key as `text`, type as `String`, and mark it as required by setting the first option to `true`.
4. Click on `Create` button to create the new subflow.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FpAY40rBOdiQkN5oDPD2T%2Fcreate_embeddings.png?alt=media&#x26;token=9e26ae39-0a68-4b38-861b-2e264b15869e" alt=""><figcaption><p><code>create_embeddings</code> Subflow</p></figcaption></figure>

Here comes the main part, we'll now edit the main flow of generating the embeddings of a text from Gemini. Head over to the `Flow` tab.

#### Build JSON Object

* Add a [Build JSON Object](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/flow-builder/node-types/variable-nodes/json/build-json-object) node, and name it `jsonPayload`.
* This node will create the request body for the Gemini API. We are going to use `$.variables.text` which is coming as an argument to our Subflow. The JSON content should look like this:

```json
{
  "model": "models/text-embedding-004",
  "content": {
    "parts": [
      {
        "text": "$.variables.text"
      }
    ]
  }
}
```

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2F1nsGv1q1f9hS8OPOf6dQ%2FjsonPayload.png?alt=media&#x26;token=74548f90-904f-4f45-8655-3c1cecbad0e0" alt=""><figcaption><p><code>jsonPayload</code> JSON Object</p></figcaption></figure>

#### Concat Strings

* Add a [Concat Strings](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/flow-builder/node-types/variable-nodes/strings/concat-strings) node and name it as `gemini_url`.
* This node will concatenate the base URL `https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent?key=` with the `gemini_api_key` stored as a secret, forming the full API endpoint. Refer this [doc](https://ai.google.dev/gemini-api/docs/embeddings) to learn more about the Base URL.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FMIZ3I2bpzUYej6enty7f%2Fgemini_url.png?alt=media&#x26;token=5290b041-9b4d-4203-bbf8-27c21712750f" alt=""><figcaption><p><code>gemini_url</code> - Conact Strings Node</p></figcaption></figure>

#### API Call

Add an [API Call](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/flow-builder/node-types/external-nodes/api-call) node to make a POST request to the above `gemini_url` URL generated, using the `jsonPayload` as the request body. This call will generate the text embeddings.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2F1pV2PtqhWamFBdeUJvHY%2Fgemini_api_call.png?alt=media&#x26;token=27f04812-8a7f-4296-92ce-178f3a2c0170" alt=""><figcaption><p>Gemini API Call Node</p></figcaption></figure>

#### Subflow Response

Add a `Subflow Response` node at the end to return the embeddings generated by the previous `API Call` node.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FUJuPTdanuVVbhcuLnQqy%2Fsubflow_response.png?alt=media&#x26;token=01ddd294-8901-44af-9af8-27b065c68116" alt=""><figcaption><p>Subflow Response Node</p></figcaption></figure>

In the end, the flow will look like this:

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FRuLQeG9dqciRiDYBGZIV%2Fcreate_embeddings_subflow.png?alt=media&#x26;token=824dc57e-e5c7-427b-a9a4-aba936b4b067" alt=""><figcaption><p><code>create_embeddings</code> Final Subflow</p></figcaption></figure>

Pheww, a lot of work right! Take a moment to relax, you've made great progress! There's still more to come, but the upcoming sections will get even more interesting as you continue to build out your Semantic Search functionality.

### Creating POST API

In this section, we'll use the `create_embeddings` Subflow to generate embeddings for each record added to the database. These embeddings will be stored alongside the Hotels data for future querying.

Start by using the [CRUD API's Template](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/templates/crud-apis) to streamline the creation of your API. We'll specifically choose the `/hotels POST` API and the `Create Hotels RB` Request Body. This will provide a boilerplate to simplify the creation of the POST API's request body.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FSkgDEX98iMkdIW85vAzP%2Fcreate_hotel_crud.png?alt=media&#x26;token=30f8a037-b047-44b5-8104-56fff96ccfb8" alt=""><figcaption><p>Hotels CRUD API Template</p></figcaption></figure>

Once the template is applied, head over to the Flow Builder for the generated `POST /hotels` API and start customising the flow.

#### Execute Subflow

* Add an [Execute Subflow](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/flow-builder/node-types/external-nodes/execute-subflow) node and select `create_embeddings` under **Select Subflow to execute**
* Pass the `details` field from the request body to the subflow to generate the embeddings:

```json
{
  "text": "$.request.body.details"
}
```

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2F16p2dXAcESorWSNjtnJT%2Fexecute_subflow.png?alt=media&#x26;token=d093ef26-7c86-48a3-aa7b-6f7b865aab56" alt=""><figcaption><p>Execute Subflow Node</p></figcaption></figure>

#### Update JSON Object

* Add an [Update JSON Object](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/flow-builder/node-types/variable-nodes/json/update-json-object) node to update the request body JSON with generated embeddings.
* Use `$.request.body` as the **Existing Object Name** and update the `detailsEmbeddings` field in the request body with the embeddings generated by the subflow:

```json
{
  "detailsEmbeddings": "$.<EXECUTE_SUBFLOW_NODE_NAME>.result"
}
```

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2F8d5DYl6LkHLJBOzDpMzg%2Fupdate_object.png?alt=media&#x26;token=37edb802-41a2-45c7-ae7d-c9d68a0b9391" alt=""><figcaption><p>Update JSON Object Node</p></figcaption></figure>

The remaining nodes - `Insert One`, `Build JSON Object` and `HTTP Response` can be left as they are. These nodes will handle inserting the record into the database, building the final JSON response, and returning the HTTP response, respectively.

The final flow should somewhat look like this:

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FxjMEXRr6PpJOZReKbUMx%2Fpost_api.png?alt=media&#x26;token=1209615c-daaa-42f9-a6f3-272e961e0c6d" alt=""><figcaption><p>Hotels POST API Final Flow</p></figcaption></figure>

This setup ensures that each hotel record inserted into the database will have its `detailsEmbeddings` field populated with vector embeddings, making it ready for efficient semantic search.

### Vector Search Index

To enable semantic search capabilities, we'll leverage Cosmocloud's [Vector Search Index](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/resources/vector-search). This powerful feature will allow you to query data based on the vector embeddings we've generated for each hotel record.

Start by creating a new vector search index named `hotels_vector_search`. Follow the detailed instructions in this tutorial: [Create a Vector Search Index](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/resources/vector-search/create-a-vector-search-index).

{% hint style="info" %}
Gemini AI `models/text-embedding-004` creates an embedding of size 768, hence we will use this value as Number of Dimensions while creating our Vector Search below. You can check [Gemini docs](https://ai.google.dev/gemini-api/docs/embeddings#embeddings-models) for more info.
{% endhint %}

You’ll need to configure the fields according to the image below to guide your setup:

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FzdZvHzqD63NZArwM4zZK%2Fhotels_vector_search.png?alt=media&#x26;token=39f958d7-0102-44f3-ac4a-7e701165de3d" alt=""><figcaption><p>Hotels Vector Search Index</p></figcaption></figure>

By properly configuring your vector search index, you'll be set to perform advanced semantic searches on your hotel data. This index will allow you to query records not just by keyword but by the meaning and context of the hotel descriptions, resulting in more accurate and relevant search results.

### Defining Search Query Parameters

First, we’ll create a [Query Params](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/resources/models) model for the search query parameters, which will be used in the API to handle incoming queries.

1. Navigate to **Application Layer** -> **Request Models**.
2. Click on **Create Model** and set the **Model Name** as `Search Hotels QP` and the **Model Type** as `Query Params`.
3. Once created, switch to `Schema` tab and paste the following JSON inside the widget - `JSON to Schema`:

```json
{
    "query": "text",
    "limit": 10,
    "offset": 0
}
```

{% hint style="warning" %}
Don't forget to mark all query, limit and offset as **required**. (Red dot against the field name)
{% endhint %}

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2F6VhJMHmhBxCrbnMWxxWQ%2Fsearch_hotels_qp.png?alt=media&#x26;token=5b3c361f-bd59-4cb1-8625-a1201c319bd6" alt=""><figcaption><p>Search Hotels Query Parms Model</p></figcaption></figure>

### Creating the Search Query API

Next, we’ll create the `GET /hotels/_search` API to handle the search functionality.

1. Navigate to **Application Layer** -> **APIs**.
2. Click on **Create API** and choose **Start from Scratch**.
3. Name the API `Search Hotels`, set the **Request method** to `GET`, and set the endpoint to `/hotels/_search`. Select the **Query Params** as `Search Hotels QP`, which we created in the previous step.
4. Click `Create` to generate the API.

Once the template is applied, head over to the flow builder for the generated `GET /hotels/_search` API and start customising the flow.

#### Execute Subflow

* Add an [Execute Subflow](https://docs.cosmocloud.io/flow-builder/node-types/external-nodes/execute-subflow) node and select `create_embeddings` Subflow under **Select Subflow to execute**
* Pass the `query` parameter from the request query params to generate the embeddings:

```json
{
  "text": "$.request.queryParams.query"
}
```

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FCRfbOPVAoZl6XIJmOKig%2Fexecute_subflow_query.png?alt=media&#x26;token=d5b3af9c-38fa-47c7-800d-3b3d04e2284e" alt=""><figcaption><p>Execute Subflow Node</p></figcaption></figure>

#### Run Aggregation Pipeline

* Below the Execute Subflow node, add a [Run Aggregation Pipeline](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/flow-builder/node-types/database-nodes/run-aggregation-pipeline) node and select the **Database Collection** as `Hotels`. Select the **Response Type** as `Array`.
* Edit the **Aggregate Query** as follows:

```json
[
  {
    "$vectorSearch": {
      "index": "hotels_vector_search",
      "path": "detailsEmbeddings",
      "queryVector": "$.node_9.result",
      "numCandidates": 15,
      "limit": "$.request.queryParams.limit"
    }
  },
  {
    "$facet": {
      "data": [
        {
          "$project": {
            "hotelName": 1,
             "details": 1,
             "score": {
                  "$meta": "vectorSearchScore"
              }
          }
        },
        {"$skip": "$.request.queryParams.offset"},
        {"$limit": "$.request.queryParams.limit"}
      ],
      "count": [
        {"$count": "totalCount"}
      ]
    }
  }
]
```

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2F61bXTtXKqvrJXFccapru%2Frun_aggregation_pipeline.png?alt=media&#x26;token=b30b3072-2bf3-4cd5-b101-49d019b47a0b" alt=""><figcaption><p>Run Aggregation Pipeline Node</p></figcaption></figure>

#### HTTP Response

* Add an `HTTP Response` node at the end of the flow.
* Set the **Response value** to `$.<AGGREGATION_NODE_NAME>.result` and the **Status Code** to `200`.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FWMyRLcoDUAgj45ue1Oar%2Fhttp_response_query.png?alt=media&#x26;token=eab43f81-86ff-4076-8e5d-5ac4d76cf71e" alt=""><figcaption><p>HTTP Response Node</p></figcaption></figure>

The final flow for the `GET /hotels/_search` API should resemble the structure below, enabling the API to perform sophisticated semantic searches:

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FRRNv2gAEvZDbtg5RcdMD%2Fsearch_query_flow.png?alt=media&#x26;token=f4e34c92-d766-4f93-81c0-9cdb0685b73a" alt=""><figcaption><p>Search Query API Final Flow</p></figcaption></figure>

## Testing APIs

### Adding Data

To add hotel records to the database, use the `POST /hotels` API. We'll be using the [Hotel's Data](https://api.npoint.io/848c8b755df98cd1b4e7) dataset, and each record should be added individually.

{% hint style="warning" %}
Follow the instructions in the [Testing Free Tier APIs](https://app.gitbook.com/s/ZCdm9aJ8vkvDIIbg04AL/getting-started/6.-testing-free-tier-apis) document for detailed guidance on setting headers and URLs.
{% endhint %}

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FOPZ76EKmsSRtW5OUHHyG%2Fpostman_add_hotels.png?alt=media&#x26;token=7fd730fd-3488-49db-b0ee-81c3c4a074ca" alt=""><figcaption><p>Postman Add Hotels</p></figcaption></figure>

Once the data is added, inspect your database. You'll notice that a `detailsEmbeddings` field with a `768`-dimensional vector added to each record. These embeddings are generated using the Gemini API and are essential for performing semantic searches.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FmsVNjobl2GKL6GxgwGyY%2Fmongodb_embeddings.png?alt=media&#x26;token=854f832a-1420-4935-a148-66c00bc491ce" alt=""><figcaption><p>MongoDB Hotels Collection</p></figcaption></figure>

### Searching Data

To search the data, use the `GET /hotels/_search` API. This API will allow you to perform semantic searches based on the vector embeddings stored in the database.

For example, set the query parameter to `stylish city stay, rooftop bar with skyline views, well-equipped fitness center`, with limit as `10` and offset as `0`. The result will return records that are most relevant to the search query.

<figure><img src="https://1493448952-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvTMtDI5nM3rZwaURRww4%2Fuploads%2FLWMLvOSTVZ1NtN5LRA1E%2Fpostman_search.png?alt=media&#x26;token=91d6e840-e739-4b67-bef0-bcc3ca78fbdd" alt=""><figcaption><p>Postman Search Hotels</p></figcaption></figure>

In the aggregation pipeline, we added a `score` field which represents the similarity between the query input and the records. This score helps in identifying the most relevant records. To understand more about the **Vector Search Score** refer this [documentation](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/#atlas-vector-search-score).

## Conclusion

{% hint style="success" %}
Congratulations on completing this tutorial :tada:!
{% endhint %}

You've successfully navigated through the process of integrating semantic search capabilities into your application using Cosmocloud and the Gemini API. From creating a Hotels database model, generating vector embeddings, setting up a vector search index, and building both POST and GET APIs, you now have a powerful system capable of understanding and interpreting user intent at a deeper level.

Thank you for following along, and happy coding!
