Skip to content

Commit 15b46de

Browse files
committed
Add SSSOM/T filters for date-typed slots.
Add two new atomic filters for SSSOM/T to filter on the two date-typed slots (`mapping_date` and `publication_date`). They behave like the filters acting on numeric slots, in that: * they expect an unquoted value; * they accept '~' to represent an absence of value; * they accept the inequality operators.
1 parent b53ad73 commit 15b46de

File tree

4 files changed

+118
-8
lines changed

4 files changed

+118
-8
lines changed

ext/src/main/antlr4/org/incenp/obofoundry/sssom/transform/parser/SSSOMTransform.g4

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ filterItem: idFilterItem
1919
| textFilterItem
2020
| multiTextFilterItem
2121
| numFilterItem
22+
| dateFilterItem
2223
| cardFilterItem
2324
| predicateModifierFilterItem
2425
| entityTypeFilterItem
@@ -38,6 +39,9 @@ multiTextFilterItem : mulTxField '==' string;
3839
numFilterItem : numField numOp DOUBLE
3940
| numField '==' EMPTY;
4041

42+
dateFilterItem : dateField numOp DATE
43+
| dateField '==' EMPTY;
44+
4145
cardFilterItem : cardField '==' CARDVALUE
4246
| cardField '==' EMPTY;
4347

@@ -115,6 +119,10 @@ numField : 'confidence'
115119
| 'similarity_score'
116120
;
117121

122+
dateField : 'mapping_date'
123+
| 'publication_date'
124+
;
125+
118126
numOp : '==' | '>' | '<' | '>=' | '<=';
119127

120128
binaryOp : '&&'
@@ -155,6 +163,8 @@ SQ_STRING : '\'' (SQ_ESCAPE|.)*? '\'';
155163

156164
DQ_STRING : '"' (DQ_ESCAPE|.)*? '"';
157165

166+
DATE : [0-9][0-9][0-9][0-9] '-' [0-1][0-9] '-' [0-3][0-9];
167+
158168
TAG : [a-zA-Z0-9-]+;
159169

160170
CARDVALUE : '1:1' | '1:n' | 'n:1' | '1:0' | '0:1' | 'n:n' | '*:n' | '*:1' | 'n:*' | '1:*';

ext/src/main/java/org/incenp/obofoundry/sssom/transform/SSSOMTransformReader.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import java.io.IOException;
2323
import java.io.InputStream;
2424
import java.io.Reader;
25+
import java.time.LocalDate;
26+
import java.time.format.DateTimeFormatter;
27+
import java.time.format.DateTimeParseException;
2528
import java.util.ArrayDeque;
2629
import java.util.ArrayList;
2730
import java.util.Deque;
@@ -923,6 +926,72 @@ public IMappingFilter visitNumFilterItem(SSSOMTransformParser.NumFilterItemConte
923926
return addFilter(new NamedFilter(asText, filter));
924927
}
925928

