Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for the format parameter #1299

Draft
wants to merge 10 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.egm.stellio.search.entity.model.updateResultFromDetailedResult
import com.egm.stellio.search.temporal.model.AttributeInstance.TemporalProperty
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.search.temporal.util.WHOLE_TIME_RANGE_DURATION
import com.egm.stellio.search.temporal.util.composeAggregationSelectClause
import com.egm.stellio.shared.model.APIException
Expand Down Expand Up @@ -135,7 +136,7 @@ class ScopeService(

if (temporalEntitiesQuery.isAggregatedWithDefinedDuration())
sqlQueryBuilder.append(" GROUP BY entity_id, start")
else if (temporalEntitiesQuery.withAggregatedValues)
else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES)
sqlQueryBuilder.append(" GROUP BY entity_id")
if (temporalQuery.hasLastN())
// in order to get first or last instances, need to order by time
Expand All @@ -160,7 +161,7 @@ class ScopeService(
temporalEntitiesQuery: TemporalEntitiesQuery,
origin: ZonedDateTime?
): String = when {
temporalEntitiesQuery.withAggregatedValues -> {
temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES -> {
val temporalQuery = temporalEntitiesQuery.temporalQuery
val aggrPeriodDuration = temporalQuery.aggrPeriodDuration
val allAggregates = temporalQuery.aggrMethods?.composeAggregationSelectClause(AttributeValueType.ARRAY)
Expand Down Expand Up @@ -214,7 +215,7 @@ class ScopeService(
row: Map<String, Any>,
temporalEntitiesQuery: TemporalEntitiesQuery
): ScopeInstanceResult =
if (temporalEntitiesQuery.withAggregatedValues) {
if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val startDateTime = toZonedDateTime(row["start"])
val endDateTime =
if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration())
Expand All @@ -230,7 +231,7 @@ class ScopeService(
entityId = toUri(row["entity_id"]),
values = values
)
} else if (temporalEntitiesQuery.withTemporalValues) {
} else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES) {
SimplifiedScopeInstanceResult(
entityId = toUri(row["entity_id"]),
scopes = toList(row["value"]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.egm.stellio.search.scope
import com.egm.stellio.search.entity.model.Entity
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_LIST
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_TYPE
import com.egm.stellio.shared.util.JsonLdUtils.JSONLD_VALUE
Expand All @@ -27,12 +28,12 @@ object TemporalScopeBuilder {
// if no history but entity has a scope, add an empty scope list (no history in the given time range)
else if (scopeInstances.isEmpty())
mapOf(NGSILD_SCOPE_PROPERTY to emptyList<String>())
else if (temporalEntitiesQuery.withAggregatedValues)
else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES)
buildScopeAggregatedRepresentation(
scopeInstances,
temporalEntitiesQuery.temporalQuery.aggrMethods!!
)
else if (temporalEntitiesQuery.withTemporalValues)
else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES)
buildScopeSimplifiedRepresentation(scopeInstances)
else
buildScopeFullRepresentation(scopeInstances)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ package com.egm.stellio.search.temporal.model
import com.egm.stellio.search.entity.model.EntitiesQuery
import com.egm.stellio.search.entity.model.EntitiesQueryFromGet
import com.egm.stellio.search.entity.model.EntitiesQueryFromPost
import com.egm.stellio.search.temporal.util.TemporalRepresentation
import java.time.Duration
import java.time.Period
import java.time.temporal.TemporalAmount

sealed class TemporalEntitiesQuery(
open val entitiesQuery: EntitiesQuery,
open val temporalQuery: TemporalQuery,
open val withTemporalValues: Boolean,
open val withAudit: Boolean,
open val withAggregatedValues: Boolean
open val temporalRepresentation: TemporalRepresentation,
open val withAudit: Boolean
) {
fun isAggregatedWithDefinedDuration(): Boolean =
withAggregatedValues &&
temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES &&
temporalQuery.aggrPeriodDuration != null &&
temporalQuery.aggrPeriodDuration != "PT0S"

Expand All @@ -36,15 +36,13 @@ sealed class TemporalEntitiesQuery(
data class TemporalEntitiesQueryFromGet(
override val entitiesQuery: EntitiesQueryFromGet,
override val temporalQuery: TemporalQuery,
override val withTemporalValues: Boolean,
override val withAudit: Boolean,
override val withAggregatedValues: Boolean
) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, withTemporalValues, withAudit, withAggregatedValues)
override val temporalRepresentation: TemporalRepresentation,
override val withAudit: Boolean
) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, temporalRepresentation, withAudit)

data class TemporalEntitiesQueryFromPost(
override val entitiesQuery: EntitiesQueryFromPost,
override val temporalQuery: TemporalQuery,
override val withTemporalValues: Boolean,
override val withAudit: Boolean,
override val withAggregatedValues: Boolean
) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, withTemporalValues, withAudit, withAggregatedValues)
override val temporalRepresentation: TemporalRepresentation,
override val withAudit: Boolean
) : TemporalEntitiesQuery(entitiesQuery, temporalQuery, temporalRepresentation, withAudit)
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.egm.stellio.search.temporal.model.SimplifiedAttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.model.TemporalQuery.Timerel
import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.search.temporal.util.WHOLE_TIME_RANGE_DURATION
import com.egm.stellio.search.temporal.util.composeAggregationSelectClause
import com.egm.stellio.shared.model.APIException
Expand Down Expand Up @@ -154,7 +155,7 @@ class AttributeInstanceService(

sqlQueryBuilder.append(composeSearchSelectStatement(temporalQuery, attributes, origin))

if (!temporalEntitiesQuery.withTemporalValues && !temporalEntitiesQuery.withAggregatedValues)
if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.NONE)
sqlQueryBuilder.append(", payload")

