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