929+
private LocalDate parseDate(String text) {
930+
LocalDate value = null;
931+
try {
932+
value = LocalDate.parse(text, DateTimeFormatter.ISO_DATE);
933+
} catch ( DateTimeParseException e ) {
934+
errors.add(new SSSOMTransformError(String.format("Invalid date: %s", text)));
935+
}
936+
return value;
937+
}
938+
939+
@Override
940+
public IMappingFilter visitDateFilterItem(SSSOMTransformParser.DateFilterItemContext ctx) {
941+
String fieldName = ctx.dateField().getText();
942+
String asText;
943+
944+
Function<LocalDate, Boolean> testValue;
945+
946+
if ( ctx.EMPTY() != null ) {
947+
asText = String.format("%s==|", fieldName);
948+
testValue = (v) -> v == null;
949+
} else {
950+
String operator = ctx.numOp().getText();
951+
LocalDate value = parseDate(ctx.DATE().getText());
952+
if ( value == null ) {
953+
return null;
954+
}
955+
asText = String.format("%s%s%s", fieldName, operator, value.toString());
956+
957+
switch ( operator ) {
958+
case "==":
959+
default:
960+
testValue = (v) -> v != null && v.isEqual(value);
961+
break;
962+
963+
case ">":
964+
testValue = (v) -> v != null && v.isAfter(value);
965+
break;
966+
967+
case ">=":
968+
testValue = (v) -> v != null && (v.isEqual(value) || v.isAfter(value));
969+
break;
970+
971+
case "<":
972+
testValue = (v) -> v != null && v.isBefore(value);
973+
break;
974+
975+
case "<=":
976+
testValue = (v) -> v != null && (v.isEqual(value) || v.isBefore(value));
977+
break;
978+
}
979+
}
980+
981+
IMappingFilter filter = null;
982+
switch ( fieldName ) {
983+
case "mapping_date":
984+
filter = (mapping) -> testValue.apply(mapping.getMappingDate());
985+
break;
986+
987+
case "publication_date":
988+
filter = (mapping) -> testValue.apply(mapping.getPublicationDate());
989+
break;
990+
}
991+
992+
return addFilter(new NamedFilter(asText, filter));
993+
}
994+
926995
@Override
927996
public IMappingFilter visitCardFilterItem(SSSOMTransformParser.CardFilterItemContext ctx) {
928997
String value = ctx.CARDVALUE() != null ? ctx.CARDVALUE().getText() : ctx.EMPTY().getText();

ext/src/site/apt/sssom-transform.apt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -252,20 +252,28 @@ creator==ORCID:*
252252
least one ORCID identifier. A mapping with no creator IDs or with only
253253
creator IDs of another type than ORCIDs will not match.
254254

255-
*** 3.1.4. Filters on numeric slots
255+
*** 3.1.4. Filters on numeric and date slots
256256

257257
For the two filters that act on numeric slots (<<<confidence>>> and
258-
<<<similarity_score>>>), in addition to the equality operator
259-
(<<<==>>>), it is possible to use one of the following inequality
260-
operators: <<<\>>>>, <<<\>>>>=, <<<\<>>>, and <<<\<=>>>. They have the
261-
traditional meaning you could expect. For example:
258+
<<<similarity_score>>>) and the two filters that act on date slots
259+
(<<<mapping_date>>> and <<<publication_date>>>) in addition to the
260+
equality operator (<<<==>>>), it is possible to use one of the
261+
following inequality operators: <<<\>>>>, <<<\>>>>=, <<<\<>>>, and
262+
<<<\<=>>>. They have the traditional meaning you could expect. For
263+
example:
262264

263265
+-----------------------------------------------------------------------
264266
confidence>=0.8
265267
+-----------------------------------------------------------------------
266268

267269
will match any mapping with a confidence value higher than or equal to
268-
0.8.
270+
0.8, and:
271+
272+
+-----------------------------------------------------------------------
273+
publication_date>2025-02-01
274+
+-----------------------------------------------------------------------
275+
276+
will match any mapping published after the 1st of February, 2025.
269277

270278
*** 3.1.5. Filtering on mapping predicate
271279

@@ -335,8 +343,8 @@ mapping_tool==""
335343
author==~
336344
+-----------------------------------------------------------------------
337345

338-
That syntax is also accepted by the filters on numeric slots and on
339-
<<<mapping_cardinality>>>.
346+
That syntax is also accepted by the filters on numeric slots, date
347+
slots, and on <<<mapping_cardinality>>>.
340348

341349
*** 3.1.8. Function filters
342350

ext/src/test/java/org/incenp/obofoundry/sssom/transform/SSSOMTransformReaderTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.incenp.obofoundry.sssom.transform;
2020

2121
import java.io.IOException;
22+
import java.time.LocalDate;
2223
import java.util.ArrayList;
2324
import java.util.List;
2425
import java.util.Map;
@@ -347,6 +348,28 @@ void testNumericFilter() {
347348
checkFilter("!confidence==~ -> action();\n", none, false);
348349
}
349350

351+
/*
352+
* Test that a mapping is correctly selected by a filter on a date slot.
353+
*/
354+
@Test
355+
void testDateFilter() {
356+
Mapping feb1 = Mapping.builder().mappingDate(LocalDate.of(2025, 2, 1)).build();
357+
358+
checkFilter("mapping_date==2025-02-01 -> action();\n", feb1, true);
359+
checkFilter("publication_date==2025-02-01 -> action();\n", feb1, false);
360+
checkFilter("publication_date==~ -> action();\n", feb1, true);
361+
362+
checkFilter("mapping_date==2025-02-02 -> action();\n", feb1, false);
363+
checkFilter("mapping_date>2025-01-31 -> action();\n", feb1, true);
364+
checkFilter("mapping_date>=2025-02-01 -> action();\n", feb1, true);
365+
checkFilter("mapping_date<2025-02-01 -> action();\n", feb1, false);
366+
checkFilter("mapping_date<=2025-02-01 -> action();\n", feb1, true);
367+
368+
// Test some invalid dates
369+
parseRule("mapping_date==2025-02-49 -> action();", null);
370+
parseRule("mapping_date==2025-02-39 -> action();", null);
371+
}
372+
350373
/*
351374
* Test that a mapping is correctly selected by a filter on the
352375
* mapping_cardinality slot.

0 commit comments

Comments
 (0)