Skip to content

Commit 76fcffd

Browse files
committed
tmp update cursor and document dot notation
1 parent 0cd195a commit 76fcffd

File tree

9 files changed

+472
-174
lines changed

9 files changed

+472
-174
lines changed

astra-db-java/src/main/java/com/datastax/astra/client/collections/Collection.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -888,10 +888,11 @@ public Optional<T> findOne(Filter filter, CollectionFindOneOptions findOneOption
888888
.appendIfNotNull(INPUT_INCLUDE_SORT_VECTOR, findOneOptions.includeSortVector())
889889
);
890890

891-
return Optional.ofNullable(
892-
runCommand(findOne, findOneOptions)
893-
.getData().getDocument()
894-
.map(getDocumentClass()));
891+
return Optional
892+
// Get document first
893+
.ofNullable(runCommand(findOne, findOneOptions).getData().getDocument())
894+
// Map only if present
895+
.map(doc -> doc.map(getDocumentClass()));
895896
}
896897

897898
/**

astra-db-java/src/main/java/com/datastax/astra/client/collections/commands/cursor/DistinctIterator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ public DistinctIterator(PageableIterable<T> findIterable, String fieldName, Clas
7474
this.fieldName = fieldName;
7575
this.fieldClass = fieldClass;
7676
initResultIterator();
77-
7877
}
7978

8079
/**

astra-db-java/src/main/java/com/datastax/astra/client/collections/definition/documents/Document.java

Lines changed: 111 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.datastax.astra.client.core.vector.DataAPIVector;
4242
import com.datastax.astra.internal.serdes.DataAPISerializer;
4343
import com.datastax.astra.internal.serdes.collections.DocumentSerializer;
44+
import com.datastax.astra.internal.utils.Utils;
4445
import com.fasterxml.jackson.annotation.JsonAnyGetter;
4546
import com.fasterxml.jackson.annotation.JsonAnySetter;
4647
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -49,15 +50,9 @@
4950
import java.io.Serializable;
5051
import java.lang.reflect.Array;
5152
import java.time.Instant;
52-
import java.util.Calendar;
53-
import java.util.Collection;
54-
import java.util.Date;
55-
import java.util.LinkedHashMap;
56-
import java.util.List;
57-
import java.util.Map;
58-
import java.util.Optional;
59-
import java.util.Set;
60-
import java.util.UUID;
53+
import java.util.*;
54+
import java.util.regex.Matcher;
55+
import java.util.regex.Pattern;
6156

6257

6358
/**
@@ -73,7 +68,6 @@ public class Document implements Serializable {
7368
/**
7469
* Data to be used in the document.
7570
*/
76-
//@JsonUnwrapped
7771
public transient Map<String, Object> documentMap;
7872

7973
/**
@@ -104,7 +98,7 @@ public Map<String, Object> getDocumentMap() {
10498
*/
10599
@JsonAnySetter
106100
public void setProperty(String key, Object value) {
107-
documentMap.put(key, value);
101+
append(key, value);
108102
}
109103

110104
/**
@@ -185,8 +179,26 @@ public static Document parse(final String json) {
185179
* @param value value
186180
* @return this
187181
*/
182+
@SuppressWarnings("unchecked")
188183
public Document append(final String key, final Object value) {
189-
documentMap.put(key, value);
184+
if (!Utils.hasLength(key)) {
185+
throw new IllegalArgumentException("Field name should not be null");
186+
}
187+
String[] tokens = key.split("\\.");
188+
Map<String, Object> currentMap = documentMap;
189+
for (int i = 0; i < tokens.length - 1; i++) {
190+
String token = tokens[i];
191+
Object nested = currentMap.get(token);
192+
if (!(nested instanceof Map)) {
193+
nested = new HashMap<>();
194+
currentMap.put(token, nested);
195+
}
196+
// Go deeper
197+
currentMap = (Map<String, Object>) nested;
198+
}
199+
200+
// Finally put the value in the last token
201+
currentMap.put(tokens[tokens.length - 1], value);
190202
return this;
191203
}
192204

