Skip to content

Commit 2e013a5

Browse files
committed
Adding qdrant example
1 parent 102730d commit 2e013a5

7 files changed

Lines changed: 321 additions & 2 deletions

File tree

README.adoc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,18 @@ readme's instructions.
2727
=== Examples
2828

2929
// examples: START
30-
Number of Examples: 69 (0 deprecated)
30+
Number of Examples: 70 (0 deprecated)
3131

3232
[width="100%",cols="4,2,4",options="header"]
3333
|===
3434
| Example | Category | Description
3535

3636
| link:ai-agent/README.adoc[Ai Agent] (ai-agent) | AI | An example showing how to work with Camel Spring AI for chat, tools, and vector store
3737

38+
| link:docling/readme.adoc[Docling] (docling) | AI | An example showing how to convert documents to Markdown using Camel Docling component and Spring Boot
39+
40+
| link:qdrant/readme.adoc[Qdrant] (qdrant) | AI | An example showing how to work with Qdrant vector database using the Camel Qdrant component and Spring Boot
41+
3842
| link:aot-basic/readme.adoc[Aot Basic] (aot-basic) | AOT | Example on how to leverage Spring Boot AOT in Camel Spring Boot
3943

4044
| link:endpointdsl/readme.adoc[Endpointdsl] (endpointdsl) | Beginner | Using type-safe Endpoint DSL
@@ -89,7 +93,6 @@ Number of Examples: 69 (0 deprecated)
8993

9094
| link:multi-datasource-2pc/readme.adoc[Multi Datasource 2pc] (multi-datasource-2pc) | Database | An example showing how to work with Camel and Spring Boot using multiple pooled datasources with two-phase commit
9195

92-
| link:docling/readme.adoc[Docling] (docling) | Document Processing | An example showing how to convert documents to Markdown using Camel Docling component and Spring Boot
9396

9497
| link:load-balancer-eip/README.adoc[Load Balancer Eip] (load-balancer-eip) | EIP | An example showing Load Balancer EIP with Camel and Spring Boot
9598

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
<module>platform-http</module>
7373
<module>platform-http-proxy</module>
7474
<module>pojo</module>
75+
<module>qdrant</module>
7576
<module>rabbitmq</module>
7677
<module>reactive-streams</module>
7778
<module>resilience4j</module>

qdrant/pom.xml

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one or more
5+
contributor license agreements. See the NOTICE file distributed with
6+
this work for additional information regarding copyright ownership.
7+
The ASF licenses this file to You under the Apache License, Version 2.0
8+
(the "License"); you may not use this file except in compliance with
9+
the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0"
21+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
23+
24+
<modelVersion>4.0.0</modelVersion>
25+
<parent>
26+
<groupId>org.apache.camel.springboot.example</groupId>
27+
<artifactId>examples</artifactId>
28+
<version>4.19.0-SNAPSHOT</version>
29+
</parent>
30+
31+
<artifactId>camel-example-spring-boot-qdrant</artifactId>
32+
<name>Camel SB Examples :: Qdrant</name>
33+
<description>An example showing how to work with the Camel Qdrant component and Spring Boot</description>
34+
35+
<properties>
36+
<category>AI</category>
37+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
38+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
39+
</properties>
40+
41+
<dependencyManagement>
42+
<dependencies>
43+
<!-- Spring Boot BOM -->
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-dependencies</artifactId>
47+
<version>${spring-boot-version}</version>
48+
<type>pom</type>
49+
<scope>import</scope>
50+
</dependency>
51+
<!-- Camel BOM -->
52+
<dependency>
53+
<groupId>org.apache.camel.springboot</groupId>
54+
<artifactId>camel-spring-boot-bom</artifactId>
55+
<version>${project.version}</version>
56+
<type>pom</type>
57+
<scope>import</scope>
58+
</dependency>
59+
</dependencies>
60+
</dependencyManagement>
61+
62+
<dependencies>
63+
<!-- Spring Boot -->
64+
<dependency>
65+
<groupId>org.springframework.boot</groupId>
66+
<artifactId>spring-boot-starter</artifactId>
67+
</dependency>
68+
69+
<!-- Camel -->
70+
<dependency>
71+
<groupId>org.apache.camel.springboot</groupId>
72+
<artifactId>camel-spring-boot-starter</artifactId>
73+
</dependency>
74+
<dependency>
75+
<groupId>org.apache.camel.springboot</groupId>
76+
<artifactId>camel-qdrant-starter</artifactId>
77+
</dependency>
78+
79+
<!-- test -->
80+
<dependency>
81+
<groupId>org.springframework.boot</groupId>
82+
<artifactId>spring-boot-starter-test</artifactId>
83+
<scope>test</scope>
84+
</dependency>
85+
<dependency>
86+
<groupId>org.apache.camel</groupId>
87+
<artifactId>camel-test-spring-junit6</artifactId>
88+
<version>${camel-version}</version>
89+
<scope>test</scope>
90+
</dependency>
91+
</dependencies>
92+
93+
<build>
94+
<plugins>
95+
<plugin>
96+
<groupId>org.springframework.boot</groupId>
97+
<artifactId>spring-boot-maven-plugin</artifactId>
98+
<version>${spring-boot-version}</version>
99+
<executions>
100+
<execution>
101+
<goals>
102+
<goal>repackage</goal>
103+
</goals>
104+
</execution>
105+
</executions>
106+
</plugin>
107+
</plugins>
108+
</build>
109+
</project>

