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 db2e22647dc3 CAMEL-26214: camel-weaviate - 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 (#29061)
db2e22647dc3 is described below

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

    CAMEL-26214: camel-weaviate - 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 
(#29061)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../apache/camel/catalog/components/weaviate.json  |   2 +-
 .../camel/catalog/docs/weaviate-component.adoc     |   2 +-
 .../apache/camel/component/weaviate/weaviate.json  |   2 +-
 .../src/main/docs/weaviate-component.adoc          |   2 +-
 .../weaviate/WeaviateVectorDbEndpoint.java         |  14 +-
 .../weaviate/WeaviateVectorDbProducer.java         |  56 ++++--
 .../component/weaviate/WeaviateVectorBodyTest.java | 212 +++++++++++++++++++++
 7 files changed, 259 insertions(+), 31 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/weaviate.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/weaviate.json
index 38bed054a8ac..be4b2cac11df 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/weaviate.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/weaviate.json
@@ -49,7 +49,7 @@
     "CamelWeaviateQueryVector": { "index": 11, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "List<Float>", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Optional query vector for 
hybrid search (overrides server-side vectorizer)", "constantName": 
"org.apache.camel.component.weaviate.WeaviateVectorDbHeaders#QUERY_VECTOR" }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "list", "text", "null" ], "byOption": [ { 
"option": "action", "value": "CREATE", "carriers": [ "list" ] }, { "option": 
"action", "value": "BATCH_CREATE", "carriers": [ "list" ], "javaType": 
"java.util.List<io.weaviate.client6.v1.api.collections.WeaviateObject<java.util.Map<java.lang.String,
 java.lang.Object>>>" }, { "option": "action", "value": "UPDATE_BY_ID", 
"carriers": [ "list" ] }, { "option": "action", "value": "QUERY", "carriers": [ 
"list" ] }, { " [...]
+    "producerIn": { "carriers": [ "list", "text", "null" ], "byOption": [ { 
"option": "action", "value": "CREATE", "carriers": [ "list" ] }, { "option": 
"action", "value": "BATCH_CREATE", "carriers": [ "list" ], "javaType": 
"java.util.List<io.weaviate.client6.v1.api.collections.WeaviateObject<java.util.Map<java.lang.String,
 java.lang.Object>>>" }, { "option": "action", "value": "UPDATE_BY_ID", 
"carriers": [ "list" ] }, { "option": "action", "value": "QUERY", "carriers": [ 
"list" ] }, { " [...]
     "producerOut": { "carriers": [ "pojo", "value" ], "byOption": [ { 
"option": "action", "value": "CREATE", "carriers": [ "pojo" ], "javaType": 
"io.weaviate.client6.v1.api.collections.WeaviateObject<java.util.Map<java.lang.String,
 java.lang.Object>>" }, { "option": "action", "value": "BATCH_CREATE", 
"carriers": [ "pojo" ], "javaType": 
"io.weaviate.client6.v1.api.collections.data.InsertManyResponse" }, { "option": 
"action", "value": "QUERY", "carriers": [ "pojo" ], "javaType": "io.weavia [...]
   },
   "properties": {
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/weaviate-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/weaviate-component.adoc
index 124f6b47315a..5971c240fe21 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/weaviate-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/weaviate-component.adoc
@@ -94,4 +94,4 @@ The `action` option of the endpoint, such as 
`weaviate:myCollection?action=BM25_
             message: "${body}"
 ----
 
-
+The vector of CREATE, UPDATE_BY_ID and QUERY is the message body, and the 
optional vector of HYBRID_QUERY is the `CamelWeaviateQueryVector` header. 
Either is a List of numbers, such as the List of Double a JSON array unmarshals 
to, or a `float[]` or a `double[]`; each number is converted to a float. 
Another body fails, and so does a List with an element that is not a number.
diff --git 
a/components/camel-ai/camel-weaviate/src/generated/resources/META-INF/org/apache/camel/component/weaviate/weaviate.json
 
b/components/camel-ai/camel-weaviate/src/generated/resources/META-INF/org/apache/camel/component/weaviate/weaviate.json
index 38bed054a8ac..be4b2cac11df 100644
--- 
a/components/camel-ai/camel-weaviate/src/generated/resources/META-INF/org/apache/camel/component/weaviate/weaviate.json
+++ 
b/components/camel-ai/camel-weaviate/src/generated/resources/META-INF/org/apache/camel/component/weaviate/weaviate.json
@@ -49,7 +49,7 @@
     "CamelWeaviateQueryVector": { "index": 11, "kind": "header", 
"displayName": "", "group": "producer", "label": "", "required": false, 
"javaType": "List<Float>", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Optional query vector for 
hybrid search (overrides server-side vectorizer)", "constantName": 
"org.apache.camel.component.weaviate.WeaviateVectorDbHeaders#QUERY_VECTOR" }
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "list", "text", "null" ], "byOption": [ { 
"option": "action", "value": "CREATE", "carriers": [ "list" ] }, { "option": 
"action", "value": "BATCH_CREATE", "carriers": [ "list" ], "javaType": 
"java.util.List<io.weaviate.client6.v1.api.collections.WeaviateObject<java.util.Map<java.lang.String,
 java.lang.Object>>>" }, { "option": "action", "value": "UPDATE_BY_ID", 
"carriers": [ "list" ] }, { "option": "action", "value": "QUERY", "carriers": [ 
"list" ] }, { " [...]
+    "producerIn": { "carriers": [ "list", "text", "null" ], "byOption": [ { 
"option": "action", "value": "CREATE", "carriers": [ "list" ] }, { "option": 
"action", "value": "BATCH_CREATE", "carriers": [ "list" ], "javaType": 
"java.util.List<io.weaviate.client6.v1.api.collections.WeaviateObject<java.util.Map<java.lang.String,
 java.lang.Object>>>" }, { "option": "action", "value": "UPDATE_BY_ID", 
"carriers": [ "list" ] }, { "option": "action", "value": "QUERY", "carriers": [ 
"list" ] }, { " [...]
     "producerOut": { "carriers": [ "pojo", "value" ], "byOption": [ { 
"option": "action", "value": "CREATE", "carriers": [ "pojo" ], "javaType": 
"io.weaviate.client6.v1.api.collections.WeaviateObject<java.util.Map<java.lang.String,
 java.lang.Object>>" }, { "option": "action", "value": "BATCH_CREATE", 
"carriers": [ "pojo" ], "javaType": 
"io.weaviate.client6.v1.api.collections.data.InsertManyResponse" }, { "option": 
"action", "value": "QUERY", "carriers": [ "pojo" ], "javaType": "io.weavia [...]
   },
   "properties": {
diff --git 
a/components/camel-ai/camel-weaviate/src/main/docs/weaviate-component.adoc 
b/components/camel-ai/camel-weaviate/src/main/docs/weaviate-component.adoc
index 124f6b47315a..5971c240fe21 100644
--- a/components/camel-ai/camel-weaviate/src/main/docs/weaviate-component.adoc
+++ b/components/camel-ai/camel-weaviate/src/main/docs/weaviate-component.adoc
@@ -94,4 +94,4 @@ The `action` option of the endpoint, such as 
`weaviate:myCollection?action=BM25_
             message: "${body}"
 ----
 
-
+The vector of CREATE, UPDATE_BY_ID and QUERY is the message body, and the 
optional vector of HYBRID_QUERY is the `CamelWeaviateQueryVector` header. 
Either is a List of numbers, such as the List of Double a JSON array unmarshals 
to, or a `float[]` or a `double[]`; each number is converted to a float. 
Another body fails, and so does a List with an element that is not a number.
diff --git 
a/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbEndpoint.java
 
b/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbEndpoint.java
index 7ab6f42a11fe..85eecf41fb8a 100644
--- 
a/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbEndpoint.java
+++ 
b/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbEndpoint.java
@@ -66,13 +66,13 @@ import org.apache.camel.support.DefaultEndpoint;
                                           @BodyType.When(option = "action", 
value = "DELETE_BY_ID", carriers = Carrier.ANY),
                                           @BodyType.When(option = "action", 
value = "AGGREGATE", carriers = Carrier.ANY) },
                                   description = "By the action in the action 
option or the CamelWeaviateAction header: the vector as a"
-                                                + " List of Float or a float 
array for CREATE, UPDATE_BY_ID and QUERY, with the properties"
-                                                + " in the 
CamelWeaviateProperties header, a List of WeaviateObject for BATCH_CREATE, and"
-                                                + " the query text for 
HYBRID_QUERY and BM25_QUERY. CREATE_COLLECTION, DELETE_COLLECTION,"
-                                                + " QUERY_BY_ID, DELETE_BY_ID 
and AGGREGATE do not read the body, the id is in the"
-                                                + " CamelWeaviateIndexId 
header. Another body fails, a List of Double too: the"
-                                                + " weaviate: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 CREATE, 
UPDATE_BY_ID and QUERY, with the properties in the"
+                                                + " CamelWeaviateProperties 
header, a List of WeaviateObject for BATCH_CREATE, and the query"
+                                                + " text for HYBRID_QUERY and 
BM25_QUERY. CREATE_COLLECTION, DELETE_COLLECTION, QUERY_BY_ID,"
+                                                + " DELETE_BY_ID and AGGREGATE 
do not read the body, the id is in the CamelWeaviateIndexId"
+                                                + " header. Another body 
fails: the weaviate:embeddings data type transformer builds the"
+                                                + " vector from the output of 
langchain4j-embeddings"),
            producerOut = @BodyType(value = { Carrier.POJO, Carrier.VALUE },
                                    byOption = {
                                            @BodyType.When(option = "action", 
value = "CREATE", carriers = Carrier.POJO,
diff --git 
a/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbProducer.java
 
b/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbProducer.java
index 6ef5e3137c66..7eb1854a8d15 100644
--- 
a/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbProducer.java
+++ 
b/components/camel-ai/camel-weaviate/src/main/java/org/apache/camel/component/weaviate/WeaviateVectorDbProducer.java
@@ -30,12 +30,15 @@ import 
io.weaviate.client6.v1.api.collections.data.InsertManyResponse;
 import io.weaviate.client6.v1.api.collections.query.NearVector;
 import io.weaviate.client6.v1.api.collections.query.QueryResponse;
 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;
 import org.apache.camel.support.ExchangeHelper;
 
 public class WeaviateVectorDbProducer extends DefaultProducer {
+    private static final String VECTOR_TYPES = "a List of numbers, such as 
Float or Double, a float[] or a double[]";
+
     private WeaviateClient client;
 
     public WeaviateVectorDbProducer(WeaviateVectorDbEndpoint endpoint) {
@@ -128,15 +131,12 @@ public class WeaviateVectorDbProducer extends 
DefaultProducer {
 
     private void create(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
-        List elements = in.getMandatoryBody(List.class);
+        float[] primitiveVectors = toVector(exchange);
 
         String collectionName = resolveCollectionName(in);
 
         HashMap<String, Object> props = 
in.getHeader(WeaviateVectorDbHeaders.PROPERTIES, HashMap.class);
 
-        Float[] vectors = (Float[]) elements.toArray(new Float[0]);
-        float[] primitiveVectors = toPrimitiveFloatArray(vectors);
-
         CollectionHandle<Map<String, Object>> collection = 
client.collections.use(collectionName);
 
         WeaviateObject<Map<String, Object>> result;
@@ -151,13 +151,11 @@ public class WeaviateVectorDbProducer extends 
DefaultProducer {
 
     private void updateById(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
-        List elements = in.getMandatoryBody(List.class);
+        float[] primitiveVectors = toVector(exchange);
         String indexId = ExchangeHelper.getMandatoryHeader(exchange, 
WeaviateVectorDbHeaders.INDEX_ID, String.class);
 
         String collectionName = resolveCollectionName(in);
 
-        Float[] vectors = (Float[]) elements.toArray(new Float[0]);
-        float[] primitiveVectors = toPrimitiveFloatArray(vectors);
         HashMap<String, Object> props = 
in.getHeader(WeaviateVectorDbHeaders.PROPERTIES, HashMap.class);
 
         CollectionHandle<Map<String, Object>> collection = 
client.collections.use(collectionName);
@@ -196,15 +194,12 @@ public class WeaviateVectorDbProducer extends 
DefaultProducer {
 
     private void query(Exchange exchange) throws Exception {
         final Message in = exchange.getMessage();
-        List elements = in.getMandatoryBody(List.class);
+        float[] primitiveVectors = toVector(exchange);
 
         int topK = in.getHeader(WeaviateVectorDbHeaders.QUERY_TOP_K, 10, 
Integer.class);
 
         String collectionName = resolveCollectionName(in);
 
-        Float[] vectors = (Float[]) elements.toArray(new Float[0]);
-        float[] primitiveVectors = toPrimitiveFloatArray(vectors);
-
         CollectionHandle<Map<String, Object>> collection = 
client.collections.use(collectionName);
 
         final int limit = topK;
@@ -260,7 +255,8 @@ public class WeaviateVectorDbProducer extends 
DefaultProducer {
 
         int topK = in.getHeader(WeaviateVectorDbHeaders.QUERY_TOP_K, 10, 
Integer.class);
         Float alpha = in.getHeader(WeaviateVectorDbHeaders.HYBRID_ALPHA, 
Float.class);
-        List<Float> queryVector = 
in.getHeader(WeaviateVectorDbHeaders.QUERY_VECTOR, List.class);
+        List<?> queryVectorElements = 
in.getHeader(WeaviateVectorDbHeaders.QUERY_VECTOR, List.class);
+        float[] queryVector = queryVectorElements != null ? 
toPrimitiveFloatArray(queryVectorElements) : null;
 
         String collectionName = resolveCollectionName(in);
 
@@ -275,8 +271,7 @@ public class WeaviateVectorDbProducer extends 
DefaultProducer {
                         h.alpha(alpha);
                     }
                     if (queryVector != null) {
-                        float[] primitiveVector = 
toPrimitiveFloatArray(queryVector.toArray(new Float[0]));
-                        h.nearVector(NearVector.of(primitiveVector));
+                        h.nearVector(NearVector.of(queryVector));
                     }
                     if (fieldToSearch != null) {
                         h.returnProperties(fieldToSearch.keySet().toArray(new 
String[0]));
@@ -344,13 +339,34 @@ public class WeaviateVectorDbProducer extends 
DefaultProducer {
         out.setBody(r);
     }
 
-    private static float[] toPrimitiveFloatArray(Float[] boxed) {
-        float[] result = new float[boxed.length];
-        for (int i = 0; i < boxed.length; i++) {
-            if (boxed[i] == null) {
-                throw new IllegalArgumentException("Vector element at index " 
+ i + " is null");
+    /**
+     * The vector in the body of CREATE, UPDATE_BY_ID and QUERY: 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 toPrimitiveFloatArray(elements);
+    }
+
+    private static float[] toPrimitiveFloatArray(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] = boxed[i];
+            result[i++] = number.floatValue();
         }
         return result;
     }
diff --git 
a/components/camel-ai/camel-weaviate/src/test/java/org/apache/camel/component/weaviate/WeaviateVectorBodyTest.java
 
b/components/camel-ai/camel-weaviate/src/test/java/org/apache/camel/component/weaviate/WeaviateVectorBodyTest.java
new file mode 100644
index 000000000000..0fa71cea40c3
--- /dev/null
+++ 
b/components/camel-ai/camel-weaviate/src/test/java/org/apache/camel/component/weaviate/WeaviateVectorBodyTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.weaviate;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Stream;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpServer;
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.stub.StreamObserver;
+import io.weaviate.client6.v1.internal.grpc.ByteStringUtil;
+import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc;
+import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateProtoBase.Vectors;
+import 
io.weaviate.client6.v1.internal.grpc.protocol.WeaviateProtoSearchGet.SearchReply;
+import 
io.weaviate.client6.v1.internal.grpc.protocol.WeaviateProtoSearchGet.SearchRequest;
+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.AfterEach;
+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 static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-26214: the vector of CREATE, UPDATE_BY_ID and QUERY, and the 
CamelWeaviateQueryVector header of HYBRID_QUERY,
+ * is a List of numbers, such as the List of Double a JSON array unmarshals 
to, or a float or double array. A fake
+ * Weaviate, its REST API on a JDK HttpServer and its gRPC API, records the 
requests of the client, so no Weaviate is
+ * needed.
+ */
+class WeaviateVectorBodyTest extends CamelTestSupport {
+
+    private static final float[] VECTOR = { 0.1f, 0.2f, 0.3f };
+    // the vector as the client writes it in the JSON of an object
+    private static final String VECTOR_JSON = 
"\"vectors\":{\"default\":[0.1,0.2,0.3]}";
+    private static final String ID = "6f0b5a6e-4b5e-4b8e-9d5e-0a8c7a2a9a11";
+
+    private final List<String> restRequests = new CopyOnWriteArrayList<>();
+    private final List<SearchRequest> searches = new CopyOnWriteArrayList<>();
+    private HttpServer rest;
+    private Server grpc;
+
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        rest = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
+        rest.createContext("/", this::handle);
+        rest.start();
+        grpc = ServerBuilder.forPort(0).addService(new 
WeaviateGrpc.WeaviateImplBase() {
+            @Override
+            public void search(SearchRequest request, 
StreamObserver<SearchReply> reply) {
+                searches.add(request);
+                reply.onNext(SearchReply.getDefaultInstance());
+                reply.onCompleted();
+            }
+        }).build().start();
+
+        CamelContext context = super.createCamelContext();
+        WeaviateVectorDbConfiguration configuration
+                = context.getComponent("weaviate", 
WeaviateVectorDbComponent.class).getConfiguration();
+        configuration.setHost("localhost:" + rest.getAddress().getPort());
+        configuration.setGrpcHost("localhost");
+        configuration.setGrpcPort(grpc.getPort());
+        return context;
+    }
+
+    @AfterEach
+    void stopFakeWeaviate() {
+        rest.stop(0);
+        grpc.shutdownNow();
+    }
+
+    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 createTakesTheVector(Object body) {
+        Exchange result = fluentTemplate.to("weaviate:Articles?action=CREATE")
+                .withBody(body)
+                .request(Exchange.class);
+
+        assertThat(result.getException()).isNull();
+        assertThat(restRequests).filteredOn(request -> 
request.startsWith("POST /v1/objects"))
+                .singleElement().asString().contains(VECTOR_JSON);
+    }
+
+    @ParameterizedTest
+    @MethodSource("vectorBodies")
+    void updateByIdTakesTheVector(Object body) {
+        Exchange result = 
fluentTemplate.to("weaviate:Articles?action=UPDATE_BY_ID")
+                .withHeader(WeaviateVectorDbHeaders.INDEX_ID, ID)
+                .withBody(body)
+                .request(Exchange.class);
+
+        assertThat(result.getException()).isNull();
+        assertThat(restRequests).filteredOn(request -> 
request.startsWith("PATCH /v1/objects/Articles/" + ID))
+                .singleElement().asString().contains(VECTOR_JSON);
+    }
+
+    @ParameterizedTest
+    @MethodSource("vectorBodies")
+    void queryTakesTheVector(Object body) {
+        Exchange result = fluentTemplate.to("weaviate:Articles?action=QUERY")
+                .withBody(body)
+                .request(Exchange.class);
+
+        assertThat(result.getException()).isNull();
+        assertThat(searches).singleElement()
+                .satisfies(search -> 
assertThat(vectorOf(search.getNearVector().getVectorsList())).containsExactly(VECTOR));
+    }
+
+    @Test
+    void hybridQueryTakesTheQueryVectorHeader() {
+        Exchange result = 
fluentTemplate.to("weaviate:Articles?action=HYBRID_QUERY")
+                .withHeader(WeaviateVectorDbHeaders.QUERY_VECTOR, 
List.of(0.1d, 0.2d, 0.3d))
+                .withBody("camel")
+                .request(Exchange.class);
+
+        assertThat(result.getException()).isNull();
+        assertThat(searches).singleElement().satisfies(search -> {
+            assertThat(search.getHybridSearch().getQuery()).isEqualTo("camel");
+            
assertThat(vectorOf(search.getHybridSearch().getNearVector().getVectorsList())).containsExactly(VECTOR);
+        });
+    }
+
+    @Test
+    void aBodyThatIsNotAVectorFails() {
+        Exchange text = fluentTemplate.to("weaviate:Articles?action=CREATE")
+                .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("weaviate:Articles?action=QUERY")
+                .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("weaviate:Articles?action=UPDATE_BY_ID")
+                .withHeader(WeaviateVectorDbHeaders.INDEX_ID, ID)
+                .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(restRequests).noneMatch(request -> 
request.contains("/objects"));
+        assertThat(searches).isEmpty();
+    }
+
+    private static float[] vectorOf(List<Vectors> vectors) {
+        assertThat(vectors).hasSize(1);
+        return 
ByteStringUtil.decodeVectorSingle(vectors.get(0).getVectorBytes());
+    }
+
+    private void handle(HttpExchange http) throws IOException {
+        String body = new String(http.getRequestBody().readAllBytes(), 
StandardCharsets.UTF_8);
+        String path = http.getRequestURI().getPath();
+        restRequests.add(http.getRequestMethod() + " " + path + " " + body);
+        String response;
+        if (path.endsWith("/.well-known/live")) {
+            response = "";
+        } else if (path.endsWith("/meta")) {
+            response = 
"{\"hostname\":\"http://[::]:8080\",\"version\":\"1.37.4\",\"modules\":{}}";;
+        } else {
+            // the object, as Weaviate gives it back
+            response = body;
+        }
+        byte[] bytes = response.getBytes(StandardCharsets.UTF_8);
+        http.getResponseHeaders().add("Content-Type", "application/json");
+        http.sendResponseHeaders(200, bytes.length == 0 ? -1 : bytes.length);
+        if (bytes.length > 0) {
+            http.getResponseBody().write(bytes);
+        }
+        http.close();
+    }
+}

Reply via email to