-
Notifications
You must be signed in to change notification settings - Fork 689
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: EXPOSED-359 Add support for multidimensional arrays #2250
base: main
Are you sure you want to change the base?
Conversation
358a5d3
to
dc91966
Compare
dc91966
to
f0673d0
Compare
I called new column type |
…ltidimentional arrays
f2b53c4
to
ef054cb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left comments about documentation and examples with breaking changes. Please also update title with feat!:
.
It'd probably also be best to get other opinions about the naming of these new columns.
Current proposal is:
// existing 1D type
val col1 = array<Int>("col1")
// new types
val col2 = array2<Int>("col2") // 2D
val col3 = array3<Int>("col3") // 3D
val col4 = arrayN<Int, List<List<List<List<Int>>>>>("col4", 4) // 4D
To me, the hanging numbers at the end look incomplete. Also most other ORMs have a single array()
that can take 1+ dimensions, so I'm proposing something like:
// overload existing type to default to 1, unless arg passed to 'dimensions'
val col1 = array<Int>("col1")
val col4 = array<Int, List<List<List<List<Int>>>>>("col4", 4)
val col2 = array2d<Int>("col2")
val col3 = array3d<Int>("col3")
Other options could maybe be array2D()
, array2Multi()
, array2Dim()
.
@e5l @joc-a Could you please let us know if you have suggestions/thoughts?
* `ArrayColumnType` supports multidimensional arrays now and has one more generic parameter. | ||
If the was used directly for one dimensional arrays with parameter `T` like `ArrayColumnType<T>`, now it should |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small type in second line.
object Teams : Table("teams") { | ||
val memberIds = array2<UUID>("member_ids") | ||
val memberNames = array3<String>("member_names") | ||
val budgets = array2<Double>("budgets") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please review these blocks through the Writerside plugin, as there are many errors and column types are not appearing. For example, 'code-block's do not accept nested <
or >
, since they will be confused for html tags. You need to use <
, >
, and "
or wrap the entire code in <![CDATA[]]>
. Please see previous blocks in file.
Also please fix indentation.
@@ -325,6 +325,40 @@ | |||
</code-block> | |||
</chapter> | |||
</chapter> | |||
<chapter title="How to use Multi-Dimensional Array types" id="how-to-use-multi-dimensional-array-types"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think this would be better placed directly as a sub-chapter under 'How to use Array Types', before sub-chapter on functions. That way you could then add an example of using a function with nD arrays in the function section after.
inline fun <reified T : Any, R : List<Any>> arrayNLiteral(value: R, delegateType: ColumnType<T>? = null, dimensions: Int): LiteralOp<R> { | ||
@OptIn(InternalApi::class) | ||
return LiteralOp(ArrayColumnType(delegateType ?: resolveColumnType(T::class)), value) | ||
return LiteralOp(ArrayColumnType(delegateType ?: resolveColumnType(T::class), dimensions), value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo it looks cleaner if both of them are called arrayLiteral()
(same for arrayParam()
. Allows easy switch if user wants to for some reason use multi- instead and didn't cause any issues with the overload.
Could you also consider moving dimensions
to before the default delegateType
, so it doesn't always have to be named when invoked without delegate?
) : ColumnType<List<E>>() { | ||
class ArrayColumnType<T, R : List<Any?>>( | ||
val delegate: ColumnType<T & Any>, | ||
val dimensions: Int, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason that dimensions
can't be given a default value of 1?
If there is, then at minimum this needs to be included in the breaking changes doc.
Anybody that uses ArrayColumnType
in custom function will now need to add a value for dimensions
, for example:
class ArrayAggFunction<T>(
expression: ExpressionWithColumnType<T>,
delegate: ColumnType<T & Any>
) : CustomFunction<List<T>>(
functionName = "ARRAY_AGG",
columnType = ArrayColumnType(delegate), // won't compile anymore unless dimensions value passed
expr = arrayOf(expression)
)
* **Note:** The maximum cardinality is considered for each dimension, but it is ignored by the PostgreSQL database. | ||
* Validation is performed on the client side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused by changes to maximumCardinality
. I guess I see why the type had to change, even though it isn't used by PostgreSQL and PostgreSQL is the only db that supports multiple-dimensions (but I can see that may change).
From the line above, it sounds like Exposed is going to do some validation. Did you mean to maybe override validateValueBeforeUpdate()
here?
If not, then maybe the line should say: "Any validation should be implemented on the client side."
class ArrayColumnType<T, R : List<Any?>>( | ||
val delegate: ColumnType<T & Any>, | ||
val dimensions: Int, | ||
val maximumCardinality: List<Int>? = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If type must change, this is another breaking change that should be mentioned in docs.
If ArrayColumnType
instance was being used with original value, it will now be mistaken as dimensions
or cause type does not conform
error:
class ArrayAggFunction<T>(
expression: ExpressionWithColumnType<T>,
delegate: ColumnType<T & Any>
) : CustomFunction<List<T>>(
functionName = "ARRAY_AGG",
columnType = ArrayColumnType(delegate, 5), // will compile but argument will be now wrongly for dimensions
expr = arrayOf(expression)
)
class ArrayAggFunction<T>(
expression: ExpressionWithColumnType<T>,
delegate: ColumnType<T & Any>
) : CustomFunction<List<T>>(
functionName = "ARRAY_AGG",
columnType = ArrayColumnType(delegate, maximumCardinality = 5), // won't compile
expr = arrayOf(expression)
)
* **Note:** Providing an array size limit when using the PostgreSQL dialect is allowed, but this value will be ignored by the database. | ||
* The whole validation is performed on the client side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment about validation as in previous file.
inline fun <reified T : Any> Table.array2(name: String, maximumCardinality: List<Int>? = null): Column<List<List<T>>> = | ||
arrayN<T, List<List<T>>>(name, dimensions = 2, maximumCardinality) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest discussing naming options with the rest of the team too.
To me, there's something odd about a hanging number at the end array2<Int>()
. I'd still vote for array2d()
or maybe even array2Multi()
or array2Dim()
.
* @throws IllegalArgumentException If [dimensions] is less than or equal to 1. | ||
* @throws IllegalStateException If no column type mapping is found. | ||
*/ | ||
inline fun <reified T : Any, R : List<Any>> Table.arrayN(name: String, dimensions: Int, maximumCardinality: List<Int>? = null): Column<R> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as for arrayLiteral()
and arrayParam()
. I think it would be cleaner to have single-named array()
that has multiple overloads with/without dimensions.
Description
Summary of the change: Added support for multi-dimensional array types in Exposed for PostgreSQL database.
Detailed description:
multi2Array
,multi3Array
, andmultiArray
functions for defining multi-dimensional array columns. It also adds corresponding literal functionsmulti2ArrayLiteral
,multi3ArrayLiteral
, andmultiArrayLiteral
.Type of Change
Please mark the relevant options with an "X":
Updates/remove existing public API methods:
Affected databases:
Related Issues
EXPOSED-359 Add support for multidimensional arrays