@@ -28,7 +28,9 @@ import kotlinx.datetime.minus
2828
2929import org.eclipse.apoapsis.ortserver.dao.ConditionBuilder
3030import org.eclipse.apoapsis.ortserver.dao.QueryParametersException
31+ import org.eclipse.apoapsis.ortserver.model.CompoundHierarchyId
3132import org.eclipse.apoapsis.ortserver.model.util.ComparisonOperator
33+ import org.eclipse.apoapsis.ortserver.model.util.HierarchyFilter
3234import org.eclipse.apoapsis.ortserver.model.util.ListQueryParameters
3335import org.eclipse.apoapsis.ortserver.model.util.ListQueryResult
3436import org.eclipse.apoapsis.ortserver.model.util.OrderDirection
@@ -46,6 +48,7 @@ import org.jetbrains.exposed.sql.QueryParameter
4648import org.jetbrains.exposed.sql.ResultRow
4749import org.jetbrains.exposed.sql.SizedIterable
4850import org.jetbrains.exposed.sql.SortOrder
51+ import org.jetbrains.exposed.sql.SqlExpressionBuilder
4952import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
5053import org.jetbrains.exposed.sql.SqlExpressionBuilder.greater
5154import org.jetbrains.exposed.sql.SqlExpressionBuilder.greaterEq
@@ -55,6 +58,9 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq
5558import org.jetbrains.exposed.sql.SqlExpressionBuilder.neq
5659import org.jetbrains.exposed.sql.SqlExpressionBuilder.notInList
5760import org.jetbrains.exposed.sql.TextColumnType
61+ import org.jetbrains.exposed.sql.and
62+ import org.jetbrains.exposed.sql.not
63+ import org.jetbrains.exposed.sql.or
5864
5965/* *
6066 * Transform the given column to an [EntityID] when creating a DAO object. This can be used for foreign key columns to
@@ -80,6 +86,61 @@ fun <T : Instant?> Column<T>.transformToDatabasePrecision() =
8086 */
8187fun Instant.toDatabasePrecision () = minus(nanosecondsOfSecond, DateTimeUnit .NANOSECOND )
8288
89+ /* *
90+ * Extract the defined IDs on the specified [level] from the [CompoundHierarchyId]s in this collection as long values.
91+ */
92+ fun Collection<CompoundHierarchyId>.extractIds (level : Int ): List <Long > = mapNotNull { it[level]?.value }
93+
94+ /* *
95+ * Definition of a function type for generating query conditions based on accessible hierarchy elements. The function
96+ * has access to a [SqlExpressionBuilder] to create the conditions. It is passed the level in the hierarchy to filter
97+ * by, a list with the IDs to be included together with their child elements, and the filter itself to gain access to
98+ * additional properties. The function returns an [Op] representing the condition. The conditions for the different
99+ * hierarchy levels are then combined using an `OR` operator.
100+ */
101+ typealias HierarchyConditionGenerator = SqlExpressionBuilder .(
102+ Int ,
103+ List <CompoundHierarchyId >,
104+ HierarchyFilter
105+ ) -> Op <Boolean >
106+
107+ /* *
108+ * Generate a condition for this [HierarchyFilter] using the provided [generator] function. The [generator] is
109+ * responsible for creating the conditions on each hierarchy level. This function combines these conditions using an
110+ * `OR` operator. The result is then combined with the optional [otherCondition] using an `AND` operator.
111+ */
112+ fun HierarchyFilter.apply (
113+ otherCondition : Op <Boolean > = Op .TRUE ,
114+ generator : HierarchyConditionGenerator
115+ ): ConditionBuilder = {
116+ if (isWildcard) {
117+ otherCondition
118+ } else {
119+ val hierarchyCondition = transitiveIncludes.entries.fold(Op .FALSE as Op <Boolean >) { op, (level, ids) ->
120+ val condition = generator(this , level, ids, this @apply)
121+ op or condition
122+ }
123+
124+ otherCondition and hierarchyCondition
125+ }
126+ }
127+
128+ /* *
129+ * Generate a conditional exclusion condition for the given [filter] for the specified [column] at the given hierarchy
130+ * [level]. The function obtains the IDs to be excluded at the given [level] and constructs a `NOT IN` condition if
131+ * this list is not empty.
132+ */
133+ fun HierarchyFilter.excludesCondition (column : Column <EntityID <Long >>, level : Int ): Op <Boolean >? =
134+ excludes[level]?.takeUnless { it.isEmpty() }?.let { excludes ->
135+ not (column inList excludes.extractIds(level))
136+ }
137+
138+ /* *
139+ * Construct a logic `AND` condition with an optional [op]. If [op] is null, return this condition.
140+ */
141+ infix fun Op<Boolean>.andCond (op : Expression <Boolean >? ): Op <Boolean > =
142+ op?.let { this and it } ? : this
143+
83144/* *
84145 * Run the provided [query] with the given [parameters] to create a [ListQueryResult]. The entities are mapped to the
85146 * corresponding model objects using the provided [entityMapper].
0 commit comments