== Camel Example Spring Boot Milvus Vector Search

This example shows how to use Apache Camel with Milvus as a vector database to perform
similarity search on European cities based on geographic and demographic features.

OpenAI (via Ollama) is used to generate city data (country, latitude, longitude,
population in millions) which is stored as a 3-dimensional feature vector in Milvus.
A similarity search then finds cities with the most similar characteristics.

The pipeline works end-to-end:

1. **Insert**: LLM returns raw city data (`Rome: Italy;40.41;12.51;2.81`) → `VectorUtils` normalizes to 0-1 range → stored in Milvus as 3D vector
2. **Search**: LLM returns raw Rome coordinates → normalized → Milvus L2 similarity search → finds Rome, Milan, Zurich as the 3 closest cities
3. **Explain**: The results are sent back to the LLM which provides a natural language explanation with approximate distances

=== Prerequisites

1. A running Milvus instance (using https://camel.apache.org/manual/camel-jbang.html[Camel JBang]):

----
camel infra run milvus
----

2. A running Ollama instance with the required model:

----
ollama serve
ollama pull granite4:3b
----

=== How to run

You can run this example using:

----
mvn spring-boot:run
----

=== What happens

When the application starts, it executes the following pipeline:

1. **Create Collection** - Creates a `cities` collection in Milvus with four fields: `id` (Int64 primary key), `city` (VarChar), `country` (VarChar), and `features` (3-dimensional FloatVector). The `features` vector encodes each city as `[latitude, longitude, population_millions]`, normalized to 0-1 range. An IVF_FLAT index with L2 distance metric is created on the vector field.
2. **Insert Cities** - Reads city names from `input/cities.txt`. For each city, it asks the LLM via `openai:chat-completion` to provide the country, latitude, longitude, and population (in millions). The response is parsed using Camel Simple expressions and normalized to a float vector via `VectorUtils`, then inserted into Milvus.
3. **Similarity Search** - Searches for the 3 cities most similar to Rome based on L2 distance of their feature vectors. The search vector is also generated by the LLM.
4. **Explain** - The search results are sent back to the LLM which provides a natural language explanation with approximate distances.

=== Expected output

----
Creating Milvus collection: cities
Collection created successfully
Index created successfully
Processing cities from: cities.txt
City data for Rome: Italy;41.90;12.50;2.87
Inserted Rome into Milvus
City data for Paris: France;48.86;2.35;2.16
Inserted Paris into Milvus
...
All cities indexed. Starting similarity search...
Searching for three closest cities to: Rome
Search vector for Rome: 41.90;12.50;2.87
Closest cities to Rome: [{rank=1, city=Rome, country=Italy}, {rank=2, city=Milan, country=Italy}, {rank=3, city=Zurich, country=Switzerland}].
Answer: Rome: 0 km, Milan: 200 km, Zurich: 400 km
----

NOTE: The exact similarity results may vary depending on the LLM responses.

=== Configuration

Edit `src/main/resources/application.properties` to configure:

* `camel.component.milvus.host` - Milvus server host (default: localhost)
* `camel.component.milvus.port` - Milvus gRPC port (default: 19530)
* `camel.component.openai.base-url` - OpenAI-compatible API base URL (default: Ollama at http://localhost:11434/v1)
* `camel.component.openai.model` - Chat completion model (default: granite4:3b)

To use OpenAI instead of Ollama, change the base URL and set your API key:

----
camel.component.openai.base-url=https://api.openai.com/v1
camel.component.openai.api-key=${OPENAI_API_KEY}
camel.component.openai.model=gpt-4o-mini
----

== Trying out the example on OpenShift

First, start with creating a new OpenShift project:

----
$ oc new-project csb-example-milvus
----

Deploy a Milvus standalone instance:

----
$ cat << EOF| oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: milvus
spec:
  replicas: 1
  selector:
    matchLabels:
      app: milvus
  template:
    metadata:
      labels:
        app: milvus
    spec:
      containers:
        - name: milvus
          image: milvusdb/milvus:latest
          command: ["milvus", "run", "standalone"]
          ports:
            - containerPort: 19530
            - containerPort: 9091
          env:
            - name: ETCD_USE_EMBED
              value: "true"
            - name: COMMON_STORAGETYPE
              value: local
---
apiVersion: v1
kind: Service
metadata:
  name: milvus
spec:
  selector:
    app: milvus
  ports:
    - name: grpc
      port: 19530
      targetPort: 19530
    - name: http
      port: 9091
      targetPort: 9091
EOF
----

Deploy an Ollama instance with the required model:

----
$ cat << EOF| oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ollama
  template:
    metadata:
      labels:
        app: ollama
    spec:
      containers:
        - name: ollama
          image: ollama/ollama:latest
          ports:
            - containerPort: 11434
---
apiVersion: v1
kind: Service
metadata:
  name: ollama
spec:
  selector:
    app: ollama
  ports:
    - port: 11434
      targetPort: 11434
EOF
----

Wait for both pods to be ready, then pull the required model:

----
$ oc wait --for=condition=available deployment/milvus deployment/ollama --timeout=300s
$ oc exec deployment/ollama -- ollama pull granite4:3b
----

=== How to run

The application is deployed using the `openshift-maven-plugin` that takes care of creating all the necessary OpenShift resources.

Simply use the following command to deploy the application:

----
$ mvn clean package -Popenshift
----

After the application pod reaches the `Ready` state, you can try the same steps as in the local machine deployment.

To view the application logs, use `oc logs deployment/csb-milvus`

=== Help and contributions

If you hit any problem using Camel or have some feedback, then please
https://camel.apache.org/support.html[let us know].

We also love contributors, so
https://camel.apache.org/contributing.html[get involved] :-)

The Camel riders!
