diff --git a/.run/qdrant_upsert_points_glove_25.run.xml b/.run/qdrant_upsert_points_glove_25.run.xml
new file mode 100644
index 000000000..89e43bde9
--- /dev/null
+++ b/.run/qdrant_upsert_points_glove_25.run.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/QdrantOpMapper.java b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/QdrantOpMapper.java
index 39c2dbe49..ca13372ce 100644
--- a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/QdrantOpMapper.java
+++ b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/QdrantOpMapper.java
@@ -61,6 +61,7 @@ public class QdrantOpMapper implements OpMapper> {
case create_payload_index ->
new QdrantCreatePayloadIndexOpDispenser(adapter, op, typeAndTarget.targetFunction);
case search_points -> new QdrantSearchPointsOpDispenser(adapter, op, typeAndTarget.targetFunction);
+ case upsert_points -> new QdrantUpsertPointsOpDispenser(adapter, op, typeAndTarget.targetFunction);
// default -> throw new RuntimeException("Unrecognized op type '" + typeAndTarget.enumId.name() + "' while " +
// "mapping parsed op " + op);
};
diff --git a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantCreatePayloadIndexOpDispenser.java b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantCreatePayloadIndexOpDispenser.java
index d77eabe9c..75406f86c 100644
--- a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantCreatePayloadIndexOpDispenser.java
+++ b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantCreatePayloadIndexOpDispenser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024 nosqlbench
+ * Copyright (c) 2020-2024 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantSearchPointsOpDispenser.java b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantSearchPointsOpDispenser.java
index 4910d8f31..05762e4a6 100644
--- a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantSearchPointsOpDispenser.java
+++ b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantSearchPointsOpDispenser.java
@@ -42,7 +42,10 @@ public class QdrantSearchPointsOpDispenser extends QdrantBaseOpDispenser getParamFunc(LongFunction clientF, ParsedOp op, LongFunction targetF) {
+ public LongFunction getParamFunc(
+ LongFunction clientF,
+ ParsedOp op,
+ LongFunction targetF) {
LongFunction ebF =
l -> SearchPoints.newBuilder().setCollectionName(targetF.apply(l));
@@ -55,7 +58,7 @@ public class QdrantSearchPointsOpDispenser extends QdrantBaseOpDispenser b.setWithVectors(WithVectorsSelector.newBuilder().setEnable(wp).build()));
ebF = op.enhanceFuncOptionally(ebF, "read_consistency", Number.class,
(SearchPoints.Builder b, Number rc) -> b.setReadConsistency(
- ReadConsistency.newBuilder().setType(ReadConsistencyType.valueOf(rc.intValue())).build()));
+ ReadConsistency.newBuilder().setType(ReadConsistencyType.forNumber(rc.intValue())).build()));
// ebF = op.enhanceFunc(ebF, List.of("vector_vector", "vectors"), List.class,
// (SearchPoints.Builder b, List vec) -> b.addAllVector(vec));
diff --git a/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantUpsertPointsOpDispenser.java b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantUpsertPointsOpDispenser.java
new file mode 100644
index 000000000..7b67d4f49
--- /dev/null
+++ b/nb-adapters/adapter-qdrant/src/main/java/io/nosqlbench/adapter/qdrant/opdispensers/QdrantUpsertPointsOpDispenser.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020-2024 nosqlbench
+ *
+ * Licensed 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 io.nosqlbench.adapter.qdrant.opdispensers;
+
+import io.nosqlbench.adapter.qdrant.QdrantDriverAdapter;
+import io.nosqlbench.adapter.qdrant.ops.QdrantBaseOp;
+import io.nosqlbench.adapter.qdrant.ops.QdrantUpsertPointsOp;
+import io.nosqlbench.adapters.api.activityimpl.OpDispenser;
+import io.nosqlbench.adapters.api.templating.ParsedOp;
+import io.qdrant.client.QdrantClient;
+import io.qdrant.client.ValueFactory;
+import io.qdrant.client.VectorFactory;
+import io.qdrant.client.VectorsFactory;
+import io.qdrant.client.grpc.Collections;
+import io.qdrant.client.grpc.JsonWithInt.ListValue;
+import io.qdrant.client.grpc.JsonWithInt.NullValue;
+import io.qdrant.client.grpc.JsonWithInt.Struct;
+import io.qdrant.client.grpc.JsonWithInt.Value;
+import io.qdrant.client.grpc.Points.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.LongFunction;
+
+public class QdrantUpsertPointsOpDispenser extends QdrantBaseOpDispenser {
+ private static final Logger logger = LogManager.getLogger(QdrantUpsertPointsOpDispenser.class);
+
+ /**
+ * Create a new {@link QdrantUpsertPointsOpDispenser} implementing the {@link OpDispenser} interface.
+ * @param adapter
+ * @param op
+ * @param targetFunction
+ * @see Upsert Points
+ */
+ public QdrantUpsertPointsOpDispenser(QdrantDriverAdapter adapter, ParsedOp op, LongFunction targetFunction) {
+ super(adapter, op, targetFunction);
+ }
+
+ @Override
+ public LongFunction getParamFunc(
+ LongFunction clientF,
+ ParsedOp op,
+ LongFunction targetF
+ ) {
+ LongFunction ebF =
+ l -> UpsertPoints.newBuilder().setCollectionName(targetF.apply(l));
+
+ // set wait and ordering query params
+ ebF = op.enhanceFuncOptionally(ebF, "wait", Boolean.class, UpsertPoints.Builder::setWait);
+ WriteOrdering.Builder writeOrdering = WriteOrdering.newBuilder();
+ op.getOptionalStaticValue("ordering", Number.class)
+ .ifPresent((Number ordering) -> {
+ writeOrdering.setType(WriteOrderingType.forNumber(ordering.intValue()));
+ });
+ final LongFunction orderingF = ebF;
+ ebF = l -> orderingF.apply(l).setOrdering(writeOrdering);
+
+ // request body begins here
+ ShardKeySelector.Builder shardKeySelector = ShardKeySelector.newBuilder();
+ op.getOptionalStaticValue("shard_key", Number.class)
+ .ifPresent((Number value) -> {
+ shardKeySelector.setShardKeys(0, Collections.ShardKey.newBuilder().setNumber(value.longValue()));
+ });
+
+ List allPoints = buildPointsStructWithNamedVectors(op);
+ final LongFunction pointsOfNamedVectorsF = ebF;
+ ebF = l -> pointsOfNamedVectorsF.apply(l).addAllPoints(allPoints);
+
+ final LongFunction lastF = ebF;
+ return l -> lastF.apply(l).build();
+ }
+
+ private List buildPointsStructWithNamedVectors(ParsedOp op) {
+ List allPoints = new ArrayList<>();
+ PointStruct.Builder pointBuilder = PointStruct.newBuilder();
+
+ PointId.Builder pointId = PointId.newBuilder();
+ // id is mandatory
+ Object idObject = op.getAsRequiredFunction("id", Object.class).apply(0L);
+ if (idObject instanceof Number) {
+ pointId.setNum(((Number) idObject).longValue());
+ } else if (idObject instanceof String) {
+ pointId.setUuid((String) idObject);
+ }
+ pointBuilder.setId(pointId);
+
+ if (op.isDefined("payload")) {
+ LongFunction