Skip to content

Commit 79530fc

Browse files
committed
Improve definition of filterable properties and aliases
Rename factorValueCharacteristics and bioMaterialCharacteristics to use a common prefix with already existing paths.
1 parent e8d84a1 commit 79530fc

File tree

8 files changed

+135
-146
lines changed

8 files changed

+135
-146
lines changed

gemma-core/src/main/java/ubic/gemma/persistence/service/AbstractCriteriaFilteringVoEnabledDao.java

+24-28
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
*/
3636
public abstract class AbstractCriteriaFilteringVoEnabledDao<O extends Identifiable, VO extends IdentifiableValueObject<O>> extends AbstractFilteringVoEnabledDao<O, VO> {
3737

38+
@Autowired
39+
private PlatformTransactionManager platformTransactionManager;
40+
3841
protected AbstractCriteriaFilteringVoEnabledDao( Class<? extends O> elementClass, SessionFactory sessionFactory ) {
3942
// This is a good default objet alias for Hibernate Criteria since null is used to refer to the root entity.
4043
super( null, elementClass, sessionFactory );
@@ -232,52 +235,45 @@ public long countPreFilter( @Nullable Filters filters ) {
232235
return ret;
233236
}
234237

238+
@Override
239+
protected FilterablePropertyMeta getFilterablePropertyMeta( String propertyName ) throws IllegalArgumentException {
240+
FilterablePropertyMeta meta = super.getFilterablePropertyMeta( propertyName );
241+
List<FilterablePropertyCriteriaAlias> aliases = getFilterablePropertyCriteriaAliases();
242+
// substitute longest path first
243+
aliases.sort( Comparator.comparing( a -> a.propertyName.length(), Comparator.reverseOrder() ) );
244+
for ( FilterablePropertyCriteriaAlias alias : aliases ) {
245+
if ( propertyName.startsWith( alias.propertyName + "." ) ) {
246+
propertyName = propertyName.replaceFirst( "^" + Pattern.quote( alias.propertyName + "." ), "" );
247+
return meta
248+
.withObjectAlias( alias.alias )
249+
.withPropertyName( propertyName );
250+
}
251+
}
252+
return meta;
253+
}
254+
235255
@Value
236-
protected static class FilterablePropertyAlias {
256+
private static class FilterablePropertyCriteriaAlias {
237257
String propertyName;
238258
String alias;
239259
}
240260

241-
@Autowired
242-
private PlatformTransactionManager platformTransactionManager;
243-
244-
/**
245-
* Unfortunately, because of how criteria API works, you have to explicitly list all aliases.
246-
* TODO: infer this from the criteria.
247-
*/
248-
protected List<FilterablePropertyAlias> getFilterablePropertyAliases() {
261+
private List<FilterablePropertyCriteriaAlias> getFilterablePropertyCriteriaAliases() {
249262
// FIXME: unfortunately, this requires a session...
250263
Criteria criteria = new TransactionTemplate( platformTransactionManager ).execute( ( ts ) -> getFilteringCriteria( Filters.empty() ) );
251264
if ( criteria instanceof CriteriaImpl ) {
252265
//noinspection unchecked
253266
Iterator<CriteriaImpl.Subcriteria> it = ( ( CriteriaImpl ) criteria ).iterateSubcriteria();
254-
List<FilterablePropertyAlias> result = new ArrayList<>();
267+
List<FilterablePropertyCriteriaAlias> result = new ArrayList<>();
255268
while ( it.hasNext() ) {
256269
CriteriaImpl.Subcriteria sc = it.next();
257-
result.add( new FilterablePropertyAlias( sc.getPath(), sc.getAlias() ) );
270+
result.add( new FilterablePropertyCriteriaAlias( sc.getPath(), sc.getAlias() ) );
258271
}
259272
return result;
260273
}
261274
return Collections.emptyList();
262275
}
263276

264-
@Override
265-
protected FilterablePropertyMeta getFilterablePropertyMeta( String propertyName ) throws IllegalArgumentException {
266-
FilterablePropertyMeta meta = super.getFilterablePropertyMeta( propertyName );
267-
List<FilterablePropertyAlias> aliases = getFilterablePropertyAliases();
268-
// substitute longest path first
269-
aliases.sort( Comparator.comparing( a -> a.propertyName.length(), Comparator.reverseOrder() ) );
270-
for ( FilterablePropertyAlias alias : aliases ) {
271-
if ( propertyName.startsWith( alias.propertyName + "." ) ) {
272-
propertyName = propertyName.replaceFirst( "^" + Pattern.quote( alias.propertyName + "." ), "" );
273-
return meta
274-
.withObjectAlias( alias.alias )
275-
.withPropertyName( propertyName );
276-
}
277-
}
278-
return meta;
279-
}
280-
281277
private static void addOrder( Criteria query, Sort sort ) {
282278
String propertyName = formPropertyName( sort.getObjectAlias(), sort.getPropertyName() );
283279
// handle .size ordering

gemma-core/src/main/java/ubic/gemma/persistence/service/AbstractFilteringVoEnabledDao.java

+69-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package ubic.gemma.persistence.service;
22

3-
import lombok.AccessLevel;
4-
import lombok.AllArgsConstructor;
5-
import lombok.Value;
6-
import lombok.With;
3+
import lombok.*;
74
import org.apache.commons.lang3.ArrayUtils;
85
import org.hibernate.SessionFactory;
96
import org.hibernate.metadata.ClassMetadata;
@@ -18,8 +15,11 @@
1815
import ubic.gemma.persistence.util.Sort;
1916

2017
import javax.annotation.Nullable;
18+
import javax.annotation.OverridingMethodsMustInvokeSuper;
2119
import java.util.*;
2220
import java.util.concurrent.ConcurrentHashMap;
21+
import java.util.regex.Pattern;
22+
import java.util.stream.Collectors;
2323

2424
/**
2525
* Base implementation for {@link FilteringVoEnabledDao}.
@@ -33,7 +33,7 @@ public abstract class AbstractFilteringVoEnabledDao<O extends Identifiable, VO e
3333
/**
3434
* Maximum depth to explore when enumerating filterable properties via {@link #getFilterableProperties()}.
3535
*/
36-
protected static final int FILTERABLE_PROPERTIES_MAX_DEPTH = 3;
36+
private static final int FILTERABLE_PROPERTIES_MAX_DEPTH = 3;
3737

3838
/**
3939
* Cached partial filterable properties meta, computed as we go.
@@ -53,12 +53,18 @@ private static class Key {
5353
*/
5454
private final Set<String> filterableProperties;
5555

56+
/**
57+
* Aliases for filterable properties.
58+
*/
59+
private final Set<FilterablePropertyAlias> filterablePropertyAliases;
60+
5661
protected AbstractFilteringVoEnabledDao( @Nullable String objectAlias, Class<? extends O> elementClass, SessionFactory sessionFactory ) {
5762
super( elementClass, sessionFactory );
5863
this.objectAlias = objectAlias;
59-
Set<String> result = new HashSet<>();
60-
addFilterableProperties( "", elementClass, result, FILTERABLE_PROPERTIES_MAX_DEPTH );
61-
this.filterableProperties = Collections.unmodifiableSet( result );
64+
this.filterablePropertyAliases = new HashSet<>();
65+
registerFilterablePropertyAliases( this.filterablePropertyAliases );
66+
this.filterableProperties = new HashSet<>();
67+
registerFilterableProperties( this.filterableProperties );
6268
}
6369

6470
/**
@@ -122,10 +128,32 @@ public final List<VO> loadAllValueObjects() {
122128
}
123129

124130
@Override
125-
public Set<String> getFilterableProperties() {
131+
public final Set<String> getFilterableProperties() {
126132
return filterableProperties;
127133
}
128134

135+
/**
136+
* Register filterable properties.
137+
* @param properties a collection to which filterable properties are to be added
138+
*/
139+
@OverridingMethodsMustInvokeSuper
140+
protected void registerFilterableProperties( Set<String> properties ) {
141+
addFilterableProperties( "", elementClass, properties, FILTERABLE_PROPERTIES_MAX_DEPTH );
142+
// FIXME: the aliases are not available because they are registered afterward in the constructor
143+
Set<FilterablePropertyAlias> aliases = new HashSet<>();
144+
registerFilterablePropertyAliases( aliases );
145+
for ( FilterablePropertyAlias alias : aliases ) {
146+
addFilterableProperties( alias.prefix, alias.propertyType, properties, FILTERABLE_PROPERTIES_MAX_DEPTH - 1 );
147+
}
148+
}
149+
150+
/**
151+
* Register aliases for filterable properties.
152+
* @param aliases a collection to which aliases are to be added
153+
*/
154+
protected void registerFilterablePropertyAliases( Set<FilterablePropertyAlias> aliases ) {
155+
}
156+
129157
@Override
130158
public Class<?> getFilterablePropertyType( String propertyName ) throws IllegalArgumentException {
131159
return getFilterablePropertyMeta( propertyName ).propertyType;
@@ -139,7 +167,7 @@ public String getFilterablePropertyDescription( String propertyName ) throws Ill
139167

140168
@Nullable
141169
@Override
142-
public List<Object> getFilterablePropertyAvailableValues( String propertyName ) {
170+
public List<Object> getFilterablePropertyAvailableValues( String propertyName ) throws IllegalArgumentException {
143171
return getFilterablePropertyMeta( propertyName ).availableValues;
144172
}
145173

@@ -164,7 +192,10 @@ public final Sort getSort( String property, @Nullable Sort.Direction direction )
164192
/**
165193
* Helper that inspects a class and add all the filterable properties with the given prefix.
166194
*/
167-
protected void addFilterableProperties( String prefix, Class<?> entityClass, Set<String> destination, int maxDepth ) {
195+
private void addFilterableProperties( String prefix, Class<?> entityClass, Set<String> destination, int maxDepth ) {
196+
if ( !prefix.isEmpty() && !prefix.endsWith( "." ) ) {
197+
throw new IllegalArgumentException( "A non-empty prefix must end with a '.' character." );
198+
}
168199
if ( maxDepth <= 0 ) {
169200
throw new IllegalArgumentException( String.format( "Maximum depth for adding filterable properties of %s to %s must be strictly positive.",
170201
entityClass.getName(), prefix ) );
@@ -213,6 +244,22 @@ protected static class FilterablePropertyMeta {
213244
List<Object> availableValues;
214245
}
215246

247+
@Value
248+
@EqualsAndHashCode(of = "prefix")
249+
protected static class FilterablePropertyAlias {
250+
String prefix;
251+
@Nullable
252+
String objectAlias;
253+
Class<?> propertyType;
254+
/**
255+
* If this alias is actual aliasing another alias.
256+
* <p>
257+
* Example: {@code taxon. -> primaryTaxon.}
258+
*/
259+
@Nullable
260+
String aliasFor;
261+
}
262+
216263
/**
217264
* Obtain various meta-information used to infer what to use in a {@link Filter} or {@link Sort}.
218265
* <p>
@@ -224,6 +271,17 @@ protected static class FilterablePropertyMeta {
224271
* @see #getSort(String, Sort.Direction)
225272
*/
226273
protected FilterablePropertyMeta getFilterablePropertyMeta( String propertyName ) throws IllegalArgumentException {
274+
// replace longer prefix first
275+
List<FilterablePropertyAlias> aliases = filterablePropertyAliases.stream()
276+
.sorted( Comparator.comparing( f -> f.prefix.length(), Comparator.reverseOrder() ) )
277+
.collect( Collectors.toList() );
278+
for ( FilterablePropertyAlias alias : aliases ) {
279+
if ( propertyName.startsWith( alias.prefix ) && !propertyName.equals( alias.prefix + "size" ) ) {
280+
String fieldName = propertyName.replaceFirst( "^" + Pattern.quote( alias.prefix ), "" );
281+
return getFilterablePropertyMeta( alias.objectAlias, fieldName, alias.propertyType )
282+
.withDescription( alias.aliasFor != null ? String.format( "alias for %s.%s", alias.aliasFor, fieldName ) : null );
283+
}
284+
}
227285
return getFilterablePropertyMeta( objectAlias, propertyName, elementClass );
228286
}
229287

gemma-core/src/main/java/ubic/gemma/persistence/service/AbstractQueryFilteringVoEnabledDao.java

+8-56
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package ubic.gemma.persistence.service;
22

3-
import lombok.Value;
43
import org.apache.commons.lang3.NotImplementedException;
54
import org.apache.commons.lang3.time.StopWatch;
65
import org.hibernate.Query;
@@ -13,9 +12,10 @@
1312
import ubic.gemma.persistence.util.Sort;
1413

1514
import javax.annotation.Nullable;
16-
import java.util.*;
15+
import java.util.EnumSet;
16+
import java.util.List;
17+
import java.util.Objects;
1718
import java.util.concurrent.TimeUnit;
18-
import java.util.regex.Pattern;
1919
import java.util.stream.Collectors;
2020

2121
/**
@@ -46,54 +46,6 @@ protected AbstractQueryFilteringVoEnabledDao( String objectAlias, Class<O> eleme
4646
super( objectAlias, elementClass, sessionFactory );
4747
}
4848

49-
@Value
50-
protected static class FilterablePropertyQueryAlias {
51-
String prefix;
52-
@Nullable
53-
String objectAlias;
54-
Class<?> propertyType;
55-
}
56-
57-
/**
58-
* Since HQL-based filtering cannot simply detect aliases in the query, you have to declare them explicitly.
59-
*/
60-
protected FilterablePropertyQueryAlias[] getFilterablePropertyQueryAliases() {
61-
return new FilterablePropertyQueryAlias[0];
62-
}
63-
64-
@Override
65-
public Set<String> getFilterableProperties() {
66-
Set<String> results = super.getFilterableProperties();
67-
if ( getFilterablePropertyQueryAliases().length > 0 ) {
68-
results = new HashSet<>( results );
69-
for ( FilterablePropertyQueryAlias alias : getFilterablePropertyQueryAliases() ) {
70-
addFilterableProperties( alias.prefix, alias.propertyType, results, FILTERABLE_PROPERTIES_MAX_DEPTH - 1 );
71-
}
72-
results = Collections.unmodifiableSet( results );
73-
}
74-
return results;
75-
}
76-
77-
/**
78-
* Checks for special properties that are allowed to be referenced on certain objects. E.g. characteristics on EEs.
79-
* {@inheritDoc}
80-
*/
81-
@Override
82-
protected FilterablePropertyMeta getFilterablePropertyMeta( String propertyName ) {
83-
// replace longer prefix first
84-
List<FilterablePropertyQueryAlias> aliases = Arrays.stream( getFilterablePropertyQueryAliases() )
85-
.sorted( Comparator.comparing( f -> f.prefix.length(), Comparator.reverseOrder() ) )
86-
.collect( Collectors.toList() );
87-
for ( FilterablePropertyQueryAlias alias : aliases ) {
88-
if ( propertyName.startsWith( alias.prefix ) && !propertyName.equals( alias.prefix + "size" ) ) {
89-
String fieldName = propertyName.replaceFirst( "^" + Pattern.quote( alias.prefix ), "" );
90-
return getFilterablePropertyMeta( alias.objectAlias, fieldName, alias.propertyType );
91-
}
92-
}
93-
return super.getFilterablePropertyMeta( propertyName );
94-
}
95-
96-
9749
/**
9850
* Produce a query for retrieving value objects after applying a set of filters and a given ordering.
9951
* <p>
@@ -120,21 +72,21 @@ protected Query getFilteringCountQuery( @Nullable Filters filters ) {
12072
}
12173

12274
/**
123-
* Process a result from {@link #getFilteringQuery(Filters, Sort, EnumSet)} into a {@link O}.
75+
* Process a properties from {@link #getFilteringQuery(Filters, Sort, EnumSet)} into a {@link O}.
12476
* <p>
125-
* The default is to simply cast the result to {@link O}, assuming that it is the only return value of the query.
77+
* The default is to simply cast the properties to {@link O}, assuming that it is the only return value of the query.
12678
*/
12779
protected O processFilteringQueryResultToEntity( Object result ) {
12880
//noinspection unchecked
12981
return ( O ) result;
13082
}
13183

13284
/**
133-
* Process a result from {@link #getFilteringQuery(Filters, Sort, EnumSet)} into a {@link VO} value object.
85+
* Process a properties from {@link #getFilteringQuery(Filters, Sort, EnumSet)} into a {@link VO} value object.
13486
* <p>
135-
* The result is obtained from {@link Query#list()}.
87+
* The properties is obtained from {@link Query#list()}.
13688
* <p>
137-
* By default, it will process the result with {@link #processFilteringQueryResultToEntity(Object)} and then apply
89+
* By default, it will process the properties with {@link #processFilteringQueryResultToEntity(Object)} and then apply
13890
* {@link #doLoadValueObject(Identifiable)} to obtain a value object.
13991
*
14092
* @return a value object, or null, and it will be ignored when constructing the {@link Slice} in {@link #loadValueObjectsPreFilter(Filters, Sort, int, int)}

gemma-core/src/main/java/ubic/gemma/persistence/service/analysis/expression/diff/ExpressionAnalysisResultSetDaoImpl.java

+5-6
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,12 @@ protected Criteria getFilteringCriteria( @Nullable Filters filters ) {
234234
}
235235

236236
@Override
237-
public Set<String> getFilterableProperties() {
238-
Set<String> results = new HashSet<>( super.getFilterableProperties() );
237+
protected void registerFilterableProperties( Set<String> properties ) {
238+
super.registerFilterableProperties( properties );
239239
// these cause a org.hibernate.MappingException: Unknown collection role exception (see https://github.com/PavlidisLab/Gemma/issues/518)
240-
results.remove( "analysis.experimentAnalyzed.characteristics.size" );
241-
results.remove( "analysis.experimentAnalyzed.otherRelevantPublications.size" );
242-
results.remove( "experimentalFactors.size" );
243-
return results;
240+
properties.remove( "analysis.experimentAnalyzed.characteristics.size" );
241+
properties.remove( "analysis.experimentAnalyzed.otherRelevantPublications.size" );
242+
properties.remove( "experimentalFactors.size" );
244243
}
245244

246245
@Override

gemma-core/src/main/java/ubic/gemma/persistence/service/common/auditAndSecurity/curation/AbstractCuratableDao.java

+7-9
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,14 @@
77
import ubic.gemma.model.common.auditAndSecurity.curation.AbstractCuratableValueObject;
88
import ubic.gemma.model.common.auditAndSecurity.curation.Curatable;
99
import ubic.gemma.model.common.auditAndSecurity.curation.CurationDetails;
10-
import ubic.gemma.model.common.description.DatabaseEntry;
1110
import ubic.gemma.persistence.service.AbstractQueryFilteringVoEnabledDao;
1211
import ubic.gemma.persistence.service.common.auditAndSecurity.CurationDetailsDao;
13-
import ubic.gemma.persistence.util.Filters;
1412
import ubic.gemma.persistence.util.Filter;
15-
import ubic.gemma.persistence.util.Slice;
13+
import ubic.gemma.persistence.util.Filters;
1614

1715
import javax.annotation.Nullable;
16+
import javax.annotation.OverridingMethodsMustInvokeSuper;
1817
import java.util.*;
19-
import java.util.stream.Collectors;
2018

2119
/**
2220
* Created by tesarst on 07/03/17.
@@ -91,12 +89,12 @@ protected void addNonTroubledFilter( Filters filters, @Nullable String objectAli
9189
}
9290

9391
@Override
94-
public Set<String> getFilterableProperties() {
95-
Set<String> result = new HashSet<>( super.getFilterableProperties() );
96-
result.addAll( Arrays.asList( "lastUpdated", "troubled", "needsAttention" ) );
97-
return result;
92+
@OverridingMethodsMustInvokeSuper
93+
protected void registerFilterableProperties( Set<String> properties ) {
94+
super.registerFilterableProperties( properties );
95+
properties.addAll( Arrays.asList( "lastUpdated", "troubled", "needsAttention" ) );
9896
}
99-
97+
10098
/**
10199
* {@inheritDoc}
102100
* <p>

0 commit comments

Comments
 (0)