1
1
/*
2
- * Copyright 2011-2021 B2i Healthcare Pte Ltd, http://b2i.sg
2
+ * Copyright 2011-2022 B2i Healthcare Pte Ltd, http://b2i.sg
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
22
22
import static com .google .common .collect .Sets .newHashSet ;
23
23
24
24
import java .io .IOException ;
25
- import java .util .Collection ;
26
- import java .util .List ;
25
+ import java .util .*;
27
26
import java .util .Map .Entry ;
28
- import java .util .Objects ;
29
- import java .util .Set ;
30
27
import java .util .concurrent .TimeUnit ;
31
28
import java .util .function .BinaryOperator ;
32
29
import java .util .stream .Collectors ;
35
32
36
33
import com .b2international .commons .CompareUtils ;
37
34
import com .b2international .commons .exceptions .BadRequestException ;
35
+ import com .b2international .commons .exceptions .NotImplementedException ;
38
36
import com .b2international .commons .options .Options ;
39
37
import com .b2international .index .query .Expression ;
40
38
import com .b2international .index .query .Expressions ;
@@ -380,19 +378,27 @@ private Promise<Collection<Property>> evalRefinement(final BranchContext context
380
378
}
381
379
382
380
private Promise <Collection <Property >> evalMembers (BranchContext context , Set <String > focusConceptIds , Collection <String > typeIds , DataTypeComparison comparison ) {
383
- final Object value ;
381
+ final List < Object > values ;
384
382
final DataType type ;
385
383
if (comparison instanceof BooleanValueComparison ) {
386
- value = (( BooleanValueComparison ) comparison ).isValue ();
384
+ values = List . of ((( BooleanValueComparison ) comparison ).isValue () );
387
385
type = DataType .BOOLEAN ;
388
386
} else if (comparison instanceof StringValueComparison ) {
389
- value = ((StringValueComparison ) comparison ).getValue ();
387
+ StringValueComparison stringValueComparison = (StringValueComparison ) comparison ;
388
+ SearchTerm searchTerm = stringValueComparison .getValue ();
389
+ if (searchTerm instanceof TypedSearchTerm ) {
390
+ values = List .of (extractTerm (((TypedSearchTerm ) searchTerm ).getClause ()));
391
+ } else if (searchTerm instanceof TypedSearchTermSet ) {
392
+ values = ((TypedSearchTermSet ) searchTerm ).getClauses ().stream ().map (this ::extractTerm ).collect (Collectors .toList ());
393
+ } else {
394
+ return SnomedEclEvaluationRequest .throwUnsupported (searchTerm );
395
+ }
390
396
type = DataType .STRING ;
391
397
} else if (comparison instanceof IntegerValueComparison ) {
392
- value = (( IntegerValueComparison ) comparison ).getValue ();
398
+ values = List . of ((( IntegerValueComparison ) comparison ).getValue () );
393
399
type = DataType .INTEGER ;
394
400
} else if (comparison instanceof DecimalValueComparison ) {
395
- value = (( DecimalValueComparison ) comparison ).getValue ();
401
+ values = List . of ((( DecimalValueComparison ) comparison ).getValue () );
396
402
type = DataType .DECIMAL ;
397
403
} else {
398
404
return SnomedEclEvaluationRequest .throwUnsupported (comparison );
@@ -404,7 +410,7 @@ private Promise<Collection<Property>> evalMembers(BranchContext context, Set<Str
404
410
.put (SnomedRf2Headers .FIELD_CHARACTERISTIC_TYPE_ID , getCharacteristicTypes (expressionForm ))
405
411
.put (SnomedRf2Headers .FIELD_TYPE_ID , typeIds )
406
412
.put (SnomedRefSetMemberIndexEntry .Fields .DATA_TYPE , type )
407
- .put (SnomedRf2Headers .FIELD_VALUE , value )
413
+ .put (SnomedRf2Headers .FIELD_VALUE , values )
408
414
.put (SearchResourceRequest .operator (SnomedRf2Headers .FIELD_VALUE ), operator )
409
415
.build ();
410
416
@@ -434,40 +440,52 @@ private Promise<Collection<Property>> evalStatementsWithValue(
434
440
Collection <String > typeIds ,
435
441
DataTypeComparison comparison ) {
436
442
437
- final RelationshipValue value ;
443
+ final List < RelationshipValue > values ;
438
444
final SearchResourceRequest .Operator operator = toSearchOperator (comparison .getOp ());
439
445
440
446
// XXX: no boolean comparison for relationships with value!
441
447
if (comparison instanceof BooleanValueComparison ) {
442
448
return Promise .immediate (List .of ());
443
449
} else if (comparison instanceof StringValueComparison ) {
444
- value = new RelationshipValue (((StringValueComparison ) comparison ).getValue ());
450
+ StringValueComparison stringValueComparison = (StringValueComparison ) comparison ;
451
+ SearchTerm value = stringValueComparison .getValue ();
452
+ if (value instanceof TypedSearchTerm ) {
453
+ values = List .of (toRelationshipValue (((TypedSearchTerm ) value ).getClause ()));
454
+ } else if (value instanceof TypedSearchTermSet ) {
455
+ values = ((TypedSearchTermSet ) value ).getClauses ().stream ().map (this ::toRelationshipValue ).collect (Collectors .toList ());
456
+ } else {
457
+ return SnomedEclEvaluationRequest .throwUnsupported (value );
458
+ }
445
459
} else if (comparison instanceof IntegerValueComparison ) {
446
- value = new RelationshipValue (((IntegerValueComparison ) comparison ).getValue ());
460
+ values = List . of ( new RelationshipValue (((IntegerValueComparison ) comparison ).getValue () ));
447
461
} else if (comparison instanceof DecimalValueComparison ) {
448
- value = new RelationshipValue (((DecimalValueComparison ) comparison ).getValue ());
462
+ values = List . of ( new RelationshipValue (((DecimalValueComparison ) comparison ).getValue () ));
449
463
} else {
450
464
return SnomedEclEvaluationRequest .throwUnsupported (comparison );
451
465
}
452
466
453
- return evalStatementsWithValue (context , focusConceptIds , typeIds , value , operator );
467
+ return evalStatementsWithValue (context , focusConceptIds , typeIds , values , operator );
454
468
}
455
469
456
470
private Promise <Collection <Property >> evalStatementsWithValue (
457
471
final BranchContext context ,
458
472
final Set <String > focusConceptIds ,
459
473
final Collection <String > typeIds ,
460
- final RelationshipValue value ,
474
+ final List < RelationshipValue > values ,
461
475
final SearchResourceRequest .Operator operator ) {
462
476
477
+ if (CompareUtils .isEmpty (values )) {
478
+ return Promise .immediate (Collections .emptyList ());
479
+ }
480
+
463
481
// TODO: does this request need to support filtering by group?
464
482
Promise <Collection <Property >> statementsWithValue = SnomedRequests .prepareSearchRelationship ()
465
483
.filterByActive (true )
466
484
.filterByCharacteristicTypes (getCharacteristicTypes (expressionForm ))
467
485
.filterBySources (focusConceptIds )
468
486
.filterByTypes (typeIds )
469
- .filterByValueType (value .type ())
470
- .filterByValue (operator , value )
487
+ .filterByValueType (Iterables . getFirst ( values , null ) .type ())
488
+ .filterByValues (operator , values )
471
489
.setEclExpressionForm (expressionForm )
472
490
.setFields (ID , SOURCE_ID , TYPE_ID , RELATIONSHIP_GROUP , VALUE_TYPE , NUMERIC_VALUE , STRING_VALUE )
473
491
.setLimit (context .service (RepositoryConfiguration .class ).getIndexConfiguration ().getResultWindow ())
@@ -481,7 +499,7 @@ private Promise<Collection<Property>> evalStatementsWithValue(
481
499
);
482
500
483
501
if (Trees .STATED_FORM .equals (expressionForm )) {
484
- final Promise <Collection <Property >> axioms = evalAxiomsWithValue (context , focusConceptIds , typeIds , value , operator );
502
+ final Promise <Collection <Property >> axioms = evalAxiomsWithValue (context , focusConceptIds , typeIds , values , operator );
485
503
return Promise .all (statementsWithValue , axioms ).then (results -> {
486
504
final Collection <Property > r = (Collection <Property >) results .get (0 );
487
505
final Collection <Property > a = (Collection <Property >) results .get (1 );
@@ -496,23 +514,23 @@ private Promise<Collection<Property>> evalAxiomsWithValue(
496
514
BranchContext context ,
497
515
Set <String > focusConceptIds ,
498
516
Collection <String > typeIds ,
499
- RelationshipValue value ,
517
+ List < RelationshipValue > values ,
500
518
SearchResourceRequest .Operator operator ) {
501
519
502
520
// search existing axioms defined for the given set of conceptIds
503
- ExpressionBuilder axiomFilter = Expressions .builder ();
521
+ ExpressionBuilder axiomFilter = Expressions .bool ();
504
522
505
523
if (typeIds != null ) {
506
524
axiomFilter .filter (typeIds (typeIds ));
507
525
}
508
526
509
527
switch (operator ) {
510
- case EQUALS : axiomFilter .filter (values (List . of ( value ) )); break ;
511
- case GREATER_THAN : axiomFilter .filter (valueGreaterThan (value , false )); break ;
512
- case GREATER_THAN_EQUALS : axiomFilter .filter (valueGreaterThan (value , true )); break ;
513
- case LESS_THAN : axiomFilter .filter (valueLessThan (value , false )); break ;
514
- case LESS_THAN_EQUALS : axiomFilter .filter (valueLessThan (value , true )); break ;
515
- case NOT_EQUALS : axiomFilter .mustNot (values (List . of ( value ) )); break ;
528
+ case EQUALS : axiomFilter .filter (values (values )); break ;
529
+ case GREATER_THAN : axiomFilter .filter (valueGreaterThan (Iterables . getFirst ( values , null ) , false )); break ;
530
+ case GREATER_THAN_EQUALS : axiomFilter .filter (valueGreaterThan (Iterables . getFirst ( values , null ) , true )); break ;
531
+ case LESS_THAN : axiomFilter .filter (valueLessThan (Iterables . getFirst ( values , null ) , false )); break ;
532
+ case LESS_THAN_EQUALS : axiomFilter .filter (valueLessThan (Iterables . getFirst ( values , null ) , true )); break ;
533
+ case NOT_EQUALS : axiomFilter .mustNot (values (values )); break ;
516
534
default : throw new IllegalStateException ("Unexpected operator '" + operator + "'." );
517
535
}
518
536
@@ -540,7 +558,7 @@ private Promise<Collection<Property>> evalAxiomsWithValue(
540
558
owlMember .getClassAxiomRelationships ().stream ()
541
559
.filter (r -> typeIds == null || typeIds .contains (r .getTypeId ()))
542
560
// We need to find matching OWL relationships in Java
543
- .filter (r -> r .getValueAsObject ().matches (operator , value ))
561
+ .filter (r -> r .getValueAsObject ().matchesAny (operator , values ))
544
562
.map (r -> new Property (
545
563
owlMember .getReferencedComponentId (),
546
564
r .getTypeId (),
@@ -801,4 +819,16 @@ public String toString() {
801
819
return "Property [objectId=" + objectId + ", typeId=" + typeId + ", value=" + value + ", group=" + group + "]" ;
802
820
}
803
821
}
822
+
823
+ private RelationshipValue toRelationshipValue (TypedSearchTermClause clause ) {
824
+ return new RelationshipValue (extractTerm (clause ));
825
+ }
826
+
827
+ private String extractTerm (TypedSearchTermClause clause ) {
828
+ LexicalSearchType searchType = LexicalSearchType .fromString (clause .getLexicalSearchType ());
829
+ if (searchType != null && LexicalSearchType .EXACT != searchType ) {
830
+ throw new NotImplementedException ("Not implemented ECL feature: match, wild and regex lexical search types are not supported in concrete value string matching." );
831
+ }
832
+ return clause .getTerm ();
833
+ }
804
834
}
0 commit comments