@@ -218,7 +230,7 @@ public Document appendIfNotNull(final String key, final Object value) {
218230
* @throws ClassCastException if the value of the given key is not of type T
219231
*/
220232
public <T> T get(@NonNull final String key, @NonNull final Class<T> clazz) {
221-
return clazz.cast(SERIALIZER.convertValue(documentMap.get(key), clazz));
233+
return clazz.cast(SERIALIZER.convertValue(get(key), clazz));
222234
}
223235

224236
/**
@@ -260,7 +272,6 @@ public Document vectorize(String text) {
260272
return appendIfNotNull(DataAPIKeywords.VECTORIZE.getKeyword(), text);
261273
}
262274

263-
264275
/**
265276
* Access attribute with vectorize name if any.
266277
*
@@ -279,7 +290,7 @@ public Optional<String> getVectorize() {
279290
* vector list
280291
*/
281292
@JsonIgnore
282-
public Optional<DataAPIVector> getVector() {
293+
public Optional<DataAPIVector> getVector() {
283294
return Optional
284295
.ofNullable(get(DataAPIKeywords.VECTOR.getKeyword(), float[].class))
285296
.map(DataAPIVector::new);
@@ -379,17 +390,6 @@ public Boolean getBoolean(final String key) {
379390
return (Boolean) get(key);
380391
}
381392

382-
/**
383-
* Gets the value of the given key as a Date.
384-
*
385-
* @param key the key
386-
* @return the value as a Date, which may be null
387-
* @throws ClassCastException if the value is not a Date
388-
*/
389-
public Date getDate(final Object key) {
390-
return (Date) get(key);
391-
}
392-
393393
/**
394394
* Return an Array of items.
395395
*
@@ -506,7 +506,7 @@ public Character getCharacter(String k) {
506506
* @return
507507
* configuration value
508508
*/
509-
public Date getDate(String k) {
509+
public Date ge(String k) {
510510
return get(k, Date.class);
511511
}
512512

@@ -532,6 +532,17 @@ public Instant getInstant(String k) {
532532
return get(k, Instant.class);
533533
}
534534

535+
/**
536+
* Access element from the map
537+
* @param k
538+
* current configuration key
539+
* @return
540+
* configuration value
541+
*/
542+
public Date getDate(String k) {
543+
return get(k, Date.class);
544+
}
545+
535546
/**
536547
* Gets the list value of the given key, casting the list elements to the given {@code Class<T>}. This is useful to avoid having
537548
* casts in client code, though the effect is the same.
@@ -583,25 +594,65 @@ public String toJson() {
583594
}
584595

585596
/**
586-
* Evaluation if a key is present in the document
587-
* @param key
588-
* key to evaluate
589-
* @return
590-
* true if the key is present
597+
* Check if the given dot-delimited path exists as a key (final segment).
598+
* e.g. containsKey("foo.bar") returns true if "bar" is present in the Map
599+
* located at "foo".
591600
*/
592-
public boolean containsKey(final Object key) {
593-
return documentMap.containsKey(key);
594-
}
601+
public boolean containsKey(String key) {
602+
if (!Utils.hasLength(key)) {
603+
throw new IllegalArgumentException("Field name should not be null");
604+
}
605+
String[] tokens = key.split("\\.");
606+
Object current = documentMap;
595607

608+
// Navigate down to the second-to-last level
609+
for (int i = 0; i < tokens.length - 1; i++) {
610+
if (!(current instanceof Map)) {
611+
return false;
612+
}
613+
current = ((Map<?, ?>) current).get(tokens[i]);
614+
if (current == null) {
615+
return false;
616+
}
617+
}
618+
// Now check if we can see the last token as a key in the last map
619+
if (!(current instanceof Map)) {
620+
return false;
621+
}
622+
return ((Map<?, ?>) current).containsKey(tokens[tokens.length - 1]);
623+
}
596624

597625
/**
598626
* Retrieves the value associated with the specified key from the document.
599627
*
600628
* @param key the key whose associated value is to be returned
601629
* @return the value associated with the specified key, or {@code null} if the key is not found
602630
*/
603-
public Object get(final Object key) {
604-
return documentMap.get(key);
631+
public Object get(final String key) {
632+
if (!Utils.hasLength(key)) {
633+
throw new IllegalArgumentException("Field name should not be null");
634+
}
635+
String[] tokens = key.split("\\.");
636+
Object current = documentMap;
637+
for (String token : tokens) {
638+
if (!(current instanceof Map)) return null;
639+
640+
Matcher matcher = Pattern.compile("([a-zA-Z0-9_-]+)(\\[(\\d+)\\])?").matcher(token);
641+
if (!matcher.matches()) return null;
642+
643+
String fieldName = matcher.group(1);
644+
String indexStr = matcher.group(3);
645+
646+
current = ((Map<?, ?>) current).get(fieldName);
647+
if (indexStr != null) {
648+
if (!(current instanceof List)) return null;
649+
List<?> list = (List<?>) current;
650+
int idx = Integer.parseInt(indexStr);
651+
if (idx < 0 || idx >= list.size()) return null;
652+
current = list.get(idx);
653+
}
654+
}
655+
return current;
605656
}
606657

607658
/**
@@ -612,8 +663,8 @@ public Object get(final Object key) {
612663
* @param value the value to be associated with the specified key
613664
* @return the previous value associated with the key, or {@code null} if there was no mapping for the key
614665
*/
615-
public Object put(final String key, final Object value) {
616-
return documentMap.put(key, value);
666+
public Document put(final String key, final Object value) {
667+
return append(key, value);
617668
}
618669

619670
/**
@@ -622,8 +673,24 @@ public Object put(final String key, final Object value) {
622673
* @param key the key whose mapping is to be removed
623674
* @return the value that was associated with the key, or {@code null} if the key was not mapped
624675
*/
625-
public Object remove(final Object key) {
626-
return documentMap.remove(key);
676+
public Document remove(final String key) {
677+
String[] tokens = key.split("\\.");
678+
Object current = documentMap;
679+
for (int i = 0; i < tokens.length - 1; i++) {
680+
if (!(current instanceof Map)) {
681+
return null;
682+
}
683+
current = ((Map<?, ?>) current).get(tokens[i]);
684+
if (current == null) {
685+
return null;
686+
}
687+
}
688+
if (!(current instanceof Map)) {
689+
return null;
690+
}
691+
// Remove the final segment from the map
692+
((Map<?, ?>) current).remove(tokens[tokens.length - 1]);
693+
return this;
627694
}
628695

629696
/**
@@ -633,17 +700,9 @@ public Object remove(final Object key) {
633700
* @param map the map containing mappings to be copied to this document
634701
*/
635702
public void putAll(final Map<? extends String, ?> map) {
636-
documentMap.putAll(map);
637-
}
638-
639-
/**
640-
* Copies all mappings from the specified {@code Document} to this document.
641-
* Existing mappings will be replaced with mappings from the provided document.
642-
*
643-
* @param doc the document whose mappings are to be copied to this document
644-
*/
645-
public void putAll(Document doc) {
646-
documentMap.putAll(doc.getDocumentMap());
703+
if (map != null) {
704+
map.forEach(this::append);
705+
}
647706
}
648707

649708
/**
@@ -654,25 +713,6 @@ public void clear() {
654713
documentMap.clear();
655714
}
656715

657-
/**
658-
* Returns a collection view of the values contained in this document.
659-
*
660-
* @return a collection view of the values contained in this document
661-
*/
662-
public Collection<Object> values() {
663-
return documentMap.values();
664-
}
665-
666-
/**
667-
* Returns a set view of the mappings contained in this document.
668-
* Each entry in the set is a key-value pair.
669-
*
670-
* @return a set view of the mappings contained in this document
671-
*/
672-
public Set<Map.Entry<String, Object>> entrySet() {
673-
return documentMap.entrySet();
674-
}
675-
676716
/**
677717
* Compares this document to another object for equality.
678718
* Two documents are considered equal if their underlying maps are equal.
@@ -703,6 +743,4 @@ public int hashCode() {
703743
return documentMap.hashCode();
704744
}
705745

706-
707-
708746
}

astra-db-java/src/main/java/com/datastax/astra/client/tables/Table.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,14 @@ public TableCursor<T, T> findAll() {
741741
return find(null, new TableFindOptions());
742742
}
743743

744+
// -------------------------
745+
// --- Distinct ----
746+
// -------------------------
747+
748+
public TableCursor<T, T> distinct(Filter filter, TableFindOptions options) {
749+
return new TableCursor<>(this, filter, options, getRowClass());
750+
}
751+
744752
/**
745753
* Executes a paginated 'find' query on the table using the specified filter and find options.
746754
* <p>

0 commit comments

Comments
 (0)