View Javadoc
1   package de.dlr.shepard.data.spatialdata.repositories;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.assertFalse;
5   import static org.junit.jupiter.api.Assertions.assertNotNull;
6   import static org.junit.jupiter.api.Assertions.assertTrue;
7   
8   import de.dlr.shepard.data.spatialdata.io.FilterCondition;
9   import de.dlr.shepard.data.spatialdata.io.Operator;
10  import de.dlr.shepard.data.spatialdata.model.GeometryBuilder;
11  import de.dlr.shepard.data.spatialdata.model.SpatialDataPoint;
12  import io.quarkus.logging.Log;
13  import io.quarkus.test.junit.QuarkusTest;
14  import jakarta.enterprise.context.control.ActivateRequestContext;
15  import jakarta.inject.Inject;
16  import jakarta.transaction.Transactional;
17  import java.time.Instant;
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import org.junit.jupiter.api.AfterAll;
25  import org.junit.jupiter.api.BeforeAll;
26  import org.junit.jupiter.api.Test;
27  import org.junit.jupiter.api.TestInstance;
28  import org.locationtech.jts.geom.Coordinate;
29  
30  @QuarkusTest
31  @TestInstance(TestInstance.Lifecycle.PER_CLASS)
32  @ActivateRequestContext
33  public class SpatialDataPointRepositoryTest {
34  
35    @Inject
36    SpatialDataPointRepository repository;
37  
38    Long movingTimeStamp = Instant.now().toEpochMilli() * 1_000_000;
39    final Long testContainerId = 987651L;
40    final int numberOfTestPoints = 100;
41    final ArrayList<SpatialDataPoint> testDataPoints = new ArrayList<SpatialDataPoint>();
42    private ArrayList<Long> containerIdsForCleanup = new ArrayList<Long>();
43  
44    private long generateManagedContainerId() {
45      var id = 0L;
46      do {
47        id = (long) (Math.random() * 1_000);
48      } while (containerIdsForCleanup.contains(id));
49  
50      containerIdsForCleanup.add(id);
51      return id;
52    }
53  
54    @BeforeAll
55    @Transactional
56    public void setup() {
57      containerIdsForCleanup.add(testContainerId);
58      for (var i = 0; i < numberOfTestPoints; i++) {
59        testDataPoints.add(
60          new SpatialDataPoint(
61            testContainerId,
62            generateTimestamp(),
63            GeometryBuilder.fromCoordinate(new Coordinate(i, i, i)),
64            Map.of("a_meta_data", "metadata_%s".formatted(i)),
65            Map.of("a_measurement", i)
66          )
67        );
68      }
69      repository.insert(testContainerId, testDataPoints.toArray(new SpatialDataPoint[0]));
70    }
71  
72    @AfterAll
73    @Transactional
74    public void teardown() {
75      containerIdsForCleanup.stream().forEach(id -> repository.deleteByContainerId(id));
76    }
77  
78    @Test
79    @Transactional
80    public void insert_dataWithMetadataAndMeasurements_allPropertiesStored() {
81      var containerId = generateManagedContainerId();
82      var timestamp = 1_000_000_000_000L;
83      var geometry = GeometryBuilder.fromCoordinate(new Coordinate(4, 5, 6));
84  
85      Map<String, Object> metadata = new HashMap<>();
86      metadata.put("intValue", 1);
87      metadata.put("floatValue", 1.2345);
88      metadata.put("stringValue", "hello world");
89  
90      Map<String, Object> measurements = new HashMap<>();
91      measurements.put("a", 1);
92      measurements.put("b", 1.2345);
93      measurements.put("c", "{\"key\":\"value\"}");
94  
95      var dataPoint = new SpatialDataPoint(containerId, timestamp, geometry, metadata, measurements);
96  
97      var result = repository.insert(containerId, dataPoint);
98      assertEquals(1, result);
99  
100     var current = repository.getByContainerId(containerId);
101     assertEquals(1, current.size());
102     assertEquals(1_000_000_000_000L, current.get(0).getTime());
103     assertEquals("org.locationtech.jts.geom.Point", current.get(0).getPosition().getClass().getName());
104     assertEquals(4, current.get(0).getPosition().getCoordinate().x);
105     assertEquals(5, current.get(0).getPosition().getCoordinate().y);
106     assertEquals(6, current.get(0).getPosition().getCoordinate().z);
107     assertEquals(metadata, current.get(0).getMetadata());
108     assertEquals(measurements, current.get(0).getMeasurements());
109   }
110 
111   @Test
112   @Transactional
113   public void insert_multiplePoints_success() {
114     final ArrayList<SpatialDataPoint> entryList = new ArrayList<SpatialDataPoint>();
115     var containerId = generateManagedContainerId();
116 
117     for (var i = 0; i < 100; i++) {
118       entryList.add(
119         new SpatialDataPoint(
120           containerId,
121           generateTimestamp(),
122           GeometryBuilder.fromCoordinate(new Coordinate(i, i, i)),
123           null,
124           null
125         )
126       );
127     }
128 
129     var result = repository.insert(containerId, entryList.toArray(new SpatialDataPoint[0]));
130     assertEquals(100, result);
131 
132     var current = repository.getByContainerId(containerId);
133     assertEquals(100, current.size());
134     assertFalse(Double.isNaN(current.get(0).getPosition().getCoordinate().x));
135     assertFalse(Double.isNaN(current.get(0).getPosition().getCoordinate().y));
136     assertFalse(Double.isNaN(current.get(0).getPosition().getCoordinate().z));
137   }
138 
139   @Test
140   public void getWithoutGeometryFilter_returnsAllData_success() {
141     var expected = repository.getByContainerId(testContainerId);
142     var actual = repository.get(
143       testContainerId,
144       -1L,
145       Instant.now().toEpochMilli() * 1_000_000 + 1000,
146       Collections.emptyMap(),
147       Collections.emptyList(),
148       null,
149       null
150     );
151     assertEquals(expected, actual);
152   }
153 
154   @Test
155   public void getWithoutGeometryFilter_returnsLimitData_success() {
156     var actual = repository.get(
157       testContainerId,
158       -1L,
159       Instant.now().toEpochMilli() * 1_000_000 + 1000,
160       Collections.emptyMap(),
161       Collections.emptyList(),
162       3,
163       null
164     );
165     assertEquals(3, actual.size());
166   }
167 
168   @Test
169   public void getWithoutGeometryFilter_returnsSkippedData_success() {
170     var actual = repository.get(testContainerId, null, null, Collections.emptyMap(), Collections.emptyList(), null, 10);
171     assertEquals(10, actual.size());
172   }
173 
174   @Test
175   public void getWithoutGeometryFilter_returnsFilteredData_success() {
176     var expected = List.of(testDataPoints.get(1));
177     var actual = repository.get(
178       testContainerId,
179       -1L,
180       Instant.now().toEpochMilli() * 1_000_000 + 1000,
181       Map.of("a_meta_data", "metadata_1"),
182       List.of(new FilterCondition("a_measurement", Operator.EQUALS, 1)),
183       null,
184       null
185     );
186 
187     assertEquals(expected, actual);
188   }
189 
190   /* Bounding Box */
191   @Test
192   public void getByBoundingBox_returnsAllData_success() {
193     var data = repository.getByBoundingBox(
194       testContainerId,
195       new Coordinate(0, 0, 0),
196       new Coordinate(9999, 9999, 9999),
197       null,
198       null,
199       Collections.emptyMap(),
200       Collections.emptyList(),
201       null,
202       null
203     );
204 
205     assertNotNull(data);
206     assertTrue(data.size() == numberOfTestPoints);
207   }
208 
209   @Test
210   @Transactional
211   public void getByBoundingBox_returnsSomeData_success() {
212     var containerId = generateManagedContainerId();
213     // Setup
214     var dataPoint1 = new SpatialDataPoint(
215       containerId,
216       generateTimestamp(),
217       GeometryBuilder.fromCoordinate(new Coordinate(666, 666, 666)),
218       null,
219       null
220     );
221     var dataPoint2 = new SpatialDataPoint(
222       containerId,
223       generateTimestamp(),
224       GeometryBuilder.fromCoordinate(new Coordinate(600, 666, 600)),
225       null,
226       null
227     );
228     var dataPoint3 = new SpatialDataPoint(
229       containerId,
230       generateTimestamp(),
231       GeometryBuilder.fromCoordinate(new Coordinate(666, 700, 666)),
232       null,
233       null
234     );
235     var dataPoint4 = new SpatialDataPoint(
236       containerId,
237       generateTimestamp(),
238       GeometryBuilder.fromCoordinate(new Coordinate(700, 700, 700)),
239       null,
240       null
241     );
242     var dataPoints = new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4 };
243     var result = repository.insert(containerId, dataPoints);
244 
245     var data = repository.getByBoundingBox(
246       containerId,
247       new Coordinate(500, 500, 500),
248       new Coordinate(700, 700, 700),
249       null,
250       null,
251       Collections.emptyMap(),
252       Collections.emptyList(),
253       null,
254       null
255     );
256 
257     assertNotNull(data);
258     assertEquals(4, result);
259     assertEquals(4, data.size());
260 
261     Arrays.stream(dataPoints).forEach(dataPoint -> assertTrue(data.contains(dataPoint)));
262   }
263 
264   @Test
265   @Transactional
266   public void getByBoundingBox_returnsSomeData_withLimit_success() {
267     var containerId = generateManagedContainerId();
268     // Setup
269     var dataPoint1 = new SpatialDataPoint(
270       containerId,
271       generateTimestamp(),
272       GeometryBuilder.fromCoordinate(new Coordinate(866, 866, 866)),
273       null,
274       null
275     );
276     var dataPoint2 = new SpatialDataPoint(
277       containerId,
278       generateTimestamp(),
279       GeometryBuilder.fromCoordinate(new Coordinate(800, 866, 800)),
280       null,
281       null
282     );
283     var dataPoint3 = new SpatialDataPoint(
284       containerId,
285       generateTimestamp(),
286       GeometryBuilder.fromCoordinate(new Coordinate(866, 900, 866)),
287       null,
288       null
289     );
290     var dataPoint4 = new SpatialDataPoint(
291       containerId,
292       generateTimestamp(),
293       GeometryBuilder.fromCoordinate(new Coordinate(900, 900, 900)),
294       null,
295       null
296     );
297     var result = repository.insert(
298       containerId,
299       new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4 }
300     );
301 
302     var data = repository.getByBoundingBox(
303       containerId,
304       new Coordinate(700, 700, 700),
305       new Coordinate(900, 900, 900),
306       null,
307       null,
308       Collections.emptyMap(),
309       Collections.emptyList(),
310       3,
311       null
312     );
313 
314     assertNotNull(data);
315     assertEquals(4, result);
316     assertEquals(3, data.size());
317   }
318 
319   @Test
320   @Transactional
321   public void getByBoundingBox_returnsSomeData_withSkip_success() {
322     var containerId = generateManagedContainerId();
323     // Setup
324     var dataPoint1 = new SpatialDataPoint(
325       containerId,
326       generateTimestamp(),
327       GeometryBuilder.fromCoordinate(new Coordinate(766, 766, 766)),
328       null,
329       null
330     );
331     var dataPoint2 = new SpatialDataPoint(
332       containerId,
333       generateTimestamp(),
334       GeometryBuilder.fromCoordinate(new Coordinate(700, 766, 700)),
335       null,
336       null
337     );
338     var dataPoint3 = new SpatialDataPoint(
339       containerId,
340       generateTimestamp(),
341       GeometryBuilder.fromCoordinate(new Coordinate(766, 700, 766)),
342       null,
343       null
344     );
345     var dataPoint4 = new SpatialDataPoint(
346       containerId,
347       generateTimestamp(),
348       GeometryBuilder.fromCoordinate(new Coordinate(700, 700, 700)),
349       null,
350       null
351     );
352     var result = repository.insert(
353       containerId,
354       new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4 }
355     );
356 
357     var data = repository.getByBoundingBox(
358       containerId,
359       new Coordinate(700, 700, 700),
360       new Coordinate(800, 800, 800),
361       null,
362       null,
363       Collections.emptyMap(),
364       Collections.emptyList(),
365       null,
366       2
367     );
368 
369     assertNotNull(data);
370     assertEquals(4, result);
371     assertEquals(2, data.size());
372   }
373 
374   @Test
375   public void getByBoundingBox_returnsNoData_success() {
376     var data = repository.getByBoundingBox(
377       testContainerId,
378       new Coordinate(-1, -1, -1),
379       new Coordinate(-10, -10, -10),
380       null,
381       null,
382       Collections.emptyMap(),
383       Collections.emptyList(),
384       null,
385       null
386     );
387 
388     assertNotNull(data);
389     assertEquals(0, data.size());
390   }
391 
392   /* Bounding Sphere */
393   @Test
394   public void getByBoundingSphere_returnsAllData_success() {
395     var data = repository.getByBoundingSphere(
396       testContainerId,
397       new Coordinate(0, 0, 0),
398       9999,
399       null,
400       null,
401       Collections.emptyMap(),
402       Collections.emptyList(),
403       null,
404       null
405     );
406 
407     assertNotNull(data);
408     assertTrue(data.size() == numberOfTestPoints);
409   }
410 
411   @Test
412   @Transactional
413   public void getByBoundingSphere_returnsSomeData_success() {
414     var containerId = generateManagedContainerId();
415     var dataPoint1 = new SpatialDataPoint(
416       containerId,
417       generateTimestamp(),
418       GeometryBuilder.fromCoordinate(new Coordinate(1666, 1666, 1666)),
419       null,
420       null
421     );
422     var dataPoint2 = new SpatialDataPoint(
423       containerId,
424       generateTimestamp(),
425       GeometryBuilder.fromCoordinate(new Coordinate(1600, 1666, 1600)),
426       null,
427       null
428     );
429     var dataPoint3 = new SpatialDataPoint(
430       containerId,
431       generateTimestamp(),
432       GeometryBuilder.fromCoordinate(new Coordinate(1666, 1700, 1666)),
433       null,
434       null
435     );
436     var dataPoint4 = new SpatialDataPoint(
437       containerId,
438       generateTimestamp(),
439       GeometryBuilder.fromCoordinate(new Coordinate(1700, 1700, 1700)),
440       null,
441       null
442     );
443     var dataPoint5 = new SpatialDataPoint(
444       containerId,
445       generateTimestamp(),
446       GeometryBuilder.fromCoordinate(new Coordinate(1800, 1800, 1800)),
447       null,
448       null
449     );
450     var dataPoints = new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4, dataPoint5 };
451     var result = repository.insert(containerId, dataPoints);
452 
453     var data = repository.getByBoundingSphere(
454       containerId,
455       new Coordinate(1666, 1666, 1666),
456       100,
457       null,
458       null,
459       Collections.emptyMap(),
460       Collections.emptyList(),
461       null,
462       null
463     );
464 
465     assertNotNull(data);
466     assertEquals(5, result);
467     assertEquals(4, data.size());
468     dataPoints = new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4 };
469     Arrays.stream(dataPoints).forEach(dataPoint -> assertTrue(data.contains(dataPoint)));
470   }
471 
472   @Test
473   @Transactional
474   public void getByBoundingSphere_returnsData_withLIMIT_success() {
475     var containerId = generateManagedContainerId();
476     var dataPoint1 = new SpatialDataPoint(
477       containerId,
478       generateTimestamp(),
479       GeometryBuilder.fromCoordinate(new Coordinate(1666, 1666, 1666)),
480       null,
481       null
482     );
483     var dataPoint2 = new SpatialDataPoint(
484       containerId,
485       generateTimestamp(),
486       GeometryBuilder.fromCoordinate(new Coordinate(1600, 1666, 1600)),
487       null,
488       null
489     );
490     var dataPoint3 = new SpatialDataPoint(
491       containerId,
492       generateTimestamp(),
493       GeometryBuilder.fromCoordinate(new Coordinate(1666, 1700, 1666)),
494       null,
495       null
496     );
497     var dataPoint4 = new SpatialDataPoint(
498       containerId,
499       generateTimestamp(),
500       GeometryBuilder.fromCoordinate(new Coordinate(1700, 1700, 1700)),
501       null,
502       null
503     );
504     var dataPoint5 = new SpatialDataPoint(
505       containerId,
506       generateTimestamp(),
507       GeometryBuilder.fromCoordinate(new Coordinate(1800, 1800, 1800)),
508       null,
509       null
510     );
511     var result = repository.insert(
512       containerId,
513       new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4, dataPoint5 }
514     );
515 
516     var data = repository.getByBoundingSphere(
517       containerId,
518       new Coordinate(1666, 1666, 1666),
519       100,
520       null,
521       null,
522       Collections.emptyMap(),
523       Collections.emptyList(),
524       3,
525       null
526     );
527 
528     assertNotNull(data);
529     assertEquals(5, result);
530     assertEquals(3, data.size());
531   }
532 
533   @Test
534   @Transactional
535   public void getByBoundingSphere_returnsData_withSkip_success() {
536     var containerId = generateManagedContainerId();
537     var dataPoint1 = new SpatialDataPoint(
538       containerId,
539       generateTimestamp(),
540       GeometryBuilder.fromCoordinate(new Coordinate(3980, 20, 10)),
541       null,
542       null
543     );
544     var dataPoint2 = new SpatialDataPoint(
545       containerId,
546       generateTimestamp(),
547       GeometryBuilder.fromCoordinate(new Coordinate(4020, -15, 5)),
548       null,
549       null
550     );
551     var dataPoint3 = new SpatialDataPoint(
552       containerId,
553       generateTimestamp(),
554       GeometryBuilder.fromCoordinate(new Coordinate(4010, 25, -30)),
555       null,
556       null
557     );
558     var dataPoint4 = new SpatialDataPoint(
559       containerId,
560       generateTimestamp(),
561       GeometryBuilder.fromCoordinate(new Coordinate(3995, -35, 40)),
562       null,
563       null
564     );
565     var dataPoint5 = new SpatialDataPoint(
566       containerId,
567       generateTimestamp(),
568       GeometryBuilder.fromCoordinate(new Coordinate(4000, 10, -50)),
569       null,
570       null
571     );
572     var dataPoint6 = new SpatialDataPoint(
573       containerId,
574       generateTimestamp(),
575       GeometryBuilder.fromCoordinate(new Coordinate(4005, 0, -40)),
576       null,
577       null
578     );
579     var result = repository.insert(
580       containerId,
581       new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4, dataPoint5, dataPoint6 }
582     );
583 
584     var data = repository.getByBoundingSphere(
585       containerId,
586       new Coordinate(4000, 0, 0),
587       100,
588       null,
589       null,
590       Collections.emptyMap(),
591       Collections.emptyList(),
592       null,
593       2
594     );
595 
596     assertNotNull(data);
597     assertEquals(6, result);
598     assertEquals(3, data.size());
599   }
600 
601   @Test
602   public void getByBoundingSphere_returnsNoData_success() {
603     var data = repository.getByBoundingSphere(
604       testContainerId,
605       new Coordinate(-10, -10, -10),
606       1,
607       null,
608       null,
609       Collections.emptyMap(),
610       Collections.emptyList(),
611       null,
612       null
613     );
614 
615     assertNotNull(data);
616     assertEquals(0, data.size());
617   }
618 
619   /* KNN Search */
620   @Test
621   public void getByKNN_returnsAllData_success() {
622     var data = repository.getByKNN(
623       testContainerId,
624       new Coordinate(0, 0, 0),
625       9999,
626       null,
627       null,
628       Collections.emptyMap(),
629       Collections.emptyList()
630     );
631 
632     assertNotNull(data);
633     assertTrue(data.size() == numberOfTestPoints);
634   }
635 
636   @Test
637   public void getByKNN_returnsSomeData_success() {
638     var data = repository.getByKNN(
639       testContainerId,
640       new Coordinate(4, 4, 4),
641       3,
642       null,
643       null,
644       Collections.emptyMap(),
645       Collections.emptyList()
646     );
647 
648     assertNotNull(data);
649     assertEquals(3, data.size());
650 
651     // the three nearest points from here should be (3,3,3), (5,5,5) and (4,4,4)
652     for (SpatialDataPoint entry : data) {
653       Log.info(entry.getPosition().getCoordinate());
654       final var xCoord = entry.getPosition().getCoordinate().x;
655       final var yCoord = entry.getPosition().getCoordinate().y;
656       final var zCoord = entry.getPosition().getCoordinate().z;
657       assertTrue(xCoord == 3 || xCoord == 5 || xCoord == 4);
658       assertTrue(yCoord == 3 || yCoord == 5 || yCoord == 4);
659       assertTrue(zCoord == 3 || zCoord == 5 || zCoord == 4);
660     }
661   }
662 
663   @Test
664   public void getByKNN_returnsNoData_success() {
665     var data = repository.getByKNN(
666       testContainerId,
667       new Coordinate(-1, -1, -1),
668       0,
669       null,
670       null,
671       Collections.emptyMap(),
672       Collections.emptyList()
673     );
674 
675     assertNotNull(data);
676     assertEquals(0, data.size());
677   }
678 
679   @Test
680   @Transactional
681   public void deleteData_byContainerId_success() {
682     var containerId = generateManagedContainerId();
683     repository.insert(
684       containerId,
685       new SpatialDataPoint(containerId, generateTimestamp(), GeometryBuilder.fromXYZ(1, 1, 1), null, null)
686     );
687 
688     int numberDeletedRows = repository.deleteByContainerId(containerId);
689     assertTrue(numberDeletedRows > 0);
690   }
691 
692   /***** Test of metadata field and jsonb queries */
693   @Test
694   @Transactional
695   public void insert_storeMetadataWithNestedObject_success() {
696     var containerId = generateManagedContainerId();
697 
698     var nestedObject = new HashMap<String, Object>();
699     nestedObject.put("temperature", 23.4);
700     nestedObject.put("humidity", 0.6);
701     nestedObject.put("room", "living room");
702 
703     Map<String, Object> metadata = new HashMap<>();
704     metadata.put("track", 1);
705     metadata.put("layer", 7);
706     metadata.put("floatValue", 1.2345);
707     metadata.put("stringValue", "hello world");
708     metadata.put("sensors", nestedObject);
709 
710     var dataPoint = new SpatialDataPoint(
711       containerId,
712       generateTimestamp(),
713       GeometryBuilder.fromCoordinate(new Coordinate(1, 2, 3)),
714       metadata,
715       null
716     );
717     var result = repository.insert(containerId, dataPoint);
718     assertEquals(1, result);
719   }
720 
721   @Test
722   @Transactional
723   public void getByBoundingBox_filterByMetadata_returnsOneRecord() {
724     var containerId = generateManagedContainerId();
725     var metadataFilter = new HashMap<String, Object>();
726     metadataFilter.put("layer", 7);
727     repository.insert(
728       containerId,
729       new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadataFilter, null)
730     );
731 
732     var data = repository.getByBoundingBox(
733       containerId,
734       new Coordinate(0, 0, 0),
735       new Coordinate(2, 2, 2),
736       null,
737       null,
738       metadataFilter,
739       Collections.emptyList(),
740       null,
741       null
742     );
743 
744     assertNotNull(data);
745     assertTrue(data.size() == 1);
746   }
747 
748   @Test
749   @Transactional
750   public void getByKNN_filterByMetadata_returnsOneRecord() {
751     var containerId = generateManagedContainerId();
752     var metadataFilter = new HashMap<String, Object>();
753     metadataFilter.put("layer", 7);
754     var dataPoint = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadataFilter, null);
755     repository.insert(containerId, dataPoint);
756 
757     var data = repository.getByKNN(
758       containerId,
759       new Coordinate(0, 0, 0),
760       1,
761       null,
762       null,
763       metadataFilter,
764       Collections.emptyList()
765     );
766 
767     assertNotNull(data);
768     assertTrue(data.size() == 1);
769     assertTrue(data.get(0).equals(dataPoint));
770   }
771 
772   @Test
773   @Transactional
774   public void getByKNN_filterByNestedMetadata_returnsOneRecord() {
775     var containerId = generateManagedContainerId();
776     var nestedObject = new HashMap<String, Object>();
777     nestedObject.put("temperature", 23.4);
778     nestedObject.put("humidity", 0.6);
779     nestedObject.put("room", "living room");
780 
781     Map<String, Object> metadata = new HashMap<>();
782     metadata.put("track", 1);
783     metadata.put("layer", 7);
784     metadata.put("floatValue", 1.2345);
785     metadata.put("stringValue", "hello world");
786     metadata.put("sensors", nestedObject);
787     var dataPoint = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadata, null);
788     repository.insert(containerId, dataPoint);
789 
790     var metadataFilter = new HashMap<String, Object>();
791     metadataFilter.put("layer", 7);
792     metadataFilter.put("sensors", Map.of("room", "living room", "humidity", 0.6));
793 
794     var data = repository.getByKNN(
795       containerId,
796       new Coordinate(0, 0, 0),
797       1,
798       null,
799       null,
800       metadataFilter,
801       Collections.emptyList()
802     );
803 
804     assertNotNull(data);
805     assertTrue(data.size() == 1);
806     assertTrue(data.get(0).equals(dataPoint));
807   }
808 
809   @Test
810   @Transactional
811   public void getByKNN_filterByNestedMetadata_returnsNoData() {
812     var containerId = generateManagedContainerId();
813     var nestedObject = new HashMap<String, Object>();
814     nestedObject.put("temperature", 23.4);
815     nestedObject.put("humidity", 0.6);
816     nestedObject.put("room", "living room");
817 
818     Map<String, Object> metadata = new HashMap<>();
819     metadata.put("track", 1);
820     metadata.put("layer", 7);
821     metadata.put("floatValue", 1.2345);
822     metadata.put("stringValue", "hello world");
823     metadata.put("sensors", nestedObject);
824     var dataPoint = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadata, null);
825     repository.insert(containerId, dataPoint);
826 
827     var metadataFilter = new HashMap<String, Object>();
828     metadataFilter.put("layer", 7);
829     metadataFilter.put("sensors", Map.of("room", "dining room", "humidity", 0.6));
830 
831     var data = repository.getByKNN(
832       containerId,
833       new Coordinate(0, 0, 0),
834       1,
835       null,
836       null,
837       metadataFilter,
838       Collections.emptyList()
839     );
840 
841     assertNotNull(data);
842     assertTrue(data.size() == 0);
843   }
844 
845   @Test
846   @Transactional
847   public void getByKNN_filterByTimestamp_returnsOneRecord() {
848     var containerId = generateManagedContainerId();
849     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), null, null);
850     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(1, 1, 1), null, null);
851     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
852 
853     var data = repository.getByKNN(
854       containerId,
855       new Coordinate(0, 0, 0),
856       1,
857       0l,
858       2l,
859       Collections.emptyMap(),
860       Collections.emptyList()
861     );
862 
863     assertNotNull(data);
864     assertEquals(1, data.size());
865     assertEquals(dataPoint1, data.get(0));
866   }
867 
868   @Test
869   @Transactional
870   public void getByKNN_filterByTimestamp_returnsNoData() {
871     var containerId = generateManagedContainerId();
872     repository.insert(containerId, new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), null, null));
873 
874     var data = repository.getByKNN(
875       containerId,
876       new Coordinate(0, 0, 0),
877       1,
878       2l,
879       4l,
880       Collections.emptyMap(),
881       Collections.emptyList()
882     );
883 
884     assertNotNull(data);
885     assertEquals(0, data.size());
886   }
887 
888   @Test
889   @Transactional
890   public void getByKNN_filterByTimestampStart_returnsOneRecord() {
891     var containerId = generateManagedContainerId();
892     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), null, null);
893     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(10, 10, 10), null, null);
894     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
895 
896     var data = repository.getByKNN(
897       containerId,
898       new Coordinate(0, 0, 0),
899       1,
900       0l,
901       null,
902       Collections.emptyMap(),
903       Collections.emptyList()
904     );
905 
906     assertNotNull(data);
907     assertEquals(1, data.size());
908     assertEquals(dataPoint1, data.get(0));
909   }
910 
911   @Test
912   @Transactional
913   public void getByKNN_filterByTimestampEnd_returnsOneRecord() {
914     var containerId = generateManagedContainerId();
915     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), null, null);
916     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(1, 1, 1), null, null);
917     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
918 
919     var data = repository.getByKNN(
920       containerId,
921       new Coordinate(0, 0, 0),
922       1,
923       null,
924       2l,
925       Collections.emptyMap(),
926       Collections.emptyList()
927     );
928 
929     assertNotNull(data);
930     assertEquals(1, data.size());
931     assertEquals(dataPoint1, data.get(0));
932   }
933 
934   @Test
935   @Transactional
936   public void getByKNN_filterByMetadataAndTimestamp_returnsOneRecord() {
937     var containerId = generateManagedContainerId();
938     var metadata = new HashMap<String, Object>();
939     metadata.put("layer", 7);
940     metadata.put("track", 10);
941 
942     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadata, null);
943     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(2, 2, 2), metadata, null);
944     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
945 
946     var metadataFilter = new HashMap<String, Object>();
947     metadataFilter.put("layer", 7);
948     var data = repository.getByKNN(
949       containerId,
950       new Coordinate(0, 0, 0),
951       1,
952       0L,
953       2L,
954       metadataFilter,
955       Collections.emptyList()
956     );
957 
958     assertNotNull(data);
959     assertTrue(data.size() == 1);
960     assertTrue(data.get(0).equals(dataPoint1));
961   }
962 
963   @Test
964   @Transactional
965   public void getByKNN_filterByMetadataAndTimestamp_returnsNoData() {
966     var containerId = generateManagedContainerId();
967     var metadata = new HashMap<String, Object>();
968     metadata.put("layer", 7);
969     metadata.put("track", 10);
970 
971     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadata, null);
972     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(2, 2, 2), metadata, null);
973     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
974 
975     var metadataFilter = new HashMap<String, Object>();
976     metadataFilter.put("layer", 0);
977     var data = repository.getByKNN(
978       containerId,
979       new Coordinate(0, 0, 0),
980       1,
981       10L,
982       20L,
983       metadataFilter,
984       Collections.emptyList()
985     );
986 
987     assertNotNull(data);
988     assertTrue(data.size() == 0);
989   }
990 
991   @Test
992   @Transactional
993   public void getByKNN_filterByMeasurements_returnsOneRecord() {
994     var containerId = generateManagedContainerId();
995     var measurments = new HashMap<String, Object>();
996     var imageData = new HashMap<String, Object>();
997     imageData.put("format", "png");
998     imageData.put("size", 5000);
999     measurments.put("temperature", 7);
1000     measurments.put("pressure", 10);
1001     measurments.put("image", imageData);
1002 
1003     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), null, measurments);
1004     imageData.put("size", 3000);
1005     measurments.put("image", imageData);
1006     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(2, 2, 2), null, measurments);
1007     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
1008 
1009     var measurementsFilter = List.of(
1010       new FilterCondition("image,size", Operator.LESS_THAN, 5000),
1011       new FilterCondition("image,size", Operator.GREATER_THAN, 2000),
1012       new FilterCondition("temperature", Operator.EQUALS, 7)
1013     );
1014     var data = repository.getByKNN(
1015       containerId,
1016       new Coordinate(0, 0, 0),
1017       1,
1018       null,
1019       null,
1020       Collections.emptyMap(),
1021       measurementsFilter
1022     );
1023 
1024     assertNotNull(data);
1025     assertTrue(data.size() == 1);
1026   }
1027 
1028   @Test
1029   @Transactional
1030   public void getByKNN_filterByMeasurements_returnsNoData() {
1031     var containerId = generateManagedContainerId();
1032     var measurments = new HashMap<String, Object>();
1033     var imageData = new HashMap<String, Object>();
1034     imageData.put("format", "png");
1035     imageData.put("size", 5000);
1036     measurments.put("temperature", 7);
1037     measurments.put("pressure", 10);
1038     measurments.put("image", imageData);
1039 
1040     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), null, measurments);
1041     imageData.put("size", 3000);
1042     measurments.put("image", imageData);
1043     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(2, 2, 2), null, measurments);
1044     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2 });
1045 
1046     var measurementsFilter = List.of(new FilterCondition("temperature", Operator.EQUALS, 10));
1047     var data = repository.getByKNN(
1048       containerId,
1049       new Coordinate(0, 0, 0),
1050       1,
1051       null,
1052       null,
1053       Collections.emptyMap(),
1054       measurementsFilter
1055     );
1056 
1057     assertNotNull(data);
1058     assertTrue(data.isEmpty());
1059   }
1060 
1061   public void getByKNN_returnsSomeData_withSkip() {
1062     var containerId = generateManagedContainerId();
1063     var metadata = new HashMap<String, Object>();
1064     metadata.put("layer", 16);
1065     metadata.put("track", 16);
1066 
1067     var dataPoint1 = new SpatialDataPoint(containerId, 1l, GeometryBuilder.fromXYZ(1, 1, 1), metadata, null);
1068     var dataPoint2 = new SpatialDataPoint(containerId, 10l, GeometryBuilder.fromXYZ(2, 2, 2), metadata, null);
1069     var dataPoint3 = new SpatialDataPoint(containerId, 5l, GeometryBuilder.fromXYZ(3, 3, 3), metadata, null);
1070     var dataPoint4 = new SpatialDataPoint(containerId, 9l, GeometryBuilder.fromXYZ(4, 4, 4), metadata, null);
1071     repository.insert(containerId, new SpatialDataPoint[] { dataPoint1, dataPoint2, dataPoint3, dataPoint4 });
1072 
1073     var metadataFilter = new HashMap<String, Object>();
1074     metadataFilter.put("layer", 16);
1075     var data = repository.getByKNN(
1076       containerId,
1077       new Coordinate(0, 0, 0),
1078       4,
1079       0L,
1080       20L,
1081       metadataFilter,
1082       Collections.emptyList()
1083     );
1084     assertNotNull(data);
1085     assertEquals(2, data.size());
1086   }
1087 
1088   private Long generateTimestamp() {
1089     final Long timestamp = movingTimeStamp;
1090     movingTimeStamp += 10;
1091     return timestamp;
1092   }
1093 }