Skip to content

Commit e5c26f9

Browse files
committed
HSEARCH-3319 WIP: DRAFT: IDEA: TEST: Type-safe field references
1 parent f3bb0ec commit e5c26f9

35 files changed

+1371
-90
lines changed

documentation/src/test/java/org/hibernate/search/documentation/search/converter/ProjectionConverterIT.java

+71-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import org.hibernate.search.documentation.testsupport.DocumentationSetupHelper;
2424
import org.hibernate.search.engine.backend.types.Projectable;
2525
import org.hibernate.search.engine.search.common.ValueConvert;
26+
import org.hibernate.search.engine.search.reference.TypedFieldReference;
27+
import org.hibernate.search.engine.search.reference.ValueFieldReference;
2628
import org.hibernate.search.mapper.orm.Search;
2729
import org.hibernate.search.mapper.orm.session.SearchSession;
2830
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
@@ -34,7 +36,8 @@
3436

3537
class ProjectionConverterIT {
3638
@RegisterExtension
37-
public DocumentationSetupHelper setupHelper = DocumentationSetupHelper.withSingleBackend( BackendConfigurations.simple() );
39+
public DocumentationSetupHelper setupHelper = DocumentationSetupHelper.withSingleBackend(
40+
BackendConfigurations.simple() );
3841

3942
private EntityManagerFactory entityManagerFactory;
4043

@@ -80,6 +83,73 @@ void projectionConverterDisabled() {
8083
} );
8184
}
8285

