41
41
import com .datastax .astra .client .core .vector .DataAPIVector ;
42
42
import com .datastax .astra .internal .serdes .DataAPISerializer ;
43
43
import com .datastax .astra .internal .serdes .collections .DocumentSerializer ;
44
+ import com .datastax .astra .internal .utils .Utils ;
44
45
import com .fasterxml .jackson .annotation .JsonAnyGetter ;
45
46
import com .fasterxml .jackson .annotation .JsonAnySetter ;
46
47
import com .fasterxml .jackson .annotation .JsonIgnore ;
49
50
import java .io .Serializable ;
50
51
import java .lang .reflect .Array ;
51
52
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 ;
61
56
62
57
63
58
/**
@@ -73,7 +68,6 @@ public class Document implements Serializable {
73
68
/**
74
69
* Data to be used in the document.
75
70
*/
76
- //@JsonUnwrapped
77
71
public transient Map <String , Object > documentMap ;
78
72
79
73
/**
@@ -104,7 +98,7 @@ public Map<String, Object> getDocumentMap() {
104
98
*/
105
99
@ JsonAnySetter
106
100
public void setProperty (String key , Object value ) {
107
- documentMap . put (key , value );
101
+ append (key , value );
108
102
}
109
103
110
104
/**
@@ -185,8 +179,26 @@ public static Document parse(final String json) {
185
179
* @param value value
186
180
* @return this
187
181
*/
182
+ @ SuppressWarnings ("unchecked" )
188
183
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 );
190
202
return this ;
191
203
}
192
204
@@ -218,7 +230,7 @@ public Document appendIfNotNull(final String key, final Object value) {
218
230
* @throws ClassCastException if the value of the given key is not of type T
219
231
*/
220
232
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 ));
222
234
}
223
235
224
236
/**
@@ -260,7 +272,6 @@ public Document vectorize(String text) {
260
272
return appendIfNotNull (DataAPIKeywords .VECTORIZE .getKeyword (), text );
261
273
}
262
274
263
-
264
275
/**
265
276
* Access attribute with vectorize name if any.
266
277
*
@@ -279,7 +290,7 @@ public Optional<String> getVectorize() {
279
290
* vector list
280
291
*/
281
292
@ JsonIgnore
282
- public Optional <DataAPIVector > getVector () {
293
+ public Optional <DataAPIVector > getVector () {
283
294
return Optional
284
295
.ofNullable (get (DataAPIKeywords .VECTOR .getKeyword (), float [].class ))
285
296
.map (DataAPIVector ::new );
@@ -379,17 +390,6 @@ public Boolean getBoolean(final String key) {
379
390
return (Boolean ) get (key );
380
391
}
381
392
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
-
393
393
/**
394
394
* Return an Array of items.
395
395
*
@@ -506,7 +506,7 @@ public Character getCharacter(String k) {
506
506
* @return
507
507
* configuration value
508
508
*/
509
- public Date getDate (String k ) {
509
+ public Date ge (String k ) {
510
510
return get (k , Date .class );
511
511
}
512
512
@@ -532,6 +532,17 @@ public Instant getInstant(String k) {
532
532
return get (k , Instant .class );
533
533
}
534
534
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
+
535
546
/**
536
547
* Gets the list value of the given key, casting the list elements to the given {@code Class<T>}. This is useful to avoid having
537
548
* casts in client code, though the effect is the same.
@@ -583,25 +594,65 @@ public String toJson() {
583
594
}
584
595
585
596
/**
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".
591
600
*/
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 ;
595
607
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
+ }
596
624
597
625
/**
598
626
* Retrieves the value associated with the specified key from the document.
599
627
*
600
628
* @param key the key whose associated value is to be returned
601
629
* @return the value associated with the specified key, or {@code null} if the key is not found
602
630
*/
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 ;
605
656
}
606
657
607
658
/**
@@ -612,8 +663,8 @@ public Object get(final Object key) {
612
663
* @param value the value to be associated with the specified key
613
664
* @return the previous value associated with the key, or {@code null} if there was no mapping for the key
614
665
*/
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 );
617
668
}
618
669
619
670
/**
@@ -622,8 +673,24 @@ public Object put(final String key, final Object value) {
622
673
* @param key the key whose mapping is to be removed
623
674
* @return the value that was associated with the key, or {@code null} if the key was not mapped
624
675
*/
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 ;
627
694
}
628
695
629
696
/**
@@ -633,17 +700,9 @@ public Object remove(final Object key) {
633
700
* @param map the map containing mappings to be copied to this document
634
701
*/
635
702
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
+ }
647
706
}
648
707
649
708
/**
@@ -654,25 +713,6 @@ public void clear() {
654
713
documentMap .clear ();
655
714
}
656
715
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
-
676
716
/**
677
717
* Compares this document to another object for equality.
678
718
* Two documents are considered equal if their underlying maps are equal.
@@ -703,6 +743,4 @@ public int hashCode() {
703
743
return documentMap .hashCode ();
704
744
}
705
745
706
-
707
-
708
746
}
0 commit comments