From 1dfa05558f58ef3fc626c08abe80d6027ee25148 Mon Sep 17 00:00:00 2001 From: Florian Spiess Date: Fri, 18 Feb 2022 16:35:48 +0100 Subject: [PATCH] Update REST API (#264) --- README.md | 6 +- .../org/vitrivr/cineast/api/APIEndpoint.java | 20 +- .../api/messages/query/QueryComponent.java | 51 ---- .../api/messages/query/QueryStage.java | 10 +- .../api/messages/query/SimilarityQuery.java | 17 +- .../cineast/api/rest/OpenApiCompatHelper.java | 20 +- .../FindSegmentSimilarPostHandler.java | 33 +-- .../FindSegmentSimilarStagedPostHandler.java | 59 +++++ ...FindSegmentSimilarTemporalPostHandler.java | 59 +++++ .../vitrivr/cineast/api/util/QueryUtil.java | 201 +++++++++++---- .../queries/TemporalQueryMessageHandler.java | 8 +- .../cineast/core/data/TemporalObject.java | 6 +- .../db/dao/reader/MediaSegmentReader.java | 4 - .../core/temporal/TemporalScoring.java | 26 +- .../config/ConstrainedQueryConfig.java | 10 + docs/openapi.json | 237 +++++++++++++++--- gradle.properties | 2 +- 17 files changed, 515 insertions(+), 254 deletions(-) delete mode 100644 cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryComponent.java create mode 100644 cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarStagedPostHandler.java create mode 100644 cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarTemporalPostHandler.java diff --git a/README.md b/README.md index a432077c3..a266af348 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ [![vitrivr - cineast](https://img.shields.io/static/v1?label=vitrivr&message=cineast&color=blue&logo=github)](https://github.com/vitrivr/cineast) [![GitHub release](https://img.shields.io/github/release/vitrivr/cineast?include_prereleases=&sort=semver&color=2ea44f)](https://github.com/vitrivr/cineast/releases/) -[![License](https://img.shields.io/badge/License-MIT-blueviolet)](#license) +[![License](https://img.shields.io/badge/License-MIT-blueviolet)](LICENSE) [![swagger-editor](https://img.shields.io/badge/open--API-in--editor-green.svg?style=flat&label=Open-Api%20(Release))](https://editor.swagger.io/?url=https://raw.githubusercontent.com/vitrivr/cineast/master/docs/openapi.json) -[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-green.svg?style=flat&label=Open-Api%20(Dev))](https://editor.swagger.io/?url=https://raw.githubusercontent.com/vitrivr/cineast/master/docs/openapi.json) +[![swagger-editor](https://img.shields.io/badge/open--API-in--editor-green.svg?style=flat&label=Open-Api%20(Dev))](https://editor.swagger.io/?url=https://raw.githubusercontent.com/vitrivr/cineast/dev/docs/openapi.json) [![Java CI with Gradle](https://github.com/vitrivr/cineast/workflows/Java%20CI%20with%20Gradle/badge.svg)](https://github.com/vitrivr/cineast/actions?query=workflow:"Java+CI+with+Gradle") # Cineast @@ -47,7 +47,7 @@ $> ./gradlew -PcineastConfig= generateOpenApiSpecs ``` You can omit `-PcineastConfig`, then the default config (`cineast.json`) is used. -As a result, the OAS is stored at `docs/swagger.json` +As a result, the OAS is stored at `docs/openapi.json` ## Prerequisites diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/APIEndpoint.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/APIEndpoint.java index 3acab9eef..ffe73f9cf 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/APIEndpoint.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/APIEndpoint.java @@ -38,6 +38,8 @@ import org.vitrivr.cineast.api.rest.handlers.actions.metadata.FindSegmentMetadataGetHandler; import org.vitrivr.cineast.api.rest.handlers.actions.segment.FindSegmentByIdPostHandler; import org.vitrivr.cineast.api.rest.handlers.actions.segment.FindSegmentSimilarPostHandler; +import org.vitrivr.cineast.api.rest.handlers.actions.segment.FindSegmentSimilarStagedPostHandler; +import org.vitrivr.cineast.api.rest.handlers.actions.segment.FindSegmentSimilarTemporalPostHandler; import org.vitrivr.cineast.api.rest.handlers.actions.segment.FindSegmentsByIdGetHandler; import org.vitrivr.cineast.api.rest.handlers.actions.segment.FindSegmentsByObjectIdGetHandler; import org.vitrivr.cineast.api.rest.handlers.actions.session.EndExtractionHandler; @@ -290,21 +292,13 @@ public Javalin dispatchService(boolean secure) { this.webSocketApi = new WebsocketAPI(); service.ws(String.format("%s/websocket", namespace()), handler -> { - handler.onConnect(ctx -> { - webSocketApi.connected(ctx.session); - }); + handler.onConnect(ctx -> webSocketApi.connected(ctx.session)); - handler.onClose(ctx -> { - webSocketApi.closed(ctx.session, ctx.status(), ctx.reason()); - }); + handler.onClose(ctx -> webSocketApi.closed(ctx.session, ctx.status(), ctx.reason())); - handler.onError(ctx -> { - webSocketApi.onWebSocketException(ctx.session, ctx.error()); - }); + handler.onError(ctx -> webSocketApi.onWebSocketException(ctx.session, ctx.error())); - handler.onMessage(ctx -> { - webSocketApi.message(ctx.session, ctx.message()); - }); + handler.onMessage(ctx -> webSocketApi.message(ctx.session, ctx.message())); }); } @@ -412,6 +406,8 @@ private void registerRestOperations() { new FindSegmentsByIdGetHandler(), new FindSegmentsByObjectIdGetHandler(), new FindSegmentSimilarPostHandler(retrievalLogic), + new FindSegmentSimilarStagedPostHandler(retrievalLogic), + new FindSegmentSimilarTemporalPostHandler(retrievalLogic), new FindSegmentFeaturesGetHandler(), new FindFeaturesByCategoryGetHandler(), new FindFeaturesByEntityGetHandler(), diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryComponent.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryComponent.java deleted file mode 100644 index 88e2b612c..000000000 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryComponent.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.vitrivr.cineast.api.messages.query; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; -import org.apache.commons.lang3.builder.ReflectionToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.vitrivr.cineast.core.data.query.containers.AbstractQueryTermContainer; - -/** - * The wording is suboptimal. A {@link QueryComponent} has only one containerID, but multiple {@link AbstractQueryTermContainer}s are created out of it. - *

- * These all have the {@link QueryComponent#containerId} of their parent. - * - * @deprecated use {@link TemporalQuery} instead, which uses {@link StagedSimilarityQuery} which contains {@link QueryStage} which in turn contains {@link QueryTerm} - */ -@Deprecated -public class QueryComponent { - - /** - * List of {@link QueryTerm}s in this {@link QueryComponent}. - */ - private final List terms; - - /** - * The client-generated uuid for this container for reference purposes - */ - public final int containerId; - - private static final Logger LOGGER = LogManager.getLogger(); - - /** - * Constructor for QueryComponent. - */ - @JsonCreator - public QueryComponent(@JsonProperty("terms") List terms, @JsonProperty("containerId") int containerId) { - this.terms = terms; - this.containerId = containerId; - } - - public List getTerms() { - return this.terms; - } - - @Override - public String toString() { - return ReflectionToStringBuilder.toString(this, ToStringStyle.JSON_STYLE); - } -} diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryStage.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryStage.java index e9b26cb72..e510b9d94 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryStage.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/QueryStage.java @@ -5,7 +5,6 @@ import java.util.List; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.vitrivr.cineast.core.config.QueryConfig; /** * A {@link QueryStage} contains a list of {@link QueryTerm}s. This object represents a stage in a {@link StagedSimilarityQuery}. @@ -17,21 +16,14 @@ public class QueryStage { */ public final List terms; - /** - * The {@link QueryConfig} that should be used to configure the query. May be null! - */ - public final QueryConfig config; - /** * Constructor for the QueryStage object. * * @param terms List of {@link QueryTerm}s. - * @param config The {@link QueryConfig}. May be null! */ @JsonCreator - public QueryStage(@JsonProperty(value = "terms", required = true) List terms, @JsonProperty(value = "config", required = false) QueryConfig config) { + public QueryStage(@JsonProperty(value = "terms", required = true) List terms) { this.terms = terms; - this.config = config; } @Override diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/SimilarityQuery.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/SimilarityQuery.java index 129160314..85d34f5a5 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/SimilarityQuery.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/messages/query/SimilarityQuery.java @@ -8,33 +8,32 @@ import org.vitrivr.cineast.core.config.ReadableQueryConfig; /** - * A {@link SimilarityQuery} contains a list of {@link QueryComponent}s. This object represents a similarity-query message, i.e. a request for a similarity-search. + * A {@link SimilarityQuery} contains a list of {@link QueryTerm}s. This object represents a similarity-query message, i.e. a request for a similarity-search. */ public class SimilarityQuery extends Query { /** - * List of {@link QueryComponent}s that are part of this {@link SimilarityQuery}. + * List of {@link QueryTerm}s that are part of this {@link SimilarityQuery}. */ - private final List components; + private final List terms; /** * Constructor for the SimilarityQuery object. * - * @param components List of {@link QueryComponent}s. + * @param terms List of {@link QueryTerm}s. * @param config The {@link ReadableQueryConfig}. May be null! */ @JsonCreator - public SimilarityQuery(@JsonProperty(value = "containers", required = true) List components, - @JsonProperty(value = "config", required = false) QueryConfig config) { + public SimilarityQuery(@JsonProperty(value = "terms", required = true) List terms, @JsonProperty(value = "config") QueryConfig config) { super(config); - this.components = components; + this.terms = terms; } /** * Getter for containers. */ - public List getComponents() { - return this.components; + public List getTerms() { + return this.terms; } /** diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/OpenApiCompatHelper.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/OpenApiCompatHelper.java index c764427b3..a33313f1c 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/OpenApiCompatHelper.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/OpenApiCompatHelper.java @@ -1,6 +1,5 @@ package org.vitrivr.cineast.api.rest; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -21,12 +20,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.vitrivr.cineast.api.APIEndpoint; -import org.vitrivr.cineast.api.messages.interfaces.MessageType; import org.vitrivr.cineast.standalone.config.APIConfig; /** @@ -41,7 +39,7 @@ public class OpenApiCompatHelper { public static final String SEGMENT_OAS_TAG = "Segment"; - public static final List OAS_TAGS = Arrays.asList( + public static final List OAS_TAGS = Collections.singletonList( new Tag().name(METADATA_OAS_TAG).description("Metadata related operations") ); private static final Logger LOGGER = LogManager.getLogger(); @@ -115,11 +113,11 @@ public static void writeOpenApiDocPersistently(APIEndpoint apiEndpoint, final St apiEndpoint.getOpenApi().getOpenApiHandler().createOpenAPISchema()); File file = new File(path); File folder = file.getParentFile(); - if (folder != null) { - folder.mkdirs(); + if (folder != null && !folder.exists() && !folder.mkdirs()) { + LOGGER.warn("Could not create OpenAPI documentation path: {}", folder.getAbsolutePath()); } - if (file.exists()) { - file.delete(); + if (file.exists() && !file.delete()) { + LOGGER.warn("Could not delete existing OpenAPI documentation: {}", file.getAbsolutePath()); } try (FileOutputStream stream = new FileOutputStream( file); PrintWriter writer = new PrintWriter(stream)) { @@ -132,10 +130,4 @@ public static void writeOpenApiDocPersistently(APIEndpoint apiEndpoint, final St APIEndpoint.stop(); } } - - private static abstract class MediaObjectMetadataQueryResultMixin { - - @JsonIgnore - public abstract MessageType getMessageType(); - } } diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarPostHandler.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarPostHandler.java index d8e901d71..e129d7844 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarPostHandler.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarPostHandler.java @@ -3,20 +3,10 @@ import io.javalin.http.Context; import io.javalin.plugin.openapi.dsl.OpenApiBuilder; import io.javalin.plugin.openapi.dsl.OpenApiDocumentation; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; import org.vitrivr.cineast.api.messages.query.SimilarityQuery; import org.vitrivr.cineast.api.messages.result.SimilarityQueryResultBatch; import org.vitrivr.cineast.api.rest.handlers.interfaces.ParsingPostRestHandler; import org.vitrivr.cineast.api.util.QueryUtil; -import org.vitrivr.cineast.core.config.QueryConfig; -import org.vitrivr.cineast.core.config.ReadableQueryConfig; -import org.vitrivr.cineast.core.data.Pair; -import org.vitrivr.cineast.core.data.StringDoublePair; -import org.vitrivr.cineast.core.data.query.containers.AbstractQueryTermContainer; -import org.vitrivr.cineast.standalone.config.Config; import org.vitrivr.cineast.standalone.config.ConstrainedQueryConfig; import org.vitrivr.cineast.standalone.util.ContinuousRetrievalLogic; @@ -31,28 +21,11 @@ public FindSegmentSimilarPostHandler(ContinuousRetrievalLogic continuousRetrieva @Override public SimilarityQueryResultBatch performPost(SimilarityQuery query, Context ctx) { + ConstrainedQueryConfig config = ConstrainedQueryConfig.getApplyingConfig(query.getQueryConfig()); - HashMap> returnMap = new HashMap<>(); - /* - * Prepare map that maps categories to QueryTerm components. - */ - HashMap> categoryMap = QueryUtil.groupComponentsByCategory(query.getComponents()); + var returnMap = QueryUtil.findSegmentsSimilar(continuousRetrievalLogic, query.getTerms(), config); - QueryConfig config = query.getQueryConfig(); - ConstrainedQueryConfig qconf = new ConstrainedQueryConfig(config); - if (config == null) { - final int max = Math.min(qconf.getMaxResults().orElse(Config.sharedConfig().getRetriever().getMaxResults()), Config.sharedConfig().getRetriever().getMaxResults()); - qconf.setMaxResults(max); - final int resultsPerModule = Math.min(qconf.getRawResultsPerModule() == -1 ? Config.sharedConfig().getRetriever().getMaxResultsPerModule() : qconf.getResultsPerModule(), Config.sharedConfig().getRetriever().getMaxResultsPerModule()); - qconf.setResultsPerModule(resultsPerModule); - } - - for (String category : categoryMap.keySet()) { - List> containerList = categoryMap.get(category).stream().map(x -> new Pair<>(x, (ReadableQueryConfig) qconf)).collect(Collectors.toList()); - returnMap.put(category, QueryUtil.retrieveCategory(continuousRetrievalLogic, containerList, category)); - } - - return new SimilarityQueryResultBatch(returnMap, qconf.getQueryId().toString()); + return new SimilarityQueryResultBatch(returnMap, config.getQueryId().toString()); } @Override diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarStagedPostHandler.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarStagedPostHandler.java new file mode 100644 index 000000000..7a75c262f --- /dev/null +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarStagedPostHandler.java @@ -0,0 +1,59 @@ +package org.vitrivr.cineast.api.rest.handlers.actions.segment; + +import io.javalin.http.Context; +import io.javalin.plugin.openapi.dsl.OpenApiBuilder; +import io.javalin.plugin.openapi.dsl.OpenApiDocumentation; +import org.vitrivr.cineast.api.messages.query.StagedSimilarityQuery; +import org.vitrivr.cineast.api.messages.result.SimilarityQueryResultBatch; +import org.vitrivr.cineast.api.rest.handlers.interfaces.ParsingPostRestHandler; +import org.vitrivr.cineast.api.util.QueryUtil; +import org.vitrivr.cineast.standalone.config.ConstrainedQueryConfig; +import org.vitrivr.cineast.standalone.util.ContinuousRetrievalLogic; + +public class FindSegmentSimilarStagedPostHandler implements ParsingPostRestHandler { + + public static final String ROUTE = "find/segments/similar/staged"; + private final ContinuousRetrievalLogic continuousRetrievalLogic; + + public FindSegmentSimilarStagedPostHandler(ContinuousRetrievalLogic continuousRetrievalLogic) { + this.continuousRetrievalLogic = continuousRetrievalLogic; + } + + + @Override + public OpenApiDocumentation docs() { + return OpenApiBuilder.document() + .operation(op -> { + op.summary("Find similar segments based on the given staged query"); + op.description("Performs a similarity search based on the formulated query stages, executing each subsequent stage on the results of the previous stage"); + op.operationId("findSegmentSimilarStaged"); + op.addTagsItem("Segments"); + }) + .body(inClass()) + .json("200", outClass()); + } + + @Override + public SimilarityQueryResultBatch performPost(StagedSimilarityQuery query, Context ctx) { + ConstrainedQueryConfig config = ConstrainedQueryConfig.getApplyingConfig(query.getConfig()); + + var results = QueryUtil.findSegmentsSimilarStaged(continuousRetrievalLogic, query.getStages(), config); + + return new SimilarityQueryResultBatch(results, config.getQueryId().toString()); + } + + @Override + public Class inClass() { + return StagedSimilarityQuery.class; + } + + @Override + public Class outClass() { + return SimilarityQueryResultBatch.class; + } + + @Override + public String route() { + return ROUTE; + } +} diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarTemporalPostHandler.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarTemporalPostHandler.java new file mode 100644 index 000000000..37b31be7e --- /dev/null +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/rest/handlers/actions/segment/FindSegmentSimilarTemporalPostHandler.java @@ -0,0 +1,59 @@ +package org.vitrivr.cineast.api.rest.handlers.actions.segment; + +import io.javalin.http.Context; +import io.javalin.plugin.openapi.dsl.OpenApiBuilder; +import io.javalin.plugin.openapi.dsl.OpenApiDocumentation; +import org.vitrivr.cineast.api.messages.query.TemporalQuery; +import org.vitrivr.cineast.api.messages.result.TemporalQueryResult; +import org.vitrivr.cineast.api.rest.handlers.interfaces.ParsingPostRestHandler; +import org.vitrivr.cineast.api.util.QueryUtil; +import org.vitrivr.cineast.standalone.config.ConstrainedQueryConfig; +import org.vitrivr.cineast.standalone.util.ContinuousRetrievalLogic; + +public class FindSegmentSimilarTemporalPostHandler implements ParsingPostRestHandler { + + public static final String ROUTE = "find/segments/similar/temporal"; + private final ContinuousRetrievalLogic continuousRetrievalLogic; + + public FindSegmentSimilarTemporalPostHandler(ContinuousRetrievalLogic continuousRetrievalLogic) { + this.continuousRetrievalLogic = continuousRetrievalLogic; + } + + + @Override + public OpenApiDocumentation docs() { + return OpenApiBuilder.document() + .operation(op -> { + op.summary("Find similar segments based on the given temporal query"); + op.description("Performs a similarity search based on the formulated query stages in the given temporal order, scoring final results by their similarity to the specified temporal context"); + op.operationId("findSegmentSimilarTemporal"); + op.addTagsItem("Segments"); + }) + .body(inClass()) + .json("200", outClass()); + } + + @Override + public TemporalQueryResult performPost(TemporalQuery query, Context ctx) { + ConstrainedQueryConfig config = ConstrainedQueryConfig.getApplyingConfig(query.getQueryConfig()); + + var temporalResults = QueryUtil.findSegmentsSimilarTemporal(continuousRetrievalLogic, query, config); + + return new TemporalQueryResult(config.getQueryId().toString(), temporalResults); + } + + @Override + public Class inClass() { + return TemporalQuery.class; + } + + @Override + public Class outClass() { + return TemporalQueryResult.class; + } + + @Override + public String route() { + return ROUTE; + } +} diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/util/QueryUtil.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/util/QueryUtil.java index 9b43bebfb..e75543595 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/util/QueryUtil.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/util/QueryUtil.java @@ -5,27 +5,35 @@ import gnu.trove.map.hash.TObjectDoubleHashMap; import java.util.ArrayList; -import java.util.Collections; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.vitrivr.cineast.api.messages.query.QueryComponent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.vitrivr.cineast.api.messages.query.QueryStage; import org.vitrivr.cineast.api.messages.query.QueryTerm; +import org.vitrivr.cineast.api.messages.query.TemporalQuery; import org.vitrivr.cineast.api.messages.result.FeaturesAllCategoriesQueryResult; import org.vitrivr.cineast.api.messages.result.FeaturesByCategoryQueryResult; import org.vitrivr.cineast.api.messages.result.FeaturesByEntityQueryResult; +import org.vitrivr.cineast.core.config.QueryConfig; import org.vitrivr.cineast.core.config.ReadableQueryConfig; import org.vitrivr.cineast.core.data.Pair; import org.vitrivr.cineast.core.data.StringDoublePair; +import org.vitrivr.cineast.core.data.TemporalObject; import org.vitrivr.cineast.core.data.providers.primitive.PrimitiveTypeProvider; import org.vitrivr.cineast.core.data.providers.primitive.StringTypeProvider; import org.vitrivr.cineast.core.data.query.containers.AbstractQueryTermContainer; import org.vitrivr.cineast.core.data.score.SegmentScoreElement; import org.vitrivr.cineast.core.data.tag.Tag; import org.vitrivr.cineast.core.db.DBSelector; +import org.vitrivr.cineast.core.db.dao.reader.MediaSegmentReader; import org.vitrivr.cineast.core.db.dao.reader.TagReader; import org.vitrivr.cineast.core.features.SegmentTags; +import org.vitrivr.cineast.core.temporal.TemporalScoring; import org.vitrivr.cineast.core.util.math.MathHelper; import org.vitrivr.cineast.standalone.config.Config; import org.vitrivr.cineast.standalone.config.RetrievalRuntimeConfig; @@ -34,39 +42,136 @@ //TODO maybe this should be moved to core? public class QueryUtil { - public static HashMap> groupComponentsByCategory( - List queryComponents) { - HashMap> categoryMap = new HashMap<>(); - for (QueryComponent component : queryComponents) { - for (QueryTerm term : component.getTerms()) { - if (term.getCategories() == null) { - continue; + private static final Logger LOGGER = LogManager.getLogger(); + + /** + * Executes a similarity query specified by the list of {@link QueryTerm}s. + * + * @param continuousRetrievalLogic The continuous retrieval logic to execute the query. + * @param terms The terms specifying the query. + * @param config The config to use for this query. + * @return The query results as a map of query term categories to scored segment lists. + */ + public static HashMap> findSegmentsSimilar(ContinuousRetrievalLogic continuousRetrievalLogic, List terms, QueryConfig config) { + HashMap> returnMap = new HashMap<>(); + + // Group terms by categories + var categoryMap = QueryUtil.groupQueryTermsByCategory(terms); + + for (var category : categoryMap.keySet()) { + var containerList = categoryMap.get(category).stream().map(x -> new Pair<>(x, (ReadableQueryConfig) config)).collect(Collectors.toList()); + var categoryResults = QueryUtil.retrieveCategory(continuousRetrievalLogic, containerList, category); + returnMap.put(category, categoryResults); + } + + return returnMap; + } + + /** + * Executes a staged similarity query specified by the list of {@link QueryStage}s. + *

+ * Each {@link QueryTerm} category is expected to appear no more than once in the entire query. + * + * @param continuousRetrievalLogic The continuous retrieval logic to execute the query. + * @param stages The stages specifying the query. + * @param config The config to use for this query. + * @return The query results as a map of query term categories to scored segment lists. + */ + public static HashMap> findSegmentsSimilarStaged(ContinuousRetrievalLogic continuousRetrievalLogic, List stages, QueryConfig config) { + var stageConfig = config.clone(); + + var stagedQueryResults = new ArrayList>>(); + + for (QueryStage stage : stages) { + var stageResults = findSegmentsSimilar(continuousRetrievalLogic, stage.terms, stageConfig); + stagedQueryResults.add(stageResults); + + var relevantSegments = new HashSet(); + for (var result : stageResults.values()) { + relevantSegments.addAll(result.stream().map(pair -> pair.key).collect(Collectors.toList())); + } + + // Return empty results if there are no more results in stage + if (relevantSegments.isEmpty()) { + return stageResults; + } + + stageConfig.setRelevantSegmentIds(relevantSegments); + } + + return mergeStagedQueryResults(stagedQueryResults); + } + + /** + * Executes a temporal similarity query. + * + * @param continuousRetrievalLogic The continuous retrieval logic to execute the query. + * @param query The temporal query to execute. + * @param config The config to use for the execution of the query + * @return The query results as a list of temporal objects. + */ + public static List findSegmentsSimilarTemporal(ContinuousRetrievalLogic continuousRetrievalLogic, TemporalQuery query, QueryConfig config) { + var stagedResults = query.getQueries().stream() + .map(stagedQuery -> findSegmentsSimilarStaged(continuousRetrievalLogic, stagedQuery.getStages(), config)) + .collect(Collectors.toList()); + + // TODO: New MediaSegmentReader for every request like FindSegmentByIdPostHandler or one persistent on per endpoint like AbstractQueryMessageHandler? + final var segmentReader = new MediaSegmentReader(Config.sharedConfig().getDatabase().getSelectorSupplier().get()); + + var segmentIds = stagedResults.stream().flatMap( + resultsMap -> resultsMap.values().stream().flatMap( + pairs -> pairs.stream().map(pair -> pair.key) + ) + ).distinct().collect(Collectors.toList()); + + var segmentDescriptors = segmentReader.lookUpSegments(segmentIds); + var stagedQueryResults = stagedResults.stream().map( + resultsMap -> resultsMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList()) + ).collect(Collectors.toList()); + + return TemporalScoring.score(segmentDescriptors, stagedQueryResults, query.getTimeDistances(), query.getMaxLength()); + } + + /** + * Merges staged query results into a single result map. + * + * @param stagedQueryResults List of staged query results by stage. The results are expected to be in order of the stages. + * @return Map of merged results mapping categories to their results filtered to the final set of segments. + */ + public static HashMap> mergeStagedQueryResults(ArrayList>> stagedQueryResults) { + var results = new HashMap>(); + + var relevantSegments = new HashSet(); + for (var result : stagedQueryResults.get(stagedQueryResults.size() - 1).values()) { + relevantSegments.addAll(result.stream().map(pair -> pair.key).collect(Collectors.toList())); + } + + for (var stageResults : stagedQueryResults) { + for (var category : stageResults.keySet()) { + if (results.containsKey(category)) { + LOGGER.warn("Staged query contained the category \"{}\" multiple times.", category); } - term.getCategories().forEach((String category) -> { - if (!categoryMap.containsKey(category)) { - categoryMap.put(category, new ArrayList<>()); - } - categoryMap.get(category).add(term.toContainer()); - }); + var filteredResults = stageResults.get(category).stream().filter(pair -> relevantSegments.contains(pair.key)).collect(Collectors.toList()); + results.put(category, filteredResults); } } - return categoryMap; + + return results; } - public static HashMap>> groupTermsByCategory( - List terms) { - HashMap>> categoryMap = new HashMap<>(); - for (org.vitrivr.cineast.api.grpc.data.QueryTerm term : terms) { - if (term.getCategories().isEmpty()) { + public static HashMap> groupQueryTermsByCategory(List queryTerms) { + HashMap> categoryMap = new HashMap<>(); + for (QueryTerm term : queryTerms) { + if (term.getCategories() == null) { + LOGGER.warn("Encountered query term without categories. Ignoring: {}", term.toString()); continue; } term.getCategories().forEach((String category) -> { if (!categoryMap.containsKey(category)) { categoryMap.put(category, new ArrayList<>()); } - categoryMap.get(category).add(new Pair<>(term.getContainer(), term.getQueryConfig())); + categoryMap.get(category).add(term.toContainer()); }); - } return categoryMap; } @@ -86,22 +191,7 @@ public static List retrieveCategory( float weight = MathHelper.limit(qc.getWeight(), -1f, 1f); - List scoreResults; - if (qc.hasId()) { - scoreResults = continuousRetrievalLogic.retrieve(qc.getId(), category, qconf); - } else { - scoreResults = continuousRetrievalLogic.retrieve(qc, category, qconf); - } - - for (SegmentScoreElement element : scoreResults) { - String segmentId = element.getSegmentId(); - double score = element.getScore(); - if (Double.isInfinite(score) || Double.isNaN(score)) { - continue; - } - double weightedScore = score * weight; - scoreBySegmentId.adjustOrPutValue(segmentId, weightedScore, weightedScore); - } + retrieveAndWeight(continuousRetrievalLogic, category, scoreBySegmentId, qc, qconf, weight); } final List list = new ArrayList<>(scoreBySegmentId.size()); @@ -112,8 +202,9 @@ public static List retrieveCategory( return true; }); - Collections.sort(list, StringDoublePair.COMPARATOR); + list.sort(StringDoublePair.COMPARATOR); + // FIXME: Using an arbitrary query config to limit results is prone to errors final int MAX_RESULTS = queryContainers.get(0).second.getMaxResults() .orElse(Config.sharedConfig().getRetriever().getMaxResults()); List resultList = list; @@ -128,11 +219,25 @@ public static List retrieve(ContinuousRetrievalLogic continuou float weight = MathHelper.limit(queryTermContainer.getWeight(), -1f, 1f); TObjectDoubleHashMap scoreBySegmentId = new TObjectDoubleHashMap<>(); + retrieveAndWeight(continuousRetrievalLogic, category, scoreBySegmentId, queryTermContainer, config, weight); + + final List list = new ArrayList<>(scoreBySegmentId.size()); + scoreBySegmentId.forEachEntry((segmentId, score) -> { + if (score > 0) { + list.add(new StringDoublePair(segmentId, score)); + } + return true; + }); + + return list; + } + + private static void retrieveAndWeight(ContinuousRetrievalLogic continuousRetrievalLogic, String category, TObjectDoubleHashMap scoreBySegmentId, AbstractQueryTermContainer qc, ReadableQueryConfig qconf, float weight) { List scoreResults; - if (queryTermContainer.hasId()) { - scoreResults = continuousRetrievalLogic.retrieve(queryTermContainer.getId(), category, config); + if (qc.hasId()) { + scoreResults = continuousRetrievalLogic.retrieve(qc.getId(), category, qconf); } else { - scoreResults = continuousRetrievalLogic.retrieve(queryTermContainer, category, config); + scoreResults = continuousRetrievalLogic.retrieve(qc, category, qconf); } for (SegmentScoreElement element : scoreResults) { @@ -144,16 +249,6 @@ public static List retrieve(ContinuousRetrievalLogic continuou double weightedScore = score * weight; scoreBySegmentId.adjustOrPutValue(segmentId, weightedScore, weightedScore); } - - final List list = new ArrayList<>(scoreBySegmentId.size()); - scoreBySegmentId.forEachEntry((segmentId, score) -> { - if (score > 0) { - list.add(new StringDoublePair(segmentId, score)); - } - return true; - }); - - return list; } /** diff --git a/cineast-api/src/main/java/org/vitrivr/cineast/api/websocket/handlers/queries/TemporalQueryMessageHandler.java b/cineast-api/src/main/java/org/vitrivr/cineast/api/websocket/handlers/queries/TemporalQueryMessageHandler.java index feaa08441..4afed8c98 100644 --- a/cineast-api/src/main/java/org/vitrivr/cineast/api/websocket/handlers/queries/TemporalQueryMessageHandler.java +++ b/cineast-api/src/main/java/org/vitrivr/cineast/api/websocket/handlers/queries/TemporalQueryMessageHandler.java @@ -127,7 +127,7 @@ public void execute(Session session, QueryConfig qconf, TemporalQuery message, S .collect(Collectors.toList()); if (results.isEmpty()) { - LOGGER.warn("No results found for category {} and qt {} in stage with id {}. Full compoment: {}", category, qt.getType(), lambdaFinalContainerIdx, stage); + LOGGER.warn("No results found for category {} and qt {} in stage with id {}. Full component: {}", category, qt.getType(), lambdaFinalContainerIdx, stage); } if (cache.get(stageIndex).containsKey(category)) { LOGGER.error("Category {} was used twice in stage {}. This erases the results of the previous category... ", category, stageIndex); @@ -137,7 +137,7 @@ public void execute(Session session, QueryConfig qconf, TemporalQuery message, S results.forEach(res -> relevantSegments.add(res.key)); /* - * If this is the last stage, we can collect the results and send relevant results per category back the the requester. + * If this is the last stage, we can collect the results and send relevant results per category back the requester. * Otherwise we shouldn't yet send since we might send results to the requester that would be filtered at a later stage. */ if (stageIndex == stagedSimilarityQuery.getStages().size() - 1) { @@ -222,10 +222,8 @@ public void execute(Session session, QueryConfig qconf, TemporalQuery message, S IntStream.range(0, message.getQueries().size()).forEach(idx -> tmpContainerResults.add(containerResults.getOrDefault(idx, new ArrayList<>()))); - TemporalScoring temporalScoring = new TemporalScoring(segmentMap, tmpContainerResults, message.getTimeDistances(), message.getMaxLength()); - /* Score and retrieve the results */ - List results = temporalScoring.score(); + List results = TemporalScoring.score(segmentMap, tmpContainerResults, message.getTimeDistances(), message.getMaxLength()); List finalResults = results.stream() .sorted(TemporalObject.COMPARATOR.reversed()) diff --git a/cineast-core/src/main/java/org/vitrivr/cineast/core/data/TemporalObject.java b/cineast-core/src/main/java/org/vitrivr/cineast/core/data/TemporalObject.java index d3656b1fc..01eaafb21 100644 --- a/cineast-core/src/main/java/org/vitrivr/cineast/core/data/TemporalObject.java +++ b/cineast-core/src/main/java/org/vitrivr/cineast/core/data/TemporalObject.java @@ -32,11 +32,7 @@ public List getSegments() { return segments; } - public static final Comparator COMPARATOR = new Comparator() { - public int compare(TemporalObject o1, TemporalObject o2) { - return Double.compare(o1.getScore(), o2.getScore()); - } - }; + public static final Comparator COMPARATOR = Comparator.comparingDouble(TemporalObject::getScore); @Override public String toString() { diff --git a/cineast-core/src/main/java/org/vitrivr/cineast/core/db/dao/reader/MediaSegmentReader.java b/cineast-core/src/main/java/org/vitrivr/cineast/core/db/dao/reader/MediaSegmentReader.java index 367e9a9d4..e324a33a2 100644 --- a/cineast-core/src/main/java/org/vitrivr/cineast/core/db/dao/reader/MediaSegmentReader.java +++ b/cineast-core/src/main/java/org/vitrivr/cineast/core/db/dao/reader/MediaSegmentReader.java @@ -14,8 +14,6 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.vitrivr.cineast.core.data.entities.MediaSegmentDescriptor; import org.vitrivr.cineast.core.data.providers.primitive.PrimitiveTypeProvider; import org.vitrivr.cineast.core.data.providers.primitive.StringTypeProvider; @@ -23,8 +21,6 @@ public class MediaSegmentReader extends AbstractEntityReader { - private static final Logger LOGGER = LogManager.getLogger(); - /** * Constructor for MediaSegmentReader * diff --git a/cineast-core/src/main/java/org/vitrivr/cineast/core/temporal/TemporalScoring.java b/cineast-core/src/main/java/org/vitrivr/cineast/core/temporal/TemporalScoring.java index 542f48602..f1f8c312d 100644 --- a/cineast-core/src/main/java/org/vitrivr/cineast/core/temporal/TemporalScoring.java +++ b/cineast-core/src/main/java/org/vitrivr/cineast/core/temporal/TemporalScoring.java @@ -17,39 +17,23 @@ */ public class TemporalScoring { - private final Map segmentMap; - private final List> containerResults; - private final List timeDistances; - private final Float maxLength; - /** - * Constructor to create a temporal scoring instance. + * Score with either the time distance method or sequential depending on time distances provided. * * @param segmentMap map of the segment ids and their respective {@link MediaSegmentDescriptor}s to retrieve the necessary information for temporal scoring * @param containerResults list of lists of the retrieval results where at the index of the outer list we have the result list of the corresponding temporal query in sequence * @param timeDistances time distances between the temporal segments, may be empty * @param maxLength the maximal length of the temporal sequences to be scored */ - public TemporalScoring(Map segmentMap, List> containerResults, List timeDistances, Float maxLength) { - this.segmentMap = segmentMap; - this.containerResults = containerResults; - this.timeDistances = timeDistances; - this.maxLength = maxLength; - } - - /** - * Score the information provided at the constructor with either the time distance method or sequential depending on time distances provided. - */ - public List score() { + public static List score(Map segmentMap, List> containerResults, List timeDistances, Float maxLength) { AbstractTemporalScoringAlgorithm temporalScoringAlgorithm; - float maxLength = this.maxLength; if (maxLength < 0) { maxLength = Float.MAX_VALUE; } - if (this.timeDistances.size() > 0) { - temporalScoringAlgorithm = new TimeDistanceTemporalScoringAlgorithm(this.segmentMap, this.containerResults, maxLength, this.timeDistances); + if (timeDistances.size() > 0) { + temporalScoringAlgorithm = new TimeDistanceTemporalScoringAlgorithm(segmentMap, containerResults, maxLength, timeDistances); } else { - temporalScoringAlgorithm = new SequentialTemporalScoringAlgorithm(this.segmentMap, this.containerResults, maxLength); + temporalScoringAlgorithm = new SequentialTemporalScoringAlgorithm(segmentMap, containerResults, maxLength); } return temporalScoringAlgorithm.score(); } diff --git a/cineast-runtime/src/main/java/org/vitrivr/cineast/standalone/config/ConstrainedQueryConfig.java b/cineast-runtime/src/main/java/org/vitrivr/cineast/standalone/config/ConstrainedQueryConfig.java index dca8c2f2f..58c7ceb45 100644 --- a/cineast-runtime/src/main/java/org/vitrivr/cineast/standalone/config/ConstrainedQueryConfig.java +++ b/cineast-runtime/src/main/java/org/vitrivr/cineast/standalone/config/ConstrainedQueryConfig.java @@ -32,5 +32,15 @@ public Optional getMaxResults() { return Optional.of(Config.sharedConfig().getRetriever().getMaxResults()); } + public static ConstrainedQueryConfig getApplyingConfig(QueryConfig config) { + ConstrainedQueryConfig queryConfig = new ConstrainedQueryConfig(config); + if (config == null) { + final int max = Math.min(queryConfig.getMaxResults().orElse(Config.sharedConfig().getRetriever().getMaxResults()), Config.sharedConfig().getRetriever().getMaxResults()); + queryConfig.setMaxResults(max); + final int resultsPerModule = Math.min(queryConfig.getRawResultsPerModule() == -1 ? Config.sharedConfig().getRetriever().getMaxResultsPerModule() : queryConfig.getResultsPerModule(), Config.sharedConfig().getRetriever().getMaxResultsPerModule()); + queryConfig.setResultsPerModule(resultsPerModule); + } + return queryConfig; + } } diff --git a/docs/openapi.json b/docs/openapi.json index 900a24ff3..a817fa4a6 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -505,6 +505,64 @@ } } }, + "/api/v1/find/segments/similar/staged" : { + "post" : { + "tags" : [ "Segments" ], + "summary" : "Find similar segments based on the given staged query", + "description" : "Performs a similarity search based on the formulated query stages, executing each subsequent stage on the results of the previous stage", + "operationId" : "findSegmentSimilarStaged", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/StagedSimilarityQuery" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/SimilarityQueryResultBatch" + } + } + } + } + } + } + }, + "/api/v1/find/segments/similar/temporal" : { + "post" : { + "tags" : [ "Segments" ], + "summary" : "Find similar segments based on the given temporal query", + "description" : "Performs a similarity search based on the formulated query stages in the given temporal order, scoring final results by their similarity to the specified temporal context", + "operationId" : "findSegmentSimilarTemporal", + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TemporalQuery" + } + } + } + }, + "responses" : { + "200" : { + "description" : "OK", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/TemporalQueryResult" + } + } + } + } + } + } + }, "/api/v1/find/feature/all/by/id/{id}" : { "get" : { "tags" : [ "Metadata" ], @@ -1035,8 +1093,9 @@ "MediaObjectMetadataDescriptor" : { "type" : "object", "properties" : { - "objectId" : { - "type" : "string" + "objectid" : { + "type" : "string", + "writeOnly" : true }, "domain" : { "type" : "string" @@ -1050,6 +1109,9 @@ "exists" : { "type" : "boolean", "writeOnly" : true + }, + "objectId" : { + "type" : "string" } } }, @@ -1142,8 +1204,9 @@ "MediaSegmentMetadataDescriptor" : { "type" : "object", "properties" : { - "segmentId" : { - "type" : "string" + "segmentid" : { + "type" : "string", + "writeOnly" : true }, "domain" : { "type" : "string" @@ -1157,6 +1220,9 @@ "exists" : { "type" : "boolean", "writeOnly" : true + }, + "segmentId" : { + "type" : "string" } } }, @@ -1181,8 +1247,9 @@ "MediaObjectDescriptor" : { "type" : "object", "properties" : { - "objectId" : { - "type" : "string" + "objectid" : { + "type" : "string", + "writeOnly" : true }, "name" : { "type" : "string" @@ -1198,6 +1265,9 @@ "type" : "boolean", "writeOnly" : true }, + "objectId" : { + "type" : "string" + }, "contentURL" : { "type" : "string" } @@ -1277,21 +1347,6 @@ "CorrespondenceFunction" : { "type" : "object" }, - "QueryComponent" : { - "type" : "object", - "properties" : { - "terms" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryTerm" - } - }, - "containerId" : { - "type" : "integer", - "format" : "int32" - } - } - }, "QueryConfig" : { "type" : "object", "properties" : { @@ -1337,19 +1392,19 @@ "type" : "string" } }, - "distanceIfEmpty" : { + "correspondenceFunctionIfEmpty" : { "$ref" : "#/components/schemas/QueryConfig" }, - "correspondenceFunctionIfEmpty" : { + "distanceIfEmpty" : { "$ref" : "#/components/schemas/QueryConfig" }, "correspondenceFunction" : { "$ref" : "#/components/schemas/CorrespondenceFunction" }, - "normIfEmpty" : { + "distanceWeightsIfEmpty" : { "$ref" : "#/components/schemas/QueryConfig" }, - "distanceWeightsIfEmpty" : { + "normIfEmpty" : { "$ref" : "#/components/schemas/QueryConfig" }, "rawResultsPerModule" : { @@ -1378,24 +1433,18 @@ } }, "SimilarityQuery" : { + "required" : [ "terms" ], "type" : "object", "properties" : { - "containers" : { + "terms" : { "type" : "array", - "writeOnly" : true, "items" : { - "$ref" : "#/components/schemas/QueryComponent" + "$ref" : "#/components/schemas/QueryTerm" } }, "config" : { "$ref" : "#/components/schemas/QueryConfig" }, - "components" : { - "type" : "array", - "items" : { - "$ref" : "#/components/schemas/QueryComponent" - } - }, "messageType" : { "type" : "string", "enum" : [ "PING", "Q_SIM", "Q_MLT", "Q_NESEG", "Q_SEG", "M_LOOKUP", "Q_TEMPORAL", "SESSION_START", "QR_START", "QR_END", "QR_ERROR", "QR_OBJECT", "QR_METADATA_O", "QR_METADATA_S", "QR_SEGMENT", "QR_SIMILARITY", "QR_TEMPORAL" ] @@ -1456,6 +1505,120 @@ } } }, + "QueryStage" : { + "required" : [ "terms" ], + "type" : "object", + "properties" : { + "terms" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryTerm" + } + } + } + }, + "StagedSimilarityQuery" : { + "required" : [ "stages" ], + "type" : "object", + "properties" : { + "stages" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/QueryStage" + } + }, + "config" : { + "$ref" : "#/components/schemas/QueryConfig" + } + } + }, + "MetadataAccessSpecification" : { + "type" : "object", + "properties" : { + "type" : { + "type" : "string", + "enum" : [ "OBJECT", "SEGMENT" ] + }, + "domain" : { + "type" : "string" + }, + "key" : { + "type" : "string" + } + } + }, + "TemporalQuery" : { + "required" : [ "queries" ], + "type" : "object", + "properties" : { + "queries" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/StagedSimilarityQuery" + } + }, + "config" : { + "$ref" : "#/components/schemas/QueryConfig" + }, + "timeDistances" : { + "type" : "array", + "items" : { + "type" : "number", + "format" : "float" + } + }, + "maxLength" : { + "type" : "number", + "format" : "float" + }, + "metadataAccessSpec" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/MetadataAccessSpecification" + } + }, + "messageType" : { + "type" : "string", + "enum" : [ "PING", "Q_SIM", "Q_MLT", "Q_NESEG", "Q_SEG", "M_LOOKUP", "Q_TEMPORAL", "SESSION_START", "QR_START", "QR_END", "QR_ERROR", "QR_OBJECT", "QR_METADATA_O", "QR_METADATA_S", "QR_SEGMENT", "QR_SIMILARITY", "QR_TEMPORAL" ] + } + } + }, + "TemporalObject" : { + "type" : "object", + "properties" : { + "segments" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "objectId" : { + "type" : "string" + }, + "score" : { + "type" : "number", + "format" : "double" + } + } + }, + "TemporalQueryResult" : { + "type" : "object", + "properties" : { + "content" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/TemporalObject" + } + }, + "queryId" : { + "type" : "string" + }, + "messageType" : { + "type" : "string", + "enum" : [ "PING", "Q_SIM", "Q_MLT", "Q_NESEG", "Q_SEG", "M_LOOKUP", "Q_TEMPORAL", "SESSION_START", "QR_START", "QR_END", "QR_ERROR", "QR_OBJECT", "QR_METADATA_O", "QR_METADATA_S", "QR_SEGMENT", "QR_SIMILARITY", "QR_TEMPORAL" ] + } + } + }, "FeaturesAllCategoriesQueryResult" : { "type" : "object", "properties" : { @@ -1542,9 +1705,6 @@ "Tag" : { "type" : "object", "properties" : { - "description" : { - "type" : "string" - }, "name" : { "type" : "string" }, @@ -1554,6 +1714,9 @@ }, "id" : { "type" : "string" + }, + "description" : { + "type" : "string" } } }, diff --git a/gradle.properties b/gradle.properties index 4876c0eeb..be686a949 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,7 +15,7 @@ version_guava=30.1.1-jre version_kotlin_stdlib=1.4.32 version_log4j2=2.17.0 version_jackson=2.12.2 -version_javalin=4.1.1 +version_javalin=4.3.0 version_javaewah=1.1.7 version_jbcrypt=0.4.3 version_javacpp=1.5.5