This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new d20702f9101e CAMEL-26215: camel-pgvector - take the vector as a List 
of any numbers, such as the List of Double a JSON array gives, or a float or 
double array (#29062)
d20702f9101e is described below

commit d20702f9101e3283f2fc2cacac8f9322ef958965
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Nov 3 12:13:06 2026 +0100

    CAMEL-26215: camel-pgvector - take the vector as a List of any numbers, 
such as the List of Double a JSON array gives, or a float or double array 
(#29062)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../apache/camel/catalog/components/pgvector.json  |   2 +-
 .../camel/catalog/docs/pgvector-component.adoc     |   4 +-
 .../apache/camel/component/pgvector/pgvector.json  |   2 +-
 .../src/main/docs/pgvector-component.adoc          |   4 +-
 .../camel/component/pgvector/PgVectorEndpoint.java |  11 +-
 .../camel/component/pgvector/PgVectorProducer.java |  45 ++++--
 .../component/pgvector/PgVectorVectorBodyTest.java | 159 +++++++++++++++++++++
 7 files changed, 203 insertions(+), 24 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pgvector.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pgvector.json
index 9e1e646de8e2..02e825a759f2 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pgvector.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/pgvector.json
@@ -42,7 +42,7 @@
     "CamelPgVectorFilterParams": { "index": 6, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "java.util.List", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Parameter values for 
parameterized filter queries. Use with placeholders in the CamelPgVectorFilter 
header. Example: filter = 'text_content LIKE AND metadata::jsonb-'category' = ' 
with filterParams = List.of(%hello%, science).", [...]
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "list", "null" ], "byOption": [ { "option": 
"action", "value": "UPSERT", "carriers": [ "list" ] }, { "option": "action", 
"value": "SIMILARITY_SEARCH", "carriers": [ "list" ] }, { "option": "action", 
"value": "CREATE_TABLE", "carriers": [ "any" ] }, { "option": "action", 
"value": "CREATE_INDEX", "carriers": [ "any" ] }, { "option": "action", 
"value": "DROP_TABLE", "carriers": [ "any" ] }, { "option": "action", "value": 
"DELETE", "carriers": [ "any" ] } ], [...]
+    "producerIn": { "carriers": [ "list", "null" ], "byOption": [ { "option": 
"action", "value": "UPSERT", "carriers": [ "list" ] }, { "option": "action", 
"value": "SIMILARITY_SEARCH", "carriers": [ "list" ] }, { "option": "action", 
"value": "CREATE_TABLE", "carriers": [ "any" ] }, { "option": "action", 
"value": "CREATE_INDEX", "carriers": [ "any" ] }, { "option": "action", 
"value": "DROP_TABLE", "carriers": [ "any" ] }, { "option": "action", "value": 
"DELETE", "carriers": [ "any" ] } ], [...]
     "producerOut": { "carriers": [ "value", "text", "list" ], "byOption": [ { 
"option": "action", "value": "CREATE_TABLE", "carriers": [ "value" ], 
"javaType": "java.lang.Boolean" }, { "option": "action", "value": 
"CREATE_INDEX", "carriers": [ "value" ], "javaType": "java.lang.Boolean" }, { 
"option": "action", "value": "DROP_TABLE", "carriers": [ "value" ], "javaType": 
"java.lang.Boolean" }, { "option": "action", "value": "DELETE", "carriers": [ 
"value" ], "javaType": "java.lang.Boolean" [...]
   },
   "properties": {
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pgvector-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pgvector-component.adoc
index bf760999eac1..f3805a5546d6 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pgvector-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/pgvector-component.adoc
@@ -38,9 +38,9 @@ The `action` option of the endpoint, such as 
`pgvector:myTable?action=SIMILARITY
 - `CREATE_TABLE` - Creates the pgvector extension and a table with columns: 
id, text_content, metadata, embedding
 - `CREATE_INDEX` - Creates an HNSW index on the embedding column for faster 
approximate nearest neighbor search. The index uses the distance type 
configured on the endpoint (default: COSINE)
 - `DROP_TABLE` - Drops the table
-- `UPSERT` - Inserts or updates a vector record. The body must be a 
`List<Float>`. Set `CamelPgVectorRecordId` for the ID (auto-generated UUID if 
not set), `CamelPgVectorTextContent` for text, and `CamelPgVectorMetadata` for 
metadata
+- `UPSERT` - Inserts or updates a vector record. The body is the vector: a 
List of numbers, such as a `List<Float>` or the `List<Double>` a JSON array 
unmarshals to, or a `float[]` or `double[]`; each number is converted to a 
float, and another body fails. Set `CamelPgVectorRecordId` for the ID 
(auto-generated UUID if not set), `CamelPgVectorTextContent` for text, and 
`CamelPgVectorMetadata` for metadata
 - `DELETE` - Deletes a record by `CamelPgVectorRecordId`
-- `SIMILARITY_SEARCH` - Searches for similar vectors. The body must be a 
`List<Float>` query vector. Set `CamelPgVectorQueryTopK` for max results 
(default 3). Optionally set `CamelPgVectorFilter` with a SQL WHERE clause to 
filter results (e.g., `text_content LIKE '%hello%'`). Returns a 
`List<Map<String, Object>>` with keys: id, text_content, metadata, distance
+- `SIMILARITY_SEARCH` - Searches for similar vectors. The body is the query 
vector, of the same types as for `UPSERT`. Set `CamelPgVectorQueryTopK` for max 
results (default 3). Optionally set `CamelPgVectorFilter` with a SQL WHERE 
clause to filter results (e.g., `text_content LIKE '%hello%'`). Returns a 
`List<Map<String, Object>>` with keys: id, text_content, metadata, distance
 
 == Defaults
 
diff --git 
a/components/camel-ai/camel-pgvector/src/generated/resources/META-INF/org/apache/camel/component/pgvector/pgvector.json
 
b/components/camel-ai/camel-pgvector/src/generated/resources/META-INF/org/apache/camel/component/pgvector/pgvector.json
index 9e1e646de8e2..02e825a759f2 100644
--- 
a/components/camel-ai/camel-pgvector/src/generated/resources/META-INF/org/apache/camel/component/pgvector/pgvector.json
+++ 
b/components/camel-ai/camel-pgvector/src/generated/resources/META-INF/org/apache/camel/component/pgvector/pgvector.json
@@ -42,7 +42,7 @@
     "CamelPgVectorFilterParams": { "index": 6, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "java.util.List", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Parameter values for 
parameterized filter queries. Use with placeholders in the CamelPgVectorFilter 
header. Example: filter = 'text_content LIKE AND metadata::jsonb-'category' = ' 
with filterParams = List.of(%hello%, science).", [...]
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "list", "null" ], "byOption": [ { "option": 
"action", "value": "UPSERT", "carriers": [ "list" ] }, { "option": "action", 
"value": "SIMILARITY_SEARCH", "carriers": [ "list" ] }, { "option": "action", 
"value": "CREATE_TABLE", "carriers": [ "any" ] }, { "option": "action", 
"value": "CREATE_INDEX", "carriers": [ "any" ] }, { "option": "action", 
"value": "DROP_TABLE", "carriers": [ "any" ] }, { "option": "action", "value": 
"DELETE", "carriers": [ "any" ] } ], [...]
+    "producerIn": { "carriers": [ "list", "null" ], "byOption": [ { "option": 
"action", "value": "UPSERT", "carriers": [ "list" ] }, { "option": "action", 
"value": "SIMILARITY_SEARCH", "carriers": [ "list" ] }, { "option": "action", 
"value": "CREATE_TABLE", "carriers": [ "any" ] }, { "option": "action", 
"value": "CREATE_INDEX", "carriers": [ "any" ] }, { "option": "action", 
"value": "DROP_TABLE", "carriers": [ "any" ] }, { "option": "action", "value": 
"DELETE", "carriers": [ "any" ] } ], [...]
     "producerOut": { "carriers": [ "value", "text", "list" ], "byOption": [ { 
"option": "action", "value": "CREATE_TABLE", "carriers": [ "value" ], 
"javaType": "java.lang.Boolean" }, { "option": "action", "value": 
"CREATE_INDEX", "carriers": [ "value" ], "javaType": "java.lang.Boolean" }, { 
"option": "action", "value": "DROP_TABLE", "carriers": [ "value" ], "javaType": 
"java.lang.Boolean" }, { "option": "action", "value": "DELETE", "carriers": [ 
"value" ], "javaType": "java.lang.Boolean" [...]
   },
   "properties": {
diff --git 
a/components/camel-ai/camel-pgvector/src/main/docs/pgvector-component.adoc 
b/components/camel-ai/camel-pgvector/src/main/docs/pgvector-component.adoc
index bf760999eac1..f3805a5546d6 100644
--- a/components/camel-ai/camel-pgvector/src/main/docs/pgvector-component.adoc
+++ b/components/camel-ai/camel-pgvector/src/main/docs/pgvector-component.adoc
@@ -38,9 +38,9 @@ The `action` option of the endpoint, such as 
`pgvector:myTable?action=SIMILARITY
 - `CREATE_TABLE` - Creates the pgvector extension and a table with columns: 
id, text_content, metadata, embedding
 - `CREATE_INDEX` - Creates an HNSW index on the embedding column for faster 
approximate nearest neighbor search. The index uses the distance type 
configured on the endpoint (default: COSINE)
 - `DROP_TABLE` - Drops the table
-- `UPSERT` - Inserts or updates a vector record. The body must be a 
`List<Float>`. Set `CamelPgVectorRecordId` for the ID (auto-generated UUID if 
not set), `CamelPgVectorTextContent` for text, and `CamelPgVectorMetadata` for 
metadata
+- `UPSERT` - Inserts or updates a vector record. The body is the vector: a 
List of numbers, such as a `List<Float>` or the `List<Double>` a JSON array 
unmarshals to, or a `float[]` or `double[]`; each number is converted to a 
float, and another body fails. Set `CamelPgVectorRecordId` for the ID 
(auto-generated UUID if not set), `CamelPgVectorTextContent` for text, and 
`CamelPgVectorMetadata` for metadata
 - `DELETE` - Deletes a record by `CamelPgVectorRecordId`
-- `SIMILARITY_SEARCH` - Searches for similar vectors. The body must be a 
`List<Float>` query vector. Set `CamelPgVectorQueryTopK` for max results 
(default 3). Optionally set `CamelPgVectorFilter` with a SQL WHERE clause to 
filter results (e.g., `text_content LIKE '%hello%'`). Returns a 
`List<Map<String, Object>>` with keys: id, text_content, metadata, distance
+- `SIMILARITY_SEARCH` - Searches for similar vectors. The body is the query 
vector, of the same types as for `UPSERT`. Set `CamelPgVectorQueryTopK` for max 
results (default 3). Optionally set `CamelPgVectorFilter` with a SQL WHERE 
clause to filter results (e.g., `text_content LIKE '%hello%'`). Returns a 
`List<Map<String, Object>>` with keys: id, text_content, metadata, distance
 
 == Defaults
 
diff --git 
a/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorEndpoint.java
 
b/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorEndpoint.java
index 9f7cf907958e..aceac50c4720 100644
--- 
a/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorEndpoint.java
+++ 
b/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorEndpoint.java
@@ -56,11 +56,12 @@ import org.apache.camel.support.DefaultEndpoint;
                                           @BodyType.When(option = "action", 
value = "DROP_TABLE", carriers = Carrier.ANY),
                                           @BodyType.When(option = "action", 
value = "DELETE", carriers = Carrier.ANY) },
                                   description = "By the action in the action 
option or the CamelPgVectorAction header: the vector as a"
-                                                + " List of Float or a float 
array for UPSERT and SIMILARITY_SEARCH, with the id, text and"
-                                                + " metadata of UPSERT in 
headers. CREATE_TABLE, CREATE_INDEX, DROP_TABLE and DELETE do not"
-                                                + " read the body, DELETE 
takes the id from the CamelPgVectorRecordId header. Another body"
-                                                + " fails, a List of Double 
too: the pgvector:embeddings data type transformer builds the"
-                                                + " vector from the output of 
langchain4j-embeddings"),
+                                                + " List of numbers, such as 
the List of Double a JSON array unmarshals to, or a float or"
+                                                + " double array for UPSERT 
and SIMILARITY_SEARCH, with the id, text and metadata of UPSERT"
+                                                + " in headers. CREATE_TABLE, 
CREATE_INDEX, DROP_TABLE and DELETE do not read the body,"
+                                                + " DELETE takes the id from 
the CamelPgVectorRecordId header. Another body fails: the"
+                                                + " pgvector:embeddings data 
type transformer builds the vector from the output of"
+                                                + " langchain4j-embeddings"),
            producerOut = @BodyType(value = { Carrier.VALUE, Carrier.TEXT, 
Carrier.LIST },
                                    byOption = {
                                            @BodyType.When(option = "action", 
value = "CREATE_TABLE", carriers = Carrier.VALUE,
diff --git 
a/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorProducer.java
 
b/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorProducer.java
index 75625df6e841..62fa3af9ad46 100644
--- 
a/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorProducer.java
+++ 
b/components/camel-ai/camel-pgvector/src/main/java/org/apache/camel/component/pgvector/PgVectorProducer.java
@@ -29,6 +29,7 @@ import java.util.UUID;
 
 import com.pgvector.PGvector;
 import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.Message;
 import org.apache.camel.NoSuchHeaderException;
 import org.apache.camel.support.DefaultProducer;
@@ -36,6 +37,8 @@ import org.apache.camel.support.ExchangeHelper;
 
 public class PgVectorProducer extends DefaultProducer {
 
+    private static final String VECTOR_TYPES = "a List of numbers, such as 
Float or Double, a float[] or a double[]";
+
     public PgVectorProducer(PgVectorEndpoint endpoint) {
         super(endpoint);
     }
@@ -146,14 +149,12 @@ public class PgVectorProducer extends DefaultProducer {
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void upsert(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
         String tableName = getEndpoint().getCollection();
         String id = in.getHeader(PgVectorHeaders.RECORD_ID, () -> 
UUID.randomUUID().toString(), String.class);
 
-        List<Float> vector = in.getMandatoryBody(List.class);
-        float[] vectorArray = toFloatArray(vector);
+        float[] vectorArray = toVector(exchange);
 
         String textContent = in.getHeader(PgVectorHeaders.TEXT_CONTENT, 
String.class);
         String metadata = in.getHeader(PgVectorHeaders.METADATA, String.class);
@@ -193,7 +194,6 @@ public class PgVectorProducer extends DefaultProducer {
         }
     }
 
-    @SuppressWarnings("unchecked")
     private void similaritySearch(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
         String tableName = getEndpoint().getCollection();
@@ -202,8 +202,7 @@ public class PgVectorProducer extends DefaultProducer {
         String filter = in.getHeader(PgVectorHeaders.FILTER, String.class);
         List<?> filterParams = in.getHeader(PgVectorHeaders.FILTER_PARAMS, 
List.class);
 
-        List<Float> queryVector = in.getMandatoryBody(List.class);
-        float[] vectorArray = toFloatArray(queryVector);
+        float[] vectorArray = toVector(exchange);
 
         String distanceOp = distanceType.getOperator();
         StringBuilder sqlBuilder = new StringBuilder();
@@ -256,14 +255,34 @@ public class PgVectorProducer extends DefaultProducer {
     //
     // ***************************************
 
-    private static float[] toFloatArray(List<Float> vector) {
-        float[] result = new float[vector.size()];
-        for (int i = 0; i < vector.size(); i++) {
-            Float value = vector.get(i);
-            if (value == null) {
-                throw new IllegalArgumentException("Vector contains null value 
at index " + i);
+    /**
+     * The vector in the body of UPSERT and SIMILARITY_SEARCH: a float[], or a 
List of numbers, such as the List of
+     * Double a JSON array unmarshals to, or another body Camel converts to a 
List, such as a double[] or a Float[].
+     */
+    private static float[] toVector(Exchange exchange) throws 
InvalidPayloadException {
+        final Message in = exchange.getMessage();
+        if (in.getBody() instanceof float[] vector) {
+            return vector;
+        }
+        List<?> elements = in.getBody(List.class);
+        if (elements == null) {
+            throw new InvalidPayloadException(exchange, List.class, in, "the 
vector must be " + VECTOR_TYPES);
+        }
+        return toFloatArray(elements);
+    }
+
+    private static float[] toFloatArray(List<?> elements) {
+        float[] result = new float[elements.size()];
+        int i = 0;
+        for (Object element : elements) {
+            if (!(element instanceof Number number)) {
+                // the type only, not the value, which comes from the message
+                throw new IllegalArgumentException(
+                        "Vector element at index " + i + " is "
+                                                   + (element == null ? "null" 
: "a " + element.getClass().getName())
+                                                   + ": the vector must be " + 
VECTOR_TYPES);
             }
-            result[i] = value;
+            result[i++] = number.floatValue();
         }
         return result;
     }
diff --git 
a/components/camel-ai/camel-pgvector/src/test/java/org/apache/camel/component/pgvector/PgVectorVectorBodyTest.java
 
b/components/camel-ai/camel-pgvector/src/test/java/org/apache/camel/component/pgvector/PgVectorVectorBodyTest.java
new file mode 100644
index 000000000000..ce0fbf08de8d
--- /dev/null
+++ 
b/components/camel-ai/camel-pgvector/src/test/java/org/apache/camel/component/pgvector/PgVectorVectorBodyTest.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.pgvector;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Stream;
+
+import javax.sql.DataSource;
+
+import com.pgvector.PGvector;
+import org.apache.camel.CamelContext;
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Named;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.postgresql.PGConnection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-26215: the vector of UPSERT and SIMILARITY_SEARCH is a List of 
numbers, such as the List of Double a JSON array
+ * unmarshals to, or a float or double array. A stub DataSource records the 
vectors the statements are given, so no
+ * database is needed.
+ */
+class PgVectorVectorBodyTest extends CamelTestSupport {
+
+    private static final float[] VECTOR = { 0.1f, 0.2f, 0.3f };
+
+    private final List<float[]> vectors = new CopyOnWriteArrayList<>();
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        ResultSet noRows = stub(ResultSet.class, (proxy, method, args) -> 
switch (method.getName()) {
+            case "next" -> false;
+            case "close" -> null;
+            default -> throw new 
UnsupportedOperationException(method.getName());
+        });
+        PreparedStatement statement = stub(PreparedStatement.class, (proxy, 
method, args) -> switch (method.getName()) {
+            case "setObject" -> {
+                if (args[1] instanceof PGvector vector) {
+                    vectors.add(vector.toArray());
+                }
+                yield null;
+            }
+            case "setString", "setInt", "close" -> null;
+            case "executeUpdate" -> 1;
+            case "executeQuery" -> noRows;
+            default -> throw new 
UnsupportedOperationException(method.getName());
+        });
+        PGConnection pgConnection = stub(PGConnection.class, (proxy, method, 
args) -> null);
+        Connection connection = stub(Connection.class, (proxy, method, args) 
-> switch (method.getName()) {
+            case "unwrap" -> pgConnection;
+            case "prepareStatement" -> statement;
+            case "close" -> null;
+            default -> throw new 
UnsupportedOperationException(method.getName());
+        });
+        DataSource dataSource = stub(DataSource.class, (proxy, method, args) 
-> switch (method.getName()) {
+            case "getConnection" -> connection;
+            default -> throw new 
UnsupportedOperationException(method.getName());
+        });
+        context.getComponent(PgVector.SCHEME, 
PgVectorComponent.class).getConfiguration().setDataSource(dataSource);
+        return context;
+    }
+
+    static Stream<Named<Object>> vectorBodies() {
+        return Stream.of(
+                Named.of("List of Float", List.of(0.1f, 0.2f, 0.3f)),
+                Named.of("List of Double", List.of(0.1d, 0.2d, 0.3d)),
+                Named.of("List of BigDecimal", List.of(new BigDecimal("0.1"), 
new BigDecimal("0.2"), new BigDecimal("0.3"))),
+                Named.of("float[]", new float[] { 0.1f, 0.2f, 0.3f }),
+                Named.of("double[]", new double[] { 0.1d, 0.2d, 0.3d }),
+                Named.of("Double[]", new Double[] { 0.1d, 0.2d, 0.3d }));
+    }
+
+    @ParameterizedTest
+    @MethodSource("vectorBodies")
+    void upsertTakesTheVector(Object body) {
+        Exchange result = fluentTemplate.to("pgvector:items?action=UPSERT")
+                .withHeader(PgVectorHeaders.RECORD_ID, "item-1")
+                .withBody(body)
+                .request(Exchange.class);
+
+        assertThat(result.getException()).isNull();
+        assertThat(result.getMessage().getBody()).isEqualTo("item-1");
+        assertThat(vectors).hasSize(1);
+        assertThat(vectors.get(0)).containsExactly(VECTOR);
+    }
+
+    @ParameterizedTest
+    @MethodSource("vectorBodies")
+    void similaritySearchTakesTheVector(Object body) {
+        Exchange result = 
fluentTemplate.to("pgvector:items?action=SIMILARITY_SEARCH")
+                .withBody(body)
+                .request(Exchange.class);
+
+        assertThat(result.getException()).isNull();
+        assertThat(result.getMessage().getBody(List.class)).isEmpty();
+        // the vector is bound twice: for the distance and for the order
+        assertThat(vectors).hasSize(2);
+        assertThat(vectors).allSatisfy(vector -> 
assertThat(vector).containsExactly(VECTOR));
+    }
+
+    @Test
+    void aBodyThatIsNotAVectorFails() {
+        Exchange text = fluentTemplate.to("pgvector:items?action=UPSERT")
+                .withBody("0.1, 0.2, 0.3")
+                .request(Exchange.class);
+        
assertThat(text.getException()).isInstanceOfSatisfying(InvalidPayloadException.class,
+                e -> assertThat(e.getType()).isEqualTo(List.class))
+                .hasMessageContaining("the vector must be a List of numbers, 
such as Float or Double, a float[] or a double[]");
+
+        // the message names the type of the element, not its value
+        Exchange listOfText = 
fluentTemplate.to("pgvector:items?action=SIMILARITY_SEARCH")
+                .withBody(List.of("secret-0.1", "0.2"))
+                .request(Exchange.class);
+        
assertThat(listOfText.getException()).isInstanceOf(IllegalArgumentException.class)
+                .hasMessageStartingWith("Vector element at index 0 is a 
java.lang.String")
+                .hasMessageNotContaining("secret");
+
+        Exchange nullElement = 
fluentTemplate.to("pgvector:items?action=UPSERT")
+                .withBody(Arrays.asList(0.1d, null, 0.3d))
+                .request(Exchange.class);
+        
assertThat(nullElement.getException()).isInstanceOf(IllegalArgumentException.class)
+                .hasMessageStartingWith("Vector element at index 1 is null");
+
+        assertThat(vectors).isEmpty();
+    }
+
+    private static <T> T stub(Class<T> type, InvocationHandler handler) {
+        return type
+                
.cast(Proxy.newProxyInstance(PgVectorVectorBodyTest.class.getClassLoader(), new 
Class<?>[] { type }, handler));
+    }
+}

Reply via email to