qdrant/readme.adoc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
== Camel Example Spring Boot and Qdrant
2+
3+
This example shows how to work with Apache Camel and the Qdrant vector database using Spring Boot.
4+
5+
The example demonstrates the operations from the https://qdrant.tech/documentation/quickstart/[Qdrant Quickstart]:
6+
creating a collection, upserting points with vectors and payloads, performing similarity searches (unfiltered and filtered),
7+
deleting points, and cleaning up.
8+
9+
=== Prerequisites
10+
11+
Before running this example, you need a running Qdrant instance. Start one using Docker:
12+
13+
----
14+
docker run -p 6333:6333 -p 6334:6334 \
15+
-v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
16+
qdrant/qdrant
17+
----
18+
19+
Port 6333 is the REST API and port 6334 is the gRPC API (used by this example).
20+
21+
=== How to run
22+
23+
You can run this example using:
24+
25+
----
26+
mvn spring-boot:run
27+
----
28+
29+
=== What happens
30+
31+
When the application starts, it executes the following operations sequentially:
32+
33+
1. **Create Collection** - Creates `test_collection` with vector size=4 and Dot distance metric
34+
2. **Upsert Points** - Inserts 6 points with 4-dimensional vectors and city payloads (Berlin, London, Tokyo, New York, Rome, Madrid)
35+
3. **Similarity Search** - Finds the 3 nearest points to the query vector `[0.2, 0.1, 0.9, 0.7]`
36+
4. **Filtered Search** - Same similarity search but filtered to only return results where city="London"
37+
5. **Delete Points** - Deletes points where city="Madrid"
38+
6. **Delete Collection** - Removes `test_collection`
39+
40+
=== Configuration
41+
42+
Edit `src/main/resources/application.properties` to configure:
43+
44+
* `camel.component.qdrant.host` - Qdrant server host (default: localhost)
45+
* `camel.component.qdrant.port` - Qdrant gRPC port (default: 6334)
46+
47+
=== Help and contributions
48+
49+
If you hit any problem using Camel or have some feedback, then please
50+
https://camel.apache.org/support.html[let us know].
51+
52+
We also love contributors, so
53+
https://camel.apache.org/contributing.html[get involved] :-)
54+
55+
The Camel riders!
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.apache.camel.example.springboot.qdrant;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class Application {
8+
9+
public static void main(String[] args) {
10+
SpringApplication.run(Application.class, args);
11+
}
12+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package org.apache.camel.example.springboot.qdrant;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
import io.qdrant.client.ConditionFactory;
7+
import io.qdrant.client.PointIdFactory;
8+
import io.qdrant.client.ValueFactory;
9+
import io.qdrant.client.VectorsFactory;
10+
import io.qdrant.client.grpc.Collections;
11+
import io.qdrant.client.grpc.Common;
12+
import io.qdrant.client.grpc.Points;
13+
import org.apache.camel.builder.RouteBuilder;
14+
import org.apache.camel.component.qdrant.QdrantAction;
15+
import org.apache.camel.component.qdrant.QdrantHeaders;
16+
import org.springframework.stereotype.Component;
17+
18+
@Component
19+
public class QdrantRoute extends RouteBuilder {
20+
21+
private static final String COLLECTION = "test_collection";
22+
23+
@Override
24+
public void configure() throws Exception {
25+
26+
// Register city filter bean for filtered similarity search
27+
Common.Filter cityFilter = Common.Filter.newBuilder()
28+
.addMust(ConditionFactory.matchKeyword("city", "London"))
29+
.build();
30+
getContext().getRegistry().bind("cityFilter", cityFilter);
31+
32+
// Main orchestrator route - fires once
33+
from("timer:qdrant-quickstart?repeatCount=1&delay=1000")
34+
.routeId("qdrant-quickstart")
35+
.log("Starting Qdrant Quickstart Demo")
36+
.to("direct:createCollection")
37+
.to("direct:upsertPoints")
38+
.to("direct:similaritySearch")
39+
.to("direct:filteredSearch")
40+
.to("direct:deletePoints")
41+
.to("direct:deleteCollection")
42+
.log("Qdrant Quickstart Demo completed successfully");
43+
44+
// Create collection with vector size=4 and Dot distance metric
45+
from("direct:createCollection")
46+
.routeId("create-collection")
47+
.log("Creating collection: " + COLLECTION)
48+
.setHeader(QdrantHeaders.ACTION).constant(QdrantAction.CREATE_COLLECTION)
49+
.setBody().constant(
50+
Collections.VectorParams.newBuilder()
51+
.setSize(4)
52+
.setDistance(Collections.Distance.Dot)
53+
.build())
54+
.to("qdrant:" + COLLECTION)
55+
.log("Collection created successfully");
56+
57+
// Upsert 6 points with 4D vectors and city payloads
58+
from("direct:upsertPoints")
59+
.routeId("upsert-points")
60+
.log("Upserting points into collection: " + COLLECTION)
61+
.setHeader(QdrantHeaders.ACTION).constant(QdrantAction.UPSERT)
62+
.setBody().constant(List.of(
63+
Points.PointStruct.newBuilder()
64+
.setId(PointIdFactory.id(1))
65+
.setVectors(VectorsFactory.vectors(List.of(0.05f, 0.61f, 0.76f, 0.74f)))
66+
.putAllPayload(Map.of("city", ValueFactory.value("Berlin")))
67+
.build(),
68+
Points.PointStruct.newBuilder()
69+
.setId(PointIdFactory.id(2))
70+
.setVectors(VectorsFactory.vectors(List.of(0.19f, 0.81f, 0.75f, 0.11f)))
71+
.putAllPayload(Map.of("city", ValueFactory.value("London")))
72+
.build(),
73+
Points.PointStruct.newBuilder()
74+
.setId(PointIdFactory.id(3))
75+
.setVectors(VectorsFactory.vectors(List.of(0.36f, 0.55f, 0.47f, 0.94f)))
76+
.putAllPayload(Map.of("city", ValueFactory.value("Tokyo")))
77+
.build(),
78+
Points.PointStruct.newBuilder()
79+
.setId(PointIdFactory.id(4))
80+
.setVectors(VectorsFactory.vectors(List.of(0.18f, 0.01f, 0.85f, 0.80f)))
81+
.putAllPayload(Map.of("city", ValueFactory.value("New York")))
82+
.build(),
83+
Points.PointStruct.newBuilder()
84+
.setId(PointIdFactory.id(5))
85+
.setVectors(VectorsFactory.vectors(List.of(0.24f, 0.18f, 0.22f, 0.44f)))
86+
.putAllPayload(Map.of("city", ValueFactory.value("Rome")))
87+
.build(),
88+
Points.PointStruct.newBuilder()
89+
.setId(PointIdFactory.id(6))
90+
.setVectors(VectorsFactory.vectors(List.of(0.35f, 0.08f, 0.11f, 0.44f)))
91+
.putAllPayload(Map.of("city", ValueFactory.value("Madrid")))
92+
.build()))
93+
.to("qdrant:" + COLLECTION)
94+
.log("Points upserted successfully");
95+
96+
// Similarity search: find 3 nearest points to [0.2, 0.1, 0.9, 0.7]
97+
from("direct:similaritySearch")
98+
.routeId("similarity-search")
99+
.log("Performing similarity search")
100+
.setHeader(QdrantHeaders.ACTION).constant(QdrantAction.SIMILARITY_SEARCH)
101+
.setBody().constant(List.of(0.2f, 0.1f, 0.9f, 0.7f))
102+
.to("qdrant:" + COLLECTION + "?maxResults=3")
103+
.log("Similarity search results: ${body}");
104+
105+
// Filtered search: same query but filtered to city="London"
106+
from("direct:filteredSearch")
107+
.routeId("filtered-search")
108+
.log("Performing filtered similarity search (city=London)")
109+
.setHeader(QdrantHeaders.ACTION).constant(QdrantAction.SIMILARITY_SEARCH)
110+
.setBody().constant(List.of(0.2f, 0.1f, 0.9f, 0.7f))
111+
.to("qdrant:" + COLLECTION + "?maxResults=3&filter=#cityFilter")
112+
.log("Filtered search results: ${body}");
113+
114+
// Delete points where city="Madrid"
115+
from("direct:deletePoints")
116+
.routeId("delete-points")
117+
.log("Deleting points where city=Madrid")
118+
.setHeader(QdrantHeaders.ACTION).constant(QdrantAction.DELETE)
119+
.setBody().constant(ConditionFactory.matchKeyword("city", "Madrid"))
120+
.to("qdrant:" + COLLECTION)
121+
.log("Points deleted successfully");
122+
123+
// Delete the collection
124+
from("direct:deleteCollection")
125+
.routeId("delete-collection")
126+
.log("Deleting collection: " + COLLECTION)
127+
.setHeader(QdrantHeaders.ACTION).constant(QdrantAction.DELETE_COLLECTION)
128+
.to("qdrant:" + COLLECTION)
129+
.log("Collection deleted successfully");
130+
}
131+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
camel.main.name=QdrantExample
2+
camel.main.run-controller=true
3+
4+
camel.component.qdrant.host=localhost
5+
camel.component.qdrant.port=6334
6+
7+
# Disable autowiring to prevent the cityFilter bean from being applied globally
8+
camel.component.qdrant.autowired-enabled=false

0 commit comments

Comments
 (0)