diff --git a/sensorhub-core/src/main/java/org/sensorhub/api/datastore/TemporalFilter.java b/sensorhub-core/src/main/java/org/sensorhub/api/datastore/TemporalFilter.java index df496130a9..71a632dbff 100644 --- a/sensorhub-core/src/main/java/org/sensorhub/api/datastore/TemporalFilter.java +++ b/sensorhub-core/src/main/java/org/sensorhub/api/datastore/TemporalFilter.java @@ -39,8 +39,14 @@ public class TemporalFilter extends RangeFilter protected boolean timeRangeBeginsNow; // now = current time at the time of query evaluation protected boolean timeRangeEndsNow; // now = current time at the time of query evaluation protected boolean latestTime; // latest available time (can be in future) + protected boolean descendingOrder; - + protected TemporalFilter() + { + this.range = Range.open(Instant.MIN, Instant.MAX); + } + + public boolean isCurrentTime() { return timeRangeBeginsNow && timeRangeEndsNow; @@ -78,7 +84,13 @@ public boolean isSingleValue() return isCurrentTime() || isLatestTime() || super.isSingleValue(); } - + + + public boolean descendingOrder() + { + return descendingOrder; + } + @Override public Range getRange() @@ -208,6 +220,9 @@ else if (otherFilter.isLatestTime() && isAllTimes()) else if (otherFilter.isAllTimes() && isLatestTime()) return builder.withLatestTime(); + var descendingOrder = this.descendingOrder || otherFilter.descendingOrder; + builder.descendingOrder(descendingOrder); + // otherwise compute time extent intersection var thisTe = asTimeExtent(); var otherTe = otherFilter.asTimeExtent(); @@ -289,6 +304,7 @@ protected B copyFrom(F base) instance.timeRangeBeginsNow = base.timeRangeBeginsNow; instance.timeRangeEndsNow = base.timeRangeEndsNow; instance.latestTime = base.latestTime; + instance.descendingOrder = base.descendingOrder; return (B)this; } @@ -378,5 +394,16 @@ else if (!te.hasEnd()) else return withRange(te.begin(), te.end()); } + + /** + * Specify descending or ascending (default) chronological order. + * @param descending order + * @return This builder for chaining + */ + public B descendingOrder(boolean descending) + { + instance.descendingOrder = descending; + return (B)this; + } } } diff --git a/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStatusStore.java b/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStatusStore.java index 4cf637b9a8..2a7ad4391d 100644 --- a/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStatusStore.java +++ b/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStatusStore.java @@ -15,10 +15,13 @@ package org.sensorhub.impl.database.registry; import java.util.ArrayList; +import java.util.Comparator; import java.util.Map; import java.util.Set; import java.util.stream.Stream; import java.util.stream.StreamSupport; + +import org.sensorhub.api.command.ICommandData; import org.sensorhub.api.command.ICommandStatus; import org.sensorhub.api.common.BigId; import org.sensorhub.api.datastore.command.CommandFilter; @@ -170,10 +173,13 @@ public Stream> selectEntries(CommandStatusFilter fi if (cmdStreams.isEmpty()) return Stream.empty(); + + Comparator> comparator = Comparator.comparing(e -> e.getValue().getReportTime()); + if (filter.getReportTime() != null && filter.getReportTime().descendingOrder()) + comparator = comparator.reversed(); // stream and merge commands from all selected command streams and time periods - var mergeSortIt = new MergeSortSpliterator>(cmdStreams, - (e1, e2) -> e1.getValue().getReportTime().compareTo(e2.getValue().getReportTime())); + var mergeSortIt = new MergeSortSpliterator>(cmdStreams, comparator); // stream output of merge sort iterator + apply limit return StreamSupport.stream(mergeSortIt, false) diff --git a/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStore.java b/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStore.java index 52a05353a3..47f21b16a8 100644 --- a/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStore.java +++ b/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedCommandStore.java @@ -14,10 +14,7 @@ package org.sensorhub.impl.database.registry; -import java.util.ArrayList; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.sensorhub.api.command.ICommandData; @@ -204,10 +201,13 @@ public Stream> selectEntries(CommandFilter filter, Se if (cmdStreams.isEmpty()) return Stream.empty(); - + + Comparator> comparator = Comparator.comparing(e -> e.getValue().getIssueTime()); + if (filter.getIssueTime() != null && filter.getIssueTime().descendingOrder()) + comparator = comparator.reversed(); + // stream and merge commands from all selected command streams and time periods - var mergeSortIt = new MergeSortSpliterator>(cmdStreams, - (e1, e2) -> e1.getValue().getIssueTime().compareTo(e2.getValue().getIssueTime())); + var mergeSortIt = new MergeSortSpliterator>(cmdStreams, comparator); // stream output of merge sort iterator + apply limit return StreamSupport.stream(mergeSortIt, false) diff --git a/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedObsStore.java b/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedObsStore.java index 0e91abf271..cc47191efd 100644 --- a/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedObsStore.java +++ b/sensorhub-core/src/main/java/org/sensorhub/impl/database/registry/FederatedObsStore.java @@ -14,12 +14,11 @@ package org.sensorhub.impl.database.registry; -import java.util.ArrayList; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; + +import org.sensorhub.api.command.ICommandData; import org.sensorhub.api.common.BigId; import org.sensorhub.api.data.IObsData; import org.sensorhub.api.datastore.feature.FoiFilter; @@ -228,10 +227,13 @@ public Stream> selectEntries(ObsFilter filter, Set> comparator = Comparator.comparing(e -> e.getValue().getPhenomenonTime()); + if (filter.getPhenomenonTime() != null && filter.getPhenomenonTime().descendingOrder()) + comparator = comparator.reversed(); // stream and merge obs from all selected datastreams and time periods - var mergeSortIt = new MergeSortSpliterator>(obsStreams, - (e1, e2) -> e1.getValue().getPhenomenonTime().compareTo(e2.getValue().getPhenomenonTime())); + var mergeSortIt = new MergeSortSpliterator>(obsStreams, comparator); // stream output of merge sort iterator + apply limit return StreamSupport.stream(mergeSortIt, false) diff --git a/sensorhub-datastore-h2/src/main/java/org/h2/mvstore/RangeCursor.java b/sensorhub-datastore-h2/src/main/java/org/h2/mvstore/RangeCursor.java index fa2b409a1e..d99b10d339 100644 --- a/sensorhub-datastore-h2/src/main/java/org/h2/mvstore/RangeCursor.java +++ b/sensorhub-datastore-h2/src/main/java/org/h2/mvstore/RangeCursor.java @@ -48,6 +48,7 @@ public RangeCursor(MVMap map, K from) public RangeCursor(MVMap map, K from, K to) { + // TODO: Update to use reverse-order cursor in newer H2 version super(map.cursor(from)); this.map = map; this.to = to; diff --git a/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVCommandStoreImpl.java b/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVCommandStoreImpl.java index a64fd7d897..526fcc7fbd 100644 --- a/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVCommandStoreImpl.java +++ b/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVCommandStoreImpl.java @@ -20,12 +20,7 @@ import java.nio.ByteBuffer; import java.time.Duration; import java.time.Instant; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.stream.Stream; import java.util.stream.StreamSupport; import org.h2.mvstore.MVBTreeMap; @@ -300,10 +295,12 @@ public Stream> selectEntries(CommandFilter filter, Se return getPostFilteredResultStream(cmdStream, filter); })); - + Comparator> comparator = Comparator.comparing(e -> e.getValue().getIssueTime()); + if (filter.getIssueTime() != null && filter.getIssueTime().descendingOrder()) + comparator = comparator.reversed(); + // stream and merge commands from all selected command streams and time periods - var mergeSortIt = new MergeSortSpliterator>(cmdStreams, - (e1, e2) -> e1.getValue().getIssueTime().compareTo(e2.getValue().getIssueTime())); + var mergeSortIt = new MergeSortSpliterator>(cmdStreams, comparator); // stream output of merge sort iterator + apply limit return StreamSupport.stream(mergeSortIt, false) diff --git a/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVObsStoreImpl.java b/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVObsStoreImpl.java index 7c977e6706..24bb242124 100644 --- a/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVObsStoreImpl.java +++ b/sensorhub-datastore-h2/src/main/java/org/sensorhub/impl/datastore/h2/MVObsStoreImpl.java @@ -20,12 +20,7 @@ import java.nio.ByteBuffer; import java.time.Duration; import java.time.Instant; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -34,6 +29,7 @@ import org.h2.mvstore.MVBTreeMap; import org.h2.mvstore.MVStore; import org.h2.mvstore.RangeCursor; +import org.sensorhub.api.command.ICommandData; import org.sensorhub.api.common.BigId; import org.sensorhub.api.data.IObsData; import org.sensorhub.api.datastore.feature.IFoiStore; @@ -463,10 +459,13 @@ public Stream> selectEntries(ObsFilter filter, Set> comparator = Comparator.comparing(e -> e.getValue().getPhenomenonTime()); + if (filter.getPhenomenonTime() != null && filter.getPhenomenonTime().descendingOrder()) + comparator = comparator.reversed(); // stream and merge obs from all selected datastreams and time periods - var mergeSortIt = new MergeSortSpliterator>(obsStreams, - (e1, e2) -> e1.getValue().getPhenomenonTime().compareTo(e2.getValue().getPhenomenonTime())); + var mergeSortIt = new MergeSortSpliterator>(obsStreams, comparator); // stream output of merge sort iterator + apply limit return StreamSupport.stream(mergeSortIt, false) diff --git a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/BaseHandler.java b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/BaseHandler.java index 656eeb9b32..8619e65f81 100644 --- a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/BaseHandler.java +++ b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/BaseHandler.java @@ -275,31 +275,37 @@ protected IdCollection parseResourceIdsOrUids(String paramName, final Map queryParams) throws InvalidRequestException + { + var builder = parseTimeStampArgToBuilder(paramName, queryParams); + if (builder == null) + return null; + return builder.build(); + } + + + protected TemporalFilter.Builder parseTimeStampArgToBuilder(String paramName, final Map queryParams) throws InvalidRequestException { var timeVal = getSingleParam(paramName, queryParams); if (timeVal == null) return null; - + try { if (timeVal.equals("latest")) { return new TemporalFilter.Builder() - .withLatestTime() - .build(); + .withLatestTime(); } else if (timeVal.startsWith("latest/")) { return new TemporalFilter.Builder() - .withLatestTime() - .withRangeBeginningNow(Instant.MAX) - .build(); + .withLatestTime() + .withRangeBeginningNow(Instant.MAX); } else { return new TemporalFilter.Builder() - .fromTimeExtent(TimeExtent.parse(timeVal)) - .build(); + .fromTimeExtent(TimeExtent.parse(timeVal)); } } catch (Exception e) diff --git a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/obs/ObsHandler.java b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/obs/ObsHandler.java index 7081ff9757..86bf7b3759 100644 --- a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/obs/ObsHandler.java +++ b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/obs/ObsHandler.java @@ -34,6 +34,7 @@ import org.sensorhub.api.database.IObsSystemDatabase; import org.sensorhub.api.datastore.DataStoreException; import org.sensorhub.api.datastore.SpatialFilter; +import org.sensorhub.api.datastore.TemporalFilter; import org.sensorhub.api.datastore.obs.DataStreamKey; import org.sensorhub.api.datastore.obs.IObsStore; import org.sensorhub.api.datastore.obs.ObsFilter; @@ -477,12 +478,26 @@ protected ObsFilter getFilter(ResourceRef parent, Map queryPar // filter on parent if needed if (parent.internalID != null) builder.withDataStreams(parent.internalID); - + + // TODO attach to phenomenonTime + var phenomenonTimeFilterBuilder = new TemporalFilter.Builder(); + // phenomenonTime param - var phenomenonTime = parseTimeStampArg("phenomenonTime", queryParams); + var phenomenonTime = parseTimeStampArgToBuilder("phenomenonTime", queryParams); if (phenomenonTime != null) - builder.withPhenomenonTime(phenomenonTime); - + phenomenonTimeFilterBuilder = phenomenonTime; + + // chronological order, attached to phenomenonTime filter + var descendingOrder = getSingleParam("order", queryParams); + if (descendingOrder != null && !descendingOrder.isBlank() + && ("desc".equals(descendingOrder) || "descending".equals(descendingOrder))) + { + phenomenonTimeFilterBuilder.descendingOrder(true); + } + + if (phenomenonTime != null || descendingOrder != null) + builder.withPhenomenonTime(phenomenonTimeFilterBuilder.build()); + // resultTime param var resultTime = parseTimeStampArg("resultTime", queryParams); if (resultTime != null) diff --git a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/resource/BaseResourceHandler.java b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/resource/BaseResourceHandler.java index a79de70239..b520458c21 100644 --- a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/resource/BaseResourceHandler.java +++ b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/resource/BaseResourceHandler.java @@ -265,7 +265,9 @@ protected void list(final RequestContext ctx) throws IOException // stream and serialize all resources to servlet output var binding = getBinding(ctx, false); binding.startCollection(); - + + var data = dataStore.selectEntries(filter).toList(); + // fetch from DB and temporarily handle paging here try (var results = postProcessResultList(dataStore.selectEntries(filter), filter)) { diff --git a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandHandler.java b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandHandler.java index b4006ec316..739c3ade72 100644 --- a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandHandler.java +++ b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandHandler.java @@ -33,6 +33,7 @@ import org.sensorhub.api.database.IObsSystemDatabase; import org.sensorhub.api.command.ICommandStatus.CommandStatusCode; import org.sensorhub.api.datastore.DataStoreException; +import org.sensorhub.api.datastore.TemporalFilter; import org.sensorhub.api.datastore.command.CommandFilter; import org.sensorhub.api.datastore.command.CommandStatusFilter; import org.sensorhub.api.datastore.command.CommandStreamKey; @@ -307,11 +308,24 @@ protected CommandFilter getFilter(ResourceRef parent, Map quer // filter on parent if needed if (parent.internalID != null) builder.withCommandStreams(parent.internalID); - + + var issueTimeFilterBuilder = new TemporalFilter.Builder(); + // issueTime param - var issueTime = parseTimeStampArg("issueTime", queryParams); + var issueTime = parseTimeStampArgToBuilder("issueTime", queryParams); if (issueTime != null) - builder.withIssueTime(issueTime); + issueTimeFilterBuilder = issueTime; + + // chronological order, attached to issueTime filter + var descendingOrder = getSingleParam("order", queryParams); + if (descendingOrder != null && !descendingOrder.isBlank() + && ("desc".equals(descendingOrder) || "descending".equals(descendingOrder))) + { + issueTimeFilterBuilder.descendingOrder(true); + } + + if (issueTime != null || descendingOrder != null) + builder.withIssueTime(issueTimeFilterBuilder.build()); // status filter params var statusCodes = parseMultiValuesArg("statusCode", queryParams); diff --git a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandStatusHandler.java b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandStatusHandler.java index c7cf1be540..798e7f895b 100644 --- a/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandStatusHandler.java +++ b/sensorhub-service-consys/src/main/java/org/sensorhub/impl/service/consys/task/CommandStatusHandler.java @@ -28,6 +28,7 @@ import org.sensorhub.api.common.BigId; import org.sensorhub.api.database.IObsSystemDatabase; import org.sensorhub.api.datastore.DataStoreException; +import org.sensorhub.api.datastore.TemporalFilter; import org.sensorhub.api.datastore.command.CommandStatusFilter; import org.sensorhub.api.datastore.command.CommandStreamKey; import org.sensorhub.api.datastore.command.ICommandStatusStore; @@ -264,11 +265,24 @@ else if (parent.type instanceof CommandStreamHandler) var cmdIDs = parseResourceIds("commands", queryParams, idEncoders.getCommandIdEncoder()); if (parent.internalID == null && cmdIDs != null && !cmdIDs.isEmpty()) builder.withCommands(cmdIDs); - + + var reportTimeFilterBuilder = new TemporalFilter.Builder(); + // reportTime param - var issueTime = parseTimeStampArg("reportTime", queryParams); + var issueTime = parseTimeStampArgToBuilder("reportTime", queryParams); if (issueTime != null) - builder.withReportTime(issueTime); + reportTimeFilterBuilder = issueTime; + + // chronological order. attach to reportTime filter + var descendingOrder = getSingleParam("order", queryParams); + if (descendingOrder != null && !descendingOrder.isBlank() + && ("desc".equals(descendingOrder) || "descending".equals(descendingOrder))) + { + reportTimeFilterBuilder.descendingOrder(true); + } + + if (issueTime != null || descendingOrder != null) + builder.withReportTime(reportTimeFilterBuilder.build()); // executionTime param var execTime = parseTimeStampArg("executionTime", queryParams);