HHH-17375 Shorthand bracket syntax for array slicing

This commit is contained in:
Christian Beikov 2024-05-07 08:15:25 +02:00
parent 67d04577be
commit b74992198c
4 changed files with 75 additions and 10 deletions

View File

@ -1458,14 +1458,26 @@ include::{array-example-dir-hql}/ArrayRemoveIndexTest.java[tags=hql-array-remove
[[hql-array-slice-functions]]
===== `array_slice()`
Returns the sub-array as specified by the given start and end index. Returns `null` if any of the arguments is `null`
Returns the sub-array as specified by the given 1-based inclusive start and end index. Returns `null` if any of the arguments is `null`
and also if the index is out of bounds.
[[hql-array-remove-index-example]]
[[hql-array-slice-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArrayRemoveIndexTest.java[tags=hql-array-remove-index-example]
include::{array-example-dir-hql}/ArraySliceTest.java[tags=hql-array-slice-example]
----
====
Alternatively, it's also possible to slice an array by specifying the lower and upper bound,
separated by a colon, as index in the bracket array index syntax `array[lowerIndex:upperIndex]`.
This is syntax sugar that translates to the `array_slice` function.
[[hql-array-slice-hql-example]]
====
[source, JAVA, indent=0]
----
include::{array-example-dir-hql}/ArraySliceTest.java[tags=hql-array-slice-hql-example]
----
====

View File

@ -438,9 +438,11 @@ syntacticDomainPath
| collectionValueNavigablePath
| mapKeyNavigablePath
| simplePath indexedPathAccessFragment
| simplePath slicedPathAccessFragment
| toOneFkReference
| function pathContinuation
| function indexedPathAccessFragment pathContinuation?
| function slicedPathAccessFragment
;
/**
@ -463,6 +465,13 @@ indexedPathAccessFragment
: LEFT_BRACKET expression RIGHT_BRACKET (DOT generalPathFragment)?
;
/**
* The slice operator to obtain elements between the lower and upper bound.
*/
slicedPathAccessFragment
: LEFT_BRACKET expression COLON expression RIGHT_BRACKET
;
/**
* A 'treat()' function that "breaks" a path expression
*/

View File

@ -5205,17 +5205,44 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return visitToOneFkReference( ctx.toOneFkReference() );
}
else if ( ctx.function() != null ) {
return visitPathContinuation(
visitIndexedPathAccessFragment(
(SemanticPathPart) visitFunction( ctx.function() ),
ctx.indexedPathAccessFragment()
),
ctx.pathContinuation()
);
final HqlParser.SlicedPathAccessFragmentContext slicedFragmentsCtx = ctx.slicedPathAccessFragment();
if ( slicedFragmentsCtx != null ) {
final List<HqlParser.ExpressionContext> slicedFragments = slicedFragmentsCtx.expression();
return getFunctionDescriptor( "array_slice" ).generateSqmExpression(
List.of(
(SqmTypedNode<?>) visitFunction( ctx.function() ),
(SqmTypedNode<?>) slicedFragments.get( 0 ).accept( this ),
(SqmTypedNode<?>) slicedFragments.get( 1 ).accept( this )
),
null,
creationContext.getQueryEngine()
);
}
else {
return visitPathContinuation(
visitIndexedPathAccessFragment(
(SemanticPathPart) visitFunction( ctx.function() ),
ctx.indexedPathAccessFragment()
),
ctx.pathContinuation()
);
}
}
else if ( ctx.simplePath() != null && ctx.indexedPathAccessFragment() != null ) {
return visitIndexedPathAccessFragment( visitSimplePath( ctx.simplePath() ), ctx.indexedPathAccessFragment() );
}
else if ( ctx.simplePath() != null && ctx.slicedPathAccessFragment() != null ) {
final List<HqlParser.ExpressionContext> slicedFragments = ctx.slicedPathAccessFragment().expression();
return getFunctionDescriptor( "array_slice" ).generateSqmExpression(
List.of(
(SqmTypedNode<?>) visitSimplePath( ctx.simplePath() ),
(SqmTypedNode<?>) slicedFragments.get( 0 ).accept( this ),
(SqmTypedNode<?>) slicedFragments.get( 1 ).accept( this )
),
null,
creationContext.getQueryEngine()
);
}
else {
throw new ParsingException( "Illegal domain path '" + ctx.getText() + "'" );
}

View File

@ -139,4 +139,21 @@ public class ArraySliceTest {
} );
}
@Test
public void testSliceSyntax(SessionFactoryScope scope) {
scope.inSession( em -> {
//tag::hql-array-slice-hql-example[]
List<Tuple> results = em.createQuery( "select e.id, e.theArray[1:1] from EntityWithArrays e order by e.id", Tuple.class )
.getResultList();
//end::hql-array-slice-hql-example[]
assertEquals( 3, results.size() );
assertEquals( 1L, results.get( 0 ).get( 0 ) );
assertArrayEquals( new String[0], results.get( 0 ).get( 1, String[].class ) );
assertEquals( 2L, results.get( 1 ).get( 0 ) );
assertArrayEquals( new String[] { "abc" }, results.get( 1 ).get( 1, String[].class ) );
assertEquals( 3L, results.get( 2 ).get( 0 ) );
assertNull( results.get( 2 ).get( 1, String[].class ) );
} );
}
}