Skip to content

Commit 2fbcb73

Browse files
committed
Add InventoryFilter
1 parent 269c5a8 commit 2fbcb73

File tree

4 files changed

+103
-61
lines changed

4 files changed

+103
-61
lines changed

api/src/org/labkey/api/data/DatabaseMigrationService.java

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import org.labkey.api.data.SimpleFilter.FilterClause;
1010
import org.labkey.api.data.SimpleFilter.InClause;
1111
import org.labkey.api.data.SimpleFilter.OrClause;
12+
import org.labkey.api.data.SimpleFilter.SQLClause;
1213
import org.labkey.api.query.FieldKey;
1314
import org.labkey.api.query.SchemaKey;
1415
import org.labkey.api.query.TableSorter;
1516
import org.labkey.api.services.ServiceRegistry;
17+
import org.labkey.api.util.ConfigurationException;
1618
import org.labkey.api.util.GUID;
1719
import org.labkey.api.util.logging.LogHelper;
1820
import org.labkey.vfs.FileLike;
@@ -30,7 +32,7 @@ public interface DatabaseMigrationService
3032
{
3133
Logger LOG = LogHelper.getLogger(DatabaseMigrationService.class, "Information about database migration");
3234

33-
record DomainFilter(Set<GUID> containers, String column, FilterClause condition) {}
35+
record DataFilter(Set<GUID> containers, String column, FilterClause condition) {}
3436

3537
static @NotNull DatabaseMigrationService get()
3638
{
@@ -84,11 +86,13 @@ record Sequence(String schemaName, String tableName, String columnName, long las
8486

8587
List<TableInfo> getTablesToCopy();
8688

89+
FilterClause getTableFilter(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers);
90+
8791
FilterClause getContainerClause(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers);
8892

8993
@Nullable FieldKey getContainerFieldKey(TableInfo sourceTable);
9094

91-
void addDomainDataFilter(OrClause orClause, DomainFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames);
95+
void addDomainDataFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames);
9296

9397
// Do any necessary clean up after the target table has been populated. notCopiedFilter selects all rows in the
9498
// source table that were NOT copied to the target table. (For example, they were filtered out due to container
@@ -144,6 +148,12 @@ public List<TableInfo> getTablesToCopy()
144148
.collect(Collectors.toCollection(ArrayList::new)); // Ensure mutable
145149
}
146150

151+
@Override
152+
public FilterClause getTableFilter(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
153+
{
154+
return getContainerClause(sourceTable, containerFieldKey, containers);
155+
}
156+
147157
@Override
148158
public FilterClause getContainerClause(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
149159
{
@@ -192,14 +202,17 @@ public FilterClause getContainerClause(TableInfo sourceTable, FieldKey container
192202
}
193203

194204
@Override
195-
public void addDomainDataFilter(OrClause orClause, DomainFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
205+
public void addDomainDataFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
196206
{
197-
addDomainDataStandardFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
207+
addDataFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
198208
}
199209

200-
protected void addDomainDataStandardFilter(OrClause orClause, DomainFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
210+
// Add a filter and return true if the column exists directly on the table
211+
protected boolean addDataFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
201212
{
202-
if (selectColumnNames.contains(filter.column()))
213+
boolean columnExists = selectColumnNames.contains(filter.column());
214+
215+
if (columnExists)
203216
{
204217
// Select all rows in this domain-filtered container that meet its criteria
205218
orClause.addClause(
@@ -209,37 +222,44 @@ protected void addDomainDataStandardFilter(OrClause orClause, DomainFilter filte
209222
)
210223
);
211224
}
225+
226+
return columnExists;
212227
}
213228

214-
// Special domain data filter method for provisioned tables that have a built-in Flag field
215-
protected void addDomainDataFlagFilter(OrClause orClause, DomainFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
229+
// Add a filter to select all rows where the object property with <propertyId> equals the filter value
230+
protected void addObjectPropertyFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, int propertyId)
231+
{
232+
SQLFragment flagWhere = new SQLFragment("lsid IN (SELECT ObjectURI FROM exp.Object WHERE ObjectId IN (SELECT ObjectId FROM exp.ObjectProperty WHERE StringValue = ? AND PropertyId = ?))", filter.condition().getParamVals()[0], propertyId);
233+
234+
orClause.addClause(
235+
new AndClause(
236+
getContainerClause(sourceTable, fKey, filter.containers()),
237+
new SQLClause(flagWhere)
238+
)
239+
);
240+
}
241+
242+
// Special domain data filter method for provisioned tables that have a built-in Flag field (currently used by data classes)
243+
protected void addDomainDataFlagFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
216244
{
217245
if (filter.column().equalsIgnoreCase("Flag"))
218246
{
219-
SQLFragment flagWhere = new SQLFragment("lsid IN (SELECT ObjectURI FROM exp.Object WHERE ObjectId IN (SELECT ObjectId FROM exp.ObjectProperty WHERE StringValue = ? AND PropertyId = ?))", filter.condition().getParamVals()[0], getCommentPropertyId(sourceTable));
220-
221-
// Select all rows where the built-in flag column equals the filter value
222-
orClause.addClause(
223-
new AndClause(
224-
getContainerClause(sourceTable, fKey, filter.containers()),
225-
new SimpleFilter.SQLClause(flagWhere)
226-
)
227-
);
247+
addObjectPropertyFilter(orClause, filter, sourceTable, fKey, getCommentPropertyId(sourceTable.getSchema().getScope()));
228248
}
229249
else
230250
{
231-
addDomainDataStandardFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
251+
addDataFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
232252
}
233253
}
234254

235255
private Integer _commentPropertyId = null;
236256

237-
protected synchronized int getCommentPropertyId(TableInfo sourceTable)
257+
protected synchronized int getCommentPropertyId(DbScope scope)
238258
{
239259
if (_commentPropertyId == null)
240260
{
241261
// Get the exp.PropertyDescriptor table from the source scope
242-
TableInfo propertyDescriptor = sourceTable.getSchema().getScope().getSchema("exp", DbSchemaType.Migration).getTable("PropertyDescriptor");
262+
TableInfo propertyDescriptor = scope.getSchema("exp", DbSchemaType.Migration).getTable("PropertyDescriptor");
243263
// Select the PropertyId associated with built-in Flag fields ("urn:exp.labkey.org/#Comment")
244264
Integer propertyId = new TableSelector(propertyDescriptor, Collections.singleton("PropertyId"), new SimpleFilter(FieldKey.fromParts("PropertyURI"), "urn:exp.labkey.org/#Comment"), null).getObject(Integer.class);
245265
if (propertyId == null)
@@ -274,22 +294,6 @@ interface MigrationTableHandler
274294
FilterClause getAdditionalFilterClause(Set<GUID> containers);
275295
}
276296

277-
abstract class DefaultMigrationTableHandler implements MigrationTableHandler
278-
{
279-
private final TableInfo _tableInfo;
280-
281-
public DefaultMigrationTableHandler(TableInfo tableInfo)
282-
{
283-
_tableInfo = tableInfo;
284-
}
285-
286-
@Override
287-
public TableInfo getTableInfo()
288-
{
289-
return _tableInfo;
290-
}
291-
}
292-
293297
/**
294298
* A MigrationFilter adds support for the named filter property in the migration configuration file. If present,
295299
* saveFilter() is called with the container guid and property value. Modules can register these to present
@@ -301,4 +305,26 @@ interface MigrationFilter
301305
// Implementations should validate guid nullity
302306
void saveFilter(@Nullable GUID guid, String value);
303307
}
308+
309+
// Helper method that parses a data filter then adds it and its container to the provided collections, coalescing
310+
// cases where multiple containers specify the same filter
311+
static void addDataFilter(String filterName, List<DataFilter> dataFilters, Set<GUID> filteredContainers, GUID guid, String filter)
312+
{
313+
String[] filterParts = filter.split("=");
314+
if (filterParts.length != 2)
315+
throw new ConfigurationException("Bad " + filterName + " value; expected <columnName>=<value>: " + filter);
316+
317+
if (!filteredContainers.add(guid))
318+
throw new ConfigurationException("Duplicate " + filterName + " entry for container " + guid);
319+
320+
String column = filterParts[0];
321+
String value = filterParts[1];
322+
FilterClause clause = CompareType.EQUAL.createFilterClause(new FieldKey(null, column), value);
323+
// If another container is already using this filter clause, then simply add this guid to that domain filter.
324+
// Otherwise, add a new domain filter to the list.
325+
dataFilters.stream()
326+
.filter(df -> df.column().equals(column) && df.condition().equals(clause))
327+
.findFirst()
328+
.ifPresentOrElse(df -> df.containers().add(guid), () -> dataFilters.add(new DataFilter(new HashSet<>(Set.of(guid)), filterParts[0], clause)));
329+
}
304330
}

core/src/org/labkey/core/CoreMigrationSchemaHandler.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import org.labkey.api.data.DbScope;
1111
import org.labkey.api.data.PropertySchema;
1212
import org.labkey.api.data.SQLFragment;
13-
import org.labkey.api.data.SimpleFilter;
13+
import org.labkey.api.data.SimpleFilter.AndClause;
14+
import org.labkey.api.data.SimpleFilter.FilterClause;
15+
import org.labkey.api.data.SimpleFilter.OrClause;
16+
import org.labkey.api.data.SimpleFilter.SQLClause;
1417
import org.labkey.api.data.SqlExecutor;
1518
import org.labkey.api.data.Table;
1619
import org.labkey.api.data.TableInfo;
@@ -113,19 +116,13 @@ public List<TableInfo> getTablesToCopy()
113116
}
114117

115118
@Override
116-
public SimpleFilter.FilterClause getContainerClause(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
119+
public FilterClause getTableFilter(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
117120
{
118-
SimpleFilter.FilterClause containerClause = super.getContainerClause(sourceTable, containerFieldKey, containers);
121+
FilterClause filterClause = getContainerClause(sourceTable, containerFieldKey, containers);
119122
String tableName = sourceTable.getName();
120123

121-
// Users and root groups have container == null, so add that as an OR clause
122124
if ("Principals".equals(tableName) || "Members".equals(tableName))
123125
{
124-
SimpleFilter.OrClause orClause = new SimpleFilter.OrClause();
125-
orClause.addClause(containerClause);
126-
orClause.addClause(new CompareType.CompareClause(containerFieldKey, CompareType.ISBLANK, null));
127-
containerClause = orClause;
128-
129126
if (_groupFilterCondition != null)
130127
{
131128
SQLFragment groupFilterFragment = new SQLFragment();
@@ -144,7 +141,7 @@ public SimpleFilter.FilterClause getContainerClause(TableInfo sourceTable, Field
144141
.append(_groupFilterCondition);
145142
}
146143

147-
containerClause = new SimpleFilter.AndClause(containerClause, new SimpleFilter.SQLClause(groupFilterFragment));
144+
filterClause = new AndClause(filterClause, new SQLClause(groupFilterFragment));
148145
}
149146
}
150147

@@ -153,7 +150,25 @@ public SimpleFilter.FilterClause getContainerClause(TableInfo sourceTable, Field
153150
SQLFragment groupFilterFragment = new SQLFragment("UserId IN (SELECT UserId FROM core.Principals WHERE Type <> 'g' OR (type = 'g' AND UserId ")
154151
.append(_groupFilterCondition)
155152
.append("))");
156-
containerClause = new SimpleFilter.AndClause(containerClause, new SimpleFilter.SQLClause(groupFilterFragment));
153+
filterClause = new AndClause(filterClause, new SQLClause(groupFilterFragment));
154+
}
155+
156+
return filterClause;
157+
}
158+
159+
@Override
160+
public FilterClause getContainerClause(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
161+
{
162+
FilterClause containerClause = super.getContainerClause(sourceTable, containerFieldKey, containers);
163+
String tableName = sourceTable.getName();
164+
165+
if ("Principals".equals(tableName) || "Members".equals(tableName))
166+
{
167+
// Users and root groups have container == null, so add that as an OR clause
168+
OrClause orClause = new OrClause();
169+
orClause.addClause(containerClause);
170+
orClause.addClause(new CompareType.CompareClause(containerFieldKey, CompareType.ISBLANK, null));
171+
containerClause = orClause;
157172
}
158173

159174
return containerClause;
@@ -180,7 +195,7 @@ public String getName()
180195
public void saveFilter(@Nullable GUID guid, String groupFilter)
181196
{
182197
if (guid != null)
183-
throw new ConfigurationException("GUID should not be provided to GroupFilter");
198+
throw new ConfigurationException("GroupFilter is applied globally; you cannot specify a GUID");
184199

185200
_groupFilterCondition = new SQLFragment(groupFilter);
186201
}

experiment/src/org/labkey/experiment/DataClassMigrationSchemaHandler.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import org.labkey.api.data.DatabaseMigrationConfiguration;
77
import org.labkey.api.data.DatabaseMigrationService;
88
import org.labkey.api.data.DatabaseMigrationService.DefaultMigrationSchemaHandler;
9-
import org.labkey.api.data.DatabaseMigrationService.DomainFilter;
9+
import org.labkey.api.data.DatabaseMigrationService.DataFilter;
1010
import org.labkey.api.data.DbSchema;
1111
import org.labkey.api.data.DbSchemaType;
1212
import org.labkey.api.data.DbScope;
@@ -54,26 +54,26 @@ public FilterClause getContainerClause(TableInfo sourceTable, FieldKey container
5454
{
5555
final FilterClause clause;
5656

57-
if (containerFieldKey != DUMMY_FIELD_KEY)
58-
{
59-
clause = super.getContainerClause(sourceTable, containerFieldKey, containers);
60-
}
61-
else
57+
if (containerFieldKey == DUMMY_FIELD_KEY)
6258
{
6359
// There are a couple bad data class provisioned tables that lack an FK to exp.Data. In that case, craft the
64-
// FilterClause explicitly.
60+
// container FilterClause explicitly.
6561
clause = new SQLClause(
6662
new SQLFragment("LSID IN (SELECT LSID FROM exp.Data WHERE Container")
6763
.appendInClause(containers, sourceTable.getSqlDialect())
6864
.append(")")
6965
);
7066
}
67+
else
68+
{
69+
clause = super.getContainerClause(sourceTable, containerFieldKey, containers);
70+
}
7171

7272
return clause;
7373
}
7474

7575
@Override
76-
public void addDomainDataFilter(OrClause orClause, DomainFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
76+
public void addDomainDataFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
7777
{
7878
// Data classes have a built-in Flag field
7979
addDomainDataFlagFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
@@ -164,8 +164,9 @@ public void afterSchema(DatabaseMigrationConfiguration configuration, DbSchema s
164164
DatabaseMigrationService.get().copySourceTableToTargetTable(configuration, sourceTable, targetTable, DbSchemaType.Module, sequenceMap.get("biologics"), new DefaultMigrationSchemaHandler(biologicsTargetSchema)
165165
{
166166
@Override
167-
public FilterClause getContainerClause(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
167+
public FilterClause getTableFilter(TableInfo sourceTable, FieldKey containerFieldKey, Set<GUID> containers)
168168
{
169+
// This is a global table, so no container clause. Just query and copy the sequence IDs referenced by data class rows we copied.
169170
return new InClause(FieldKey.fromParts("SequenceId"), SEQUENCE_IDS);
170171
}
171172
});

experiment/src/org/labkey/experiment/SampleTypeMigrationSchemaHandler.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import org.apache.logging.log4j.Logger;
44
import org.jetbrains.annotations.Nullable;
55
import org.labkey.api.data.DatabaseMigrationService.DefaultMigrationSchemaHandler;
6-
import org.labkey.api.data.DatabaseMigrationService.DomainFilter;
6+
import org.labkey.api.data.DatabaseMigrationService.DataFilter;
77
import org.labkey.api.data.SQLFragment;
88
import org.labkey.api.data.Selector;
99
import org.labkey.api.data.SimpleFilter;
@@ -57,7 +57,7 @@ public FilterClause getContainerClause(TableInfo sourceTable, FieldKey container
5757
}
5858

5959
@Override
60-
public void addDomainDataFilter(OrClause orClause, DomainFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
60+
public void addDomainDataFilter(OrClause orClause, DataFilter filter, TableInfo sourceTable, FieldKey fKey, Set<String> selectColumnNames)
6161
{
6262
// Sample-type-specific optimization - joining to exp.Material instead of exp.Object is much faster
6363
if (filter.column().equalsIgnoreCase("Flag"))
@@ -74,13 +74,13 @@ public void addDomainDataFilter(OrClause orClause, DomainFilter filter, TableInf
7474
.appendInClause(filter.containers(), sourceTable.getSqlDialect())
7575
.append(" AND ObjectId IN (SELECT ObjectId FROM exp.ObjectProperty WHERE StringValue = ? AND PropertyId = ?))")
7676
.add(filter.condition().getParamVals()[0])
77-
.add(getCommentPropertyId(sourceTable))
77+
.add(getCommentPropertyId(sourceTable.getSchema().getScope()))
7878
)
7979
);
8080
}
8181
else
8282
{
83-
addDomainDataStandardFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
83+
addDataFilter(orClause, filter, sourceTable, fKey, selectColumnNames);
8484
}
8585
}
8686

0 commit comments

Comments
 (0)