if (temporalQuery.timeproperty == OBSERVED_AT)
Expand Down Expand Up @@ -184,7 +185,7 @@ class AttributeInstanceService(

if (temporalEntitiesQuery.isAggregatedWithDefinedDuration())
sqlQueryBuilder.append(" GROUP BY temporal_entity_attribute, start")
else if (temporalEntitiesQuery.withAggregatedValues)
else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES)
sqlQueryBuilder.append(" GROUP BY temporal_entity_attribute")

if (temporalQuery.hasLastN())
Expand Down Expand Up @@ -297,7 +298,7 @@ class AttributeInstanceService(
row: Map<String, Any>,
temporalEntitiesQuery: TemporalEntitiesQuery
): AttributeInstanceResult =
if (temporalEntitiesQuery.withAggregatedValues) {
if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val startDateTime = toZonedDateTime(row["start"])
val endDateTime =
if (!temporalEntitiesQuery.isAggregatedWithDefinedDuration())
Expand All @@ -313,7 +314,7 @@ class AttributeInstanceService(
attributeUuid = toUuid(row["temporal_entity_attribute"]),
values = values
)
} else if (temporalEntitiesQuery.withTemporalValues)
} else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES)
SimplifiedAttributeInstanceResult(
attributeUuid = toUuid(row["temporal_entity_attribute"]),
// the type of the value of a property may have changed in the history (e.g., from number to string)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.egm.stellio.search.temporal.model.AttributeInstanceResult
import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.model.TemporalQuery
import com.egm.stellio.search.temporal.util.AttributesWithInstances
import com.egm.stellio.search.temporal.util.TemporalRepresentation
import java.time.ZonedDateTime

typealias Range = Pair<ZonedDateTime, ZonedDateTime>
Expand Down Expand Up @@ -47,12 +48,13 @@ object TemporalPaginationService {
val temporalQuery = query.temporalQuery

val attributesTimeRanges = attributeInstancesWhoReachedLimit.map {
it.first().getComparableTime() to if (query.withAggregatedValues) {
val lastInstance = it.last() as AggregatedAttributeInstanceResult
lastInstance.values.first().endDateTime
} else {
it.last().getComparableTime()
}
it.first().getComparableTime() to
if (query.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val lastInstance = it.last() as AggregatedAttributeInstanceResult
lastInstance.values.first().endDateTime
} else {
it.last().getComparableTime()
}
}

if (temporalQuery.hasLastN()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.egm.stellio.search.temporal.model.TemporalEntitiesQuery
import com.egm.stellio.search.temporal.service.TemporalPaginationService.getPaginatedAttributeWithInstancesAndRange
import com.egm.stellio.search.temporal.util.AttributesWithInstances
import com.egm.stellio.search.temporal.util.TemporalEntityBuilder
import com.egm.stellio.search.temporal.util.TemporalRepresentation
import com.egm.stellio.search.temporal.web.Range
import com.egm.stellio.shared.model.APIException
import com.egm.stellio.shared.model.ExpandedEntity
Expand Down Expand Up @@ -94,7 +95,7 @@ class TemporalQueryService(
// - timeAt if it is provided
// - the oldest value if not (timeAt is optional if querying a temporal entity by id)

if (!temporalEntitiesQuery.withAggregatedValues)
if (!(temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES))
return null
else if (temporalQuery.timeAt != null)
return temporalQuery.timeAt
Expand Down Expand Up @@ -197,7 +198,7 @@ class TemporalQueryService(
// when retrieved from DB, values of geo-properties are encoded in WKT and won't be automatically
// transformed during compaction as it is not done for temporal values, so it is done now
if (it.key == Attribute.AttributeValueType.GEOMETRY &&
temporalEntitiesQuery.withTemporalValues
temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES
) {
it.value.map { attributeInstanceResult ->
attributeInstanceResult as SimplifiedAttributeInstanceResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ object TemporalEntityBuilder {
attributeAndResultsMap: AttributesWithInstances,
temporalEntitiesQuery: TemporalEntitiesQuery,
): Map<String, Any> =
if (temporalEntitiesQuery.withTemporalValues) {
if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.TEMPORAL_VALUES) {
val attributes = buildAttributesSimplifiedRepresentation(attributeAndResultsMap)
mergeSimplifiedTemporalAttributesOnAttributeName(attributes)
} else if (temporalEntitiesQuery.withAggregatedValues) {
} else if (temporalEntitiesQuery.temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES) {
val attributes = buildAttributesAggregatedRepresentation(
attributeAndResultsMap,
temporalEntitiesQuery.temporalQuery.aggrMethods!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,39 +43,22 @@ fun composeTemporalEntitiesQueryFromGet(
requestParams,
contexts
).bind()
var withTemporalValues = false
var withAggregatedValues = false
if (inQueryEntities) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did you add enclosing { for this one-line expression?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detekt automatic fix

entitiesQueryFromGet.validateMinimalQueryEntitiesParameters().bind()
}
val optionsParam = Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key))
val formatParam = requestParams.getFirst(QueryParameter.FORMAT.key)
when (formatParam) {
FormatValue.TEMPORAL_VALUES.value -> withTemporalValues = true
FormatValue.AGGREGATED_VALUES.value -> withAggregatedValues = true
else -> {
val hasTemporal = hasValueInQueryParam(optionsParam, QueryParamValue.TEMPORAL_VALUES)
val hasAggregated = hasValueInQueryParam(optionsParam, QueryParamValue.AGGREGATED_VALUES)
if (hasTemporal && hasAggregated) {
return BadRequestDataException("Only one temporal representation can be present").left()
}
withTemporalValues = hasTemporal
withAggregatedValues = hasAggregated
}
}
val temporalRepresentation = extractTemporalRepresentation(requestParams).bind()
val withAudit = hasValueInQueryParam(
Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
QueryParamValue.AUDIT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should use OptionsValue instead

)
val temporalQuery =
buildTemporalQuery(requestParams, defaultPagination, inQueryEntities, withAggregatedValues).bind()
buildTemporalQuery(requestParams, defaultPagination, inQueryEntities, temporalRepresentation).bind()

TemporalEntitiesQueryFromGet(
entitiesQuery = entitiesQueryFromGet,
temporalQuery = temporalQuery,
withTemporalValues = withTemporalValues,
withAudit = withAudit,
withAggregatedValues = withAggregatedValues
temporalRepresentation = temporalRepresentation,
withAudit = withAudit
)
}

Expand All @@ -91,20 +74,12 @@ fun composeTemporalEntitiesQueryFromPost(
requestParams,
contexts
).bind()
val temporalRepresentation = extractTemporalRepresentation(requestParams).bind()

val withTemporalValues = hasValueInQueryParam(
Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
QueryParamValue.TEMPORAL_VALUES
)
val withAudit = hasValueInQueryParam(
Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
QueryParamValue.AUDIT
)
val withAggregatedValues = hasValueInQueryParam(
Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key)),
QueryParamValue.AGGREGATED_VALUES
)

val temporalParams = mapOf(
QueryParameter.TIMEREL.key to listOf(query.temporalQ?.timerel),
QueryParameter.TIMEAT.key to listOf(query.temporalQ?.timeAt),
Expand All @@ -118,15 +93,14 @@ fun composeTemporalEntitiesQueryFromPost(
MultiValueMapAdapter(temporalParams),
defaultPagination,
true,
withAggregatedValues
temporalRepresentation
).bind()

TemporalEntitiesQueryFromPost(
entitiesQuery = entitiesQueryFromPost,
temporalQuery = temporalQuery,
withTemporalValues = withTemporalValues,
withAudit = withAudit,
withAggregatedValues = withAggregatedValues
temporalRepresentation = temporalRepresentation,
withAudit = withAudit
)
}

Expand All @@ -135,11 +109,12 @@ fun buildTemporalQuery(
params: MultiValueMap<String, String>,
pagination: ApplicationProperties.Pagination,
inQueryEntities: Boolean = false,
withAggregatedValues: Boolean = false,
temporalRepresentation: TemporalRepresentation,
): Either<APIException, TemporalQuery> {
val timerelParam = params.getFirst(QueryParameter.TIMEREL.key)
val timeAtParam = params.getFirst(QueryParameter.TIMEAT.key)
val endTimeAtParam = params.getFirst(QueryParameter.ENDTIMEAT.key)
val withAggregatedValues = temporalRepresentation == TemporalRepresentation.AGGREGATED_VALUES
val aggrPeriodDurationParam =
if (withAggregatedValues)
params.getFirst(QueryParameter.AGGRPERIODDURATION.key) ?: WHOLE_TIME_RANGE_DURATION
Expand Down Expand Up @@ -215,3 +190,30 @@ fun buildTimerelAndTime(
} else {
"'timerel' and 'time' must be used in conjunction".left()
}

fun extractTemporalRepresentation(requestParams: MultiValueMap<String, String>):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it can be a private function?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now call it queryParams, and no longer requestParams

Either<APIException, TemporalRepresentation> = either {
ranim-n marked this conversation as resolved.
Show resolved Hide resolved
val optionsParam = Optional.ofNullable(requestParams.getFirst(QueryParameter.OPTIONS.key))
val formatParam = requestParams.getFirst(QueryParameter.FORMAT.key)
return when (formatParam) {
FormatValue.TEMPORAL_VALUES.value -> TemporalRepresentation.TEMPORAL_VALUES.right()
FormatValue.AGGREGATED_VALUES.value -> TemporalRepresentation.AGGREGATED_VALUES.right()
else -> {
val hasTemporal = hasValueInQueryParam(optionsParam, QueryParamValue.TEMPORAL_VALUES)
val hasAggregated = hasValueInQueryParam(optionsParam, QueryParamValue.AGGREGATED_VALUES)
when {
hasTemporal && hasAggregated ->
return BadRequestDataException("Only one temporal representation can be present").left()
hasTemporal -> TemporalRepresentation.TEMPORAL_VALUES.right()
hasAggregated -> TemporalRepresentation.AGGREGATED_VALUES.right()
else -> TemporalRepresentation.NONE.right()
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be simplified. By adding a fromString function to the TemporalRepresentation enum.


enum class TemporalRepresentation {
TEMPORAL_VALUES,
AGGREGATED_VALUES,
NONE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NORMALIZED is more accurate (if it's none, you have nothing)

}
Loading
Loading