86+
@Test
87+
void projectionConverterDisabledFR() {
88+
with( entityManagerFactory ).runInTransaction( entityManager -> {
89+
SearchSession searchSession = Search.session( entityManager );
90+
// "status", String.class, ValueConvert.NO
91+
92+
93+
ValueFieldReference<OrderStatus, String> reference = new ValueFieldReference<>() {
94+
private final String absolutePath = "status";
95+
96+
@Override
97+
public String absolutePath() {
98+
return absolutePath;
99+
}
100+
101+
@Override
102+
public Class<OrderStatus> type() {
103+
return OrderStatus.class;
104+
}
105+
106+
@Override
107+
public TypedFieldReference<String> noConverter() {
108+
return new TypedFieldReference<>() {
109+
@Override
110+
public String absolutePath() {
111+
return absolutePath;
112+
}
113+
114+
@Override
115+
public Class<String> type() {
116+
return String.class;
117+
}
118+
119+
@Override
120+
public ValueConvert valueConvert() {
121+
return ValueConvert.NO;
122+
}
123+
};
124+
}
125+
126+
@Override
127+
public TypedFieldReference<String> asString() {
128+
throw new UnsupportedOperationException( "PARSE is not supported for projections" );
129+
}
130+
};
131+
132+
List<String> result = searchSession.search( Order.class )
133+
.select( f -> f.field( reference.noConverter() ) )
134+
.where( f -> f.matchAll() )
135+
.fetchHits( 20 );
136+
137+
assertThat( result )
138+
.containsExactlyInAnyOrder(
139+
Stream.of( OrderStatus.values() ).map( Enum::name ).toArray( String[]::new )
140+
);
141+
142+
List<OrderStatus> result2 = searchSession.search( Order.class )
143+
.select( f -> f.field( reference ) )
144+
.where( f -> f.matchAll() )
145+
.fetchHits( 20 );
146+
147+
assertThat( result2 )
148+
.containsExactlyInAnyOrder( OrderStatus.values() );
149+
} );
150+
151+
}
152+
83153
private void initData() {
84154
with( entityManagerFactory ).runInTransaction( entityManager -> {
85155
Order order1 = new Order( 1 );

documentation/src/test/java/org/hibernate/search/documentation/search/predicate/PredicateDslIT.java

+35
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.hibernate.search.engine.search.common.RewriteMethod;
2626
import org.hibernate.search.engine.search.predicate.dsl.RegexpQueryFlag;
2727
import org.hibernate.search.engine.search.predicate.dsl.SimpleQueryFlag;
28+
import org.hibernate.search.engine.search.reference.TypedFieldReference;
29+
import org.hibernate.search.engine.search.reference.ValueFieldReference;
2830
import org.hibernate.search.engine.spatial.DistanceUnit;
2931
import org.hibernate.search.engine.spatial.GeoBoundingBox;
3032
import org.hibernate.search.engine.spatial.GeoPoint;
@@ -467,6 +469,39 @@ void match() {
467469
} );
468470
}
469471

472+
@Test
473+
void matchFieldReference() {
474+
withinSearchSession( searchSession -> {
475+
List<Book> hits = searchSession.search( Book.class )
476+
.where( f -> f.match().field( new ValueFieldReference<String, String>() {
477+
@Override
478+
public String absolutePath() {
479+
return "title";
480+
}
481+
482+
@Override
483+
public Class<String> type() {
484+
return String.class;
485+
}
486+
487+
@Override
488+
public TypedFieldReference<String> noConverter() {
489+
return null;
490+
}
491+
492+
@Override
493+
public TypedFieldReference<String> asString() {
494+
return null;
495+
}
496+
} )
497+
.matching( "robot" ) )
498+
.fetchHits( 20 );
499+
assertThat( hits )
500+
.extracting( Book::getId )
501+
.containsExactlyInAnyOrder( BOOK1_ID, BOOK3_ID );
502+
} );
503+
}
504+
470505
@Test
471506
void match_analysis() {
472507
withinSearchSession( searchSession -> {

engine/src/main/java/org/hibernate/search/engine/search/predicate/dsl/CommonQueryStringPredicateFieldMoreStep.java

+45
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
*/
77
package org.hibernate.search.engine.search.predicate.dsl;
88

9+
import org.hibernate.search.engine.search.reference.TypedFieldReference;
10+
import org.hibernate.search.util.common.annotation.Incubating;
11+
912
/**
1013
* The step in a query string predicate definition, where the query string to match can be set
1114
* (see the superinterface {@link CommonQueryStringPredicateMatchingStep}),
@@ -54,4 +57,46 @@ default S field(String fieldPath) {
5457
*/
5558
S fields(String... fieldPaths);
5659

60+
/**
61+
* Target the given field in the query string predicate,
62+
* as an alternative to the already-targeted fields.
63+
* <p>
64+
* Only text fields are supported.
65+
* <p>
66+
* See {@link CommonQueryStringPredicateFieldStep#field(String)} for more information on targeted fields.
67+
*
68+
* @param field The field reference representing a <a href="SearchPredicateFactory.html#field-paths">path</a> to the index field
69+
* to apply the predicate on.
70+
* @return The next step.
71+
*
72+
* @see CommonQueryStringPredicateFieldStep#field(String)
73+
*/
74+
@Incubating
75+
default S field(TypedFieldReference<?> field) {
76+
return fields( field );
77+
}
78+
79+
/**
80+
* Target the given fields in the query string predicate,
81+
* as an alternative to the already-targeted fields.
82+
* <p>
83+
* Only text fields are supported.
84+
* <p>
85+
* See {@link CommonQueryStringPredicateFieldStep#fields(String...)} for more information on targeted fields.
86+
*
87+
* @param fields The field reference representing <a href="SearchPredicateFactory.html#field-paths">paths</a> to the index fields
88+
* to apply the predicate on.
89+
* @return The next step.
90+
*
91+
* @see CommonQueryStringPredicateFieldStep#fields(String...)
92+
*/
93+
@Incubating
94+
default S fields(TypedFieldReference<?>... fields) {
95+
String[] paths = new String[fields.length];
96+
for ( int i = 0; i < fields.length; i++ ) {
97+
paths[i] = fields[i].absolutePath();
98+
}
99+
return fields( paths );
100+
}
101+
57102
}

engine/src/main/java/org/hibernate/search/engine/search/predicate/dsl/CommonQueryStringPredicateFieldStep.java

+49
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
*/
77
package org.hibernate.search.engine.search.predicate.dsl;
88

9+
import org.hibernate.search.engine.search.reference.TypedFieldReference;
10+
import org.hibernate.search.util.common.annotation.Incubating;
11+
912
/**
1013
* The initial step in a query string predicate definition, where the target field can be set.
1114
*
@@ -51,4 +54,50 @@ default N field(String fieldPath) {
5154
*/
5255
N fields(String... fieldPaths);
5356

57+
58+
/**
59+
* Target the given field in the query string predicate.
60+
* <p>
61+
* Only text fields are supported.
62+
* <p>
63+
* Multiple fields may be targeted by the same predicate:
64+
* the predicate will match if <em>any</em> targeted field matches.
65+
* <p>
66+
* When targeting multiple fields, those fields must have compatible types.
67+
* Please refer to the reference documentation for more information.
68+
*
69+
* @param field The field reference representing a <a href="SearchPredicateFactory.html#field-paths">path</a> to the index field
70+
* to apply the predicate on.
71+
* @return The next step.
72+
*/
73+
@Incubating
74+
default N field(TypedFieldReference<?> field) {
75+
return fields( field );
76+
}
77+
78+
/**
79+
* Target the given fields in the query string predicate.
80+
* <p>
81+
* Only text fields are supported.
82+
* <p>
83+
* Equivalent to {@link #field(String)} followed by multiple calls to
84+
* {@link #field(String)},
85+
* the only difference being that calls to {@link CommonQueryStringPredicateFieldMoreStep#boost(float)}
86+
* and other field-specific settings on the returned step will only need to be done once
87+
* and will apply to all the fields passed to this method.
88+
*
89+
* @param fields The field reference representing <a href="SearchPredicateFactory.html#field-paths">paths</a> to the index fields
90+
* to apply the predicate on.
91+
* @return The next step.
92+
*
93+
* @see #field(String)
94+
*/
95+
@Incubating
96+
default N fields(TypedFieldReference<?>... fields) {
97+
String[] paths = new String[fields.length];
98+
for ( int i = 0; i < fields.length; i++ ) {
99+
paths[i] = fields[i].absolutePath();
100+
}
101+
return fields( paths );
102+
}
54103
}

engine/src/main/java/org/hibernate/search/engine/search/predicate/dsl/ExistsPredicateFieldStep.java

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
package org.hibernate.search.engine.search.predicate.dsl;
88

99

10+
import org.hibernate.search.engine.search.reference.TypedFieldReference;
11+
1012
/**
1113
* The initial step in an "exists" predicate definition, where the target field can be set.
1214
*
@@ -23,4 +25,15 @@ public interface ExistsPredicateFieldStep<N extends ExistsPredicateOptionsStep<?
2325
*/
2426
N field(String fieldPath);
2527

28+
/**
29+
* Target the given field in the "exists" predicate.
30+
*
31+
* @param field The field reference representing a <a href="SearchPredicateFactory.html#field-paths">path</a> to the index field
32+
* to apply the predicate on.
33+
* @return The next step.
34+
*/
35+
default N field(TypedFieldReference<?> field) {
36+
return field( field.absolutePath() );
37+
}
38+
2639
}

engine/src/main/java/org/hibernate/search/engine/search/predicate/dsl/KnnPredicateFieldStep.java

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.search.engine.search.predicate.dsl;
88

9+
import org.hibernate.search.engine.search.reference.TypedFieldReference;
10+
911
/**
1012
* The initial step in a "knn" predicate definition, where the target field can be set.
1113
*/
@@ -18,4 +20,6 @@ public interface KnnPredicateFieldStep {
1820
* @return The next step in the knn predicate DSL.
1921
*/
2022
KnnPredicateVectorStep field(String fieldPath);
23+
24+
<T> KnnPredicateVectorGenericStep<T> field(TypedFieldReference<T> field);
2125
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Hibernate Search, full-text search for your domain model
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.search.engine.search.predicate.dsl;
8+
9+
/**
10+
* The step in a "knn" predicate definition where the vector to match is defined.
11+
*/
12+
public interface KnnPredicateVectorGenericStep<T> {
13+
/**
14+
* @param vector The vector from which to compute the distance to vectors in the indexed field.
15+
* @return The next step in the knn predicate DSL.
16+
*/
17+
KnnPredicateOptionsStep matching(T vector);
18+
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Hibernate Search, full-text search for your domain model
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.search.engine.search.predicate.dsl;
8+
9+
/**
10+
* The step in a "match" predicate definition where the value to match can be set
11+
* (see the superinterface {@link MatchPredicateMatchingStep}),
12+
* or optional parameters for the last targeted field(s) can be set,
13+
* or more target fields can be added.
14+
*
15+
* @param <S> The "self" type (the actual exposed type of this step).
16+
* @param <N> The type of the next step.
17+
* @param <T> The type of the match value.
18+
* @param <V> The type representing the fields.
19+
*/
20+
public interface MatchPredicateFieldMoreGenericStep<
21+
S extends MatchPredicateFieldMoreGenericStep<?, N, T, V>,
22+
N extends MatchPredicateOptionsStep<?>,
23+
T,
24+
V>
25+
extends MatchPredicateMatchingGenericStep<N, T>, MultiFieldPredicateFieldBoostStep<S> {
26+
27+
/**
28+
* Target the given field in the match predicate,
29+
* as an alternative to the already-targeted fields.
30+
* <p>
31+
* See {@link MatchPredicateFieldStep#field(String)} for more information about targeting fields.
32+
*
33+
* @param field The field with a <a href="SearchPredicateFactory.html#field-paths">path</a> to the index field
34+
* to apply the predicate on.
35+
* @return The next step.
36+
*
37+
* @see MatchPredicateFieldStep#field(String)
38+
*/
39+
S field(V field);
40+
41+
/**
42+
* Target the given fields in the match predicate,
43+
* as an alternative to the already-targeted fields.
44+
* <p>
45+
* See {@link MatchPredicateFieldStep#fields(String...)} for more information about targeting fields.
46+
*
47+
* @param fieldPaths The fields with <a href="SearchPredicateFactory.html#field-paths">paths</a> to the index fields
48+
* to apply the predicate on.
49+
* @return The next step.
50+
*
51+
* @see MatchPredicateFieldStep#fields(String...)
52+
*/
53+
S fields(V... fieldPaths);
54+
55+
}

0 commit comments

Comments
 (0)