Skip to content

Commit 725ef52

Browse files
authored
Merge pull request #470 from weaviate/v6-fix-toMap
v6: Fix copying / collecting maps with null values
2 parents ddb3ef2 + f4e4527 commit 725ef52

4 files changed

Lines changed: 64 additions & 7 deletions

File tree

src/main/java/io/weaviate/client6/v1/api/collections/data/InsertManyRequest.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import java.util.Arrays;
66
import java.util.List;
77
import java.util.UUID;
8-
import java.util.stream.Collectors;
98

109
import io.weaviate.client6.v1.api.collections.CollectionHandleDefaults;
1110
import io.weaviate.client6.v1.api.collections.ObjectMetadata;
1211
import io.weaviate.client6.v1.api.collections.WeaviateObject;
12+
import io.weaviate.client6.v1.internal.MapUtil;
1313
import io.weaviate.client6.v1.internal.grpc.ByteStringUtil;
1414
import io.weaviate.client6.v1.internal.grpc.Rpc;
1515
import io.weaviate.client6.v1.internal.grpc.protocol.WeaviateGrpc.WeaviateBlockingStub;
@@ -65,8 +65,10 @@ public static <T> Rpc<InsertManyRequest<T>, WeaviateProtoBatch.BatchObjectsReque
6565
var errors = new ArrayList<String>(insertErrors.size());
6666
var uuids = new ArrayList<String>();
6767

68-
var failed = insertErrors.stream()
69-
.collect(Collectors.toMap(err -> err.getIndex(), err -> err.getError()));
68+
var failed = MapUtil.collect(
69+
insertErrors.stream(),
70+
err -> err.getIndex(),
71+
err -> err.getError());
7072

7173
var iter = insertObjects.listIterator();
7274
while (iter.hasNext()) {
@@ -137,6 +139,10 @@ public static <T> void buildObject(WeaviateProtoBatch.BatchObject.Builder object
137139
var value = entry.getValue();
138140
var protoValue = com.google.protobuf.Value.newBuilder();
139141

142+
if (value == null) {
143+
return;
144+
}
145+
140146
if (value instanceof String v) {
141147
protoValue.setStringValue(v);
142148
} else if (value instanceof UUID v) {
@@ -147,7 +153,7 @@ public static <T> void buildObject(WeaviateProtoBatch.BatchObject.Builder object
147153
protoValue.setBoolValue(v.booleanValue());
148154
} else if (value instanceof Number v) {
149155
protoValue.setNumberValue(v.doubleValue());
150-
} else if (value instanceof List v) {
156+
} else if (value instanceof List<?> v) {
151157
protoValue.setListValue(
152158
com.google.protobuf.ListValue.newBuilder()
153159
.addAllValues(v.stream()

src/main/java/io/weaviate/client6/v1/api/collections/query/QueryRequest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,13 @@ static <T> Rpc<QueryRequest, WeaviateProtoSearchGet.SearchRequest, QueryResponse
8888
group.getMaxDistance(),
8989
group.getNumberOfObjects(),
9090
objects);
91-
}).collect(Collectors.toMap(QueryResponseGroup::name, Function.identity()));
91+
})
92+
// Collectors.toMap() throws an NPE if either key or value in the map are null.
93+
// In this specific case it is safe to use it, as the function in the map above
94+
// always returns a QueryResponseGroup.
95+
// The name of the group should not be null either, that's something we assume
96+
// about the server's response.
97+
.collect(Collectors.toMap(QueryResponseGroup::name, Function.identity()));
9298

9399
return new QueryResponseGrouped<T>(allObjects, groups);
94100
}, () -> rpc.method(), () -> rpc.methodAsync());
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.weaviate.client6.v1.internal;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.function.Function;
6+
import java.util.stream.Collectors;
7+
import java.util.stream.Stream;
8+
9+
public final class MapUtil {
10+
/** Prevent public initialization. */
11+
private MapUtil() {
12+
}
13+
14+
/**
15+
* Collect stream entries into a map. Use this method whenever
16+
* potential null keys or null values prohibit {@link Collectors#toMap}.
17+
*
18+
* <p>
19+
* Example:
20+
*
21+
* <pre>{@code
22+
* Map<Integer, Integer> = MapUtil.collect(
23+
* Stream.of(1, 2, 3),
24+
* Function.identity(), // use value as key
25+
* el -> el.equals(3) ? null : el;
26+
* );
27+
*
28+
* // Result: {1: 1, 2: 2, 3: null};
29+
* }</pre>
30+
*
31+
* @param stream Stream of elements {@link T}.
32+
* @param keyFn Transforms element {@link T} to key {@link K}.
33+
* @param keyFn Transforms element {@link T} to value {@link V}.
34+
* @return Map
35+
*/
36+
public static <K, V, T> Map<K, V> collect(Stream<T> stream, Function<T, K> keyFn, Function<T, V> valueFn) {
37+
return stream.collect(
38+
HashMap::new,
39+
(m, el) -> m.put(keyFn.apply(el), valueFn.apply(el)),
40+
HashMap::putAll);
41+
}
42+
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package io.weaviate.client6.v1.internal.orm;
22

3+
import java.util.Collections;
4+
import java.util.HashMap;
35
import java.util.Map;
46

57
public class MapReader implements PropertiesReader<Map<String, Object>> {
68
private final Map<String, Object> properties;
79

810
public MapReader(Map<String, Object> properties) {
9-
this.properties = properties;
11+
// Defensive copy to ensure original properties are not modified
12+
this.properties = Collections.unmodifiableMap(new HashMap<>(properties));
1013
}
1114

1215
@Override
1316
public Map<String, Object> readProperties() {
14-
return Map.copyOf(properties); // ensure original properties immutable
17+
return properties;
1518
}
1619
}

0 commit comments

Comments
 (0)