Skip to content

Commit

Permalink
Merge pull request #1343 from b2ihealthcare/issue/SO-6148-exclude_irr…
Browse files Browse the repository at this point in the history
…elevant_axiom_types_from_validation

SO-6148 exclude irrelevant axiom types from validation
  • Loading branch information
cmark authored Nov 25, 2024
2 parents 7a14b76 + 9daee35 commit f79037c
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.b2international.index.query.Expressions.ExpressionBuilder
import com.b2international.index.revision.RevisionSearcher
import com.b2international.snowowl.core.ComponentIdentifier
import com.b2international.snowowl.core.date.EffectiveTimes
import com.b2international.snowowl.snomed.common.SnomedRf2Headers
import com.b2international.snowowl.snomed.common.SnomedTerminologyComponentConstants
import com.b2international.snowowl.snomed.common.SnomedConstants.Concepts
import com.b2international.snowowl.snomed.core.domain.constraint.SnomedCardinalityPredicate
Expand All @@ -38,6 +39,9 @@ Set<ComponentIdentifier> issues = Sets.newHashSet()

String oneMandatoryIsAPerConceptRuleId = "b93819e3-2679-46f6-a35d-3a749842d83e"

Set<String> unsupportedAxiomMarkers = [ "TransitiveObjectProperty", "ReflexiveObjectProperty",
"SubDataPropertyOf", "SubObjectPropertyOf", "ObjectPropertyChain", "SubAnnotationPropertyOf"];

def getPredicate = { SnomedConstraint constraint ->
return constraint.getPredicate() instanceof SnomedCardinalityPredicate
? ((SnomedCardinalityPredicate) constraint.getPredicate()).getPredicate()
Expand Down Expand Up @@ -154,20 +158,26 @@ if (params.isUnpublishedOnly) {
.execute(ctx);

for (SnomedReferenceSetMember owlAxiomMember : owlAxiomMembers) {
def applicableRulePredicates = getApplicableRules(owlAxiomMember.referencedComponent.id)
.stream()
.map({getPredicate(it)})
.filter({ SnomedRelationshipPredicate predicate ->
def charType = predicate.getCharacteristicTypeId()
return Concepts.STATED_RELATIONSHIP == charType || Strings.isNullOrEmpty(charType)
}).collect(Collectors.toSet())

for (SnomedRelationshipPredicate predicate : applicableRulePredicates) {
for (SnomedOWLRelationshipDocument relationship : getOWLRelationships(owlAxiomMember)) {
if (!relationship.hasValue() &&
getApplicableConcepts(predicate.getAttributeExpression()).contains(relationship.getTypeId()) &&
!getApplicableConcepts(predicate.getRangeExpression()).contains(relationship.getDestinationId())) {
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, owlAxiomMember.getId()))

final String owlExpression = owlAxiomMember.getProperties().get(SnomedRf2Headers.FIELD_OWL_EXPRESSION);
def shouldValidate = unsupportedAxiomMarkers.stream().noneMatch({ marker -> owlExpression.contains(marker)});

if (shouldValidate) {
def applicableRulePredicates = getApplicableRules(owlAxiomMember.referencedComponent.id)
.stream()
.map({getPredicate(it)})
.filter({ SnomedRelationshipPredicate predicate ->
def charType = predicate.getCharacteristicTypeId()
return Concepts.STATED_RELATIONSHIP == charType || Strings.isNullOrEmpty(charType)
}).collect(Collectors.toSet())

for (SnomedRelationshipPredicate predicate : applicableRulePredicates) {
for (SnomedOWLRelationshipDocument relationship : getOWLRelationships(owlAxiomMember)) {
if (!relationship.hasValue() &&
getApplicableConcepts(predicate.getAttributeExpression()).contains(relationship.getTypeId()) &&
!getApplicableConcepts(predicate.getRangeExpression()).contains(relationship.getDestinationId())) {
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, owlAxiomMember.getId()))
}
}
}
}
Expand Down Expand Up @@ -241,19 +251,23 @@ if (params.isUnpublishedOnly) {
.filter(SnomedRefSetMemberIndexEntry.Expressions.active())
.filter(SnomedRefSetMemberIndexEntry.Expressions.referencedComponentIds(domain))
.should(nestedMatch(SnomedRefSetMemberIndexEntry.Fields.CLASS_AXIOM_RELATIONSHIP, nestedBuilder.build()))
.should(nestedMatch(SnomedRefSetMemberIndexEntry.Fields.GCI_AXIOM_RELATIONSHIP, nestedBuilder.build()))
.setMinimumNumberShouldMatch(1)

final Query<String> query = Query.select(String.class)
final Query<String[]> query = Query.select(String[].class)
.from(SnomedRefSetMemberIndexEntry.class)
.fields(SnomedRelationshipIndexEntry.Fields.ID)
.fields(SnomedRelationshipIndexEntry.Fields.ID, SnomedRf2Headers.FIELD_OWL_EXPRESSION)
.where(expressionBuilder.build())
.limit(10_000)
.build()

searcher.scroll(query).forEach({ hits ->
hits.forEach({ id ->
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, id))
hits.forEach({ hit ->
def id = hit[0]
def owlExpression = hit[1]
def shouldValidate = unsupportedAxiomMarkers.stream().noneMatch({ marker -> owlExpression.contains(marker)});
if (shouldValidate) {
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, id))
}
})
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.b2international.index.query.Expressions.ExpressionBuilder
import com.b2international.index.revision.RevisionSearcher
import com.b2international.snowowl.core.ComponentIdentifier
import com.b2international.snowowl.core.date.EffectiveTimes
import com.b2international.snowowl.snomed.common.SnomedRf2Headers
import com.b2international.snowowl.snomed.common.SnomedTerminologyComponentConstants
import com.b2international.snowowl.snomed.common.SnomedConstants.Concepts
import com.b2international.snowowl.snomed.core.domain.SnomedRelationship
Expand Down Expand Up @@ -37,6 +38,9 @@ import com.google.common.collect.Sets
RevisionSearcher searcher = ctx.service(RevisionSearcher.class)
Set<ComponentIdentifier> issues = Sets.newHashSet()

Set<String> unsupportedAxiomMarkers = [ "TransitiveObjectProperty", "ReflexiveObjectProperty",
"SubDataPropertyOf", "SubObjectPropertyOf", "ObjectPropertyChain", "SubAnnotationPropertyOf"];

def getInnerPredicate = { SnomedPredicate predicate ->
if (predicate instanceof SnomedCardinalityPredicate) {
return predicate.getPredicate()
Expand Down Expand Up @@ -187,11 +191,17 @@ if (params.isUnpublishedOnly) {
.execute(ctx)

for (SnomedReferenceSetMember owlAxiomMember : owlAxiomMembers) {
def sourceId = owlAxiomMember.getReferencedComponent().getId()
for (SnomedOWLRelationshipDocument relationship : getOWLRelationships(owlAxiomMember)) {
def typeId = relationship.getTypeId()
if (!typeId.equals(Concepts.IS_A) && getApplicableRules(sourceId, typeId, true).isEmpty()) {
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, owlAxiomMember.getId()))

String owlExpression = owlAxiomMember.getProperties().get(SnomedRf2Headers.FIELD_OWL_EXPRESSION)
boolean shouldValidate = unsupportedAxiomMarkers.stream().noneMatch({ marker -> owlExpression.contains(marker)});

if (shouldValidate) {
def sourceId = owlAxiomMember.getReferencedComponent().getId()
for (SnomedOWLRelationshipDocument relationship : getOWLRelationships(owlAxiomMember)) {
def typeId = relationship.getTypeId()
if (!typeId.equals(Concepts.IS_A) && getApplicableRules(sourceId, typeId, true).isEmpty()) {
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, owlAxiomMember.getId()))
}
}
}
}
Expand Down Expand Up @@ -296,22 +306,30 @@ if (params.isUnpublishedOnly) {

allowedTypeIdsForOwlRelationships.add(Concepts.IS_A)

final ExpressionBuilder nestedBuilder = Expressions.builder()
.mustNot(SnomedRelationshipIndexEntry.Expressions.typeIds(allowedTypeIdsForOwlRelationships))

final ExpressionBuilder owlMemberExpressionBuilder = Expressions.builder()
.filter(SnomedRefSetMemberIndexEntry.Expressions.active())
.filter(SnomedRefSetMemberIndexEntry.Expressions.refSetTypes([SnomedRefSetType.OWL_AXIOM]))
.mustNot(SnomedRefSetMemberIndexEntry.Expressions.owlExpressionType(allowedTypeIdsForOwlRelationships))
.should(Expressions.nestedMatch(SnomedRefSetMemberIndexEntry.Fields.CLASS_AXIOM_RELATIONSHIP, nestedBuilder.build()))

final Query<String> owlMemberQuery = Query.select(String.class)
final Query<String[]> owlMemberQuery = Query.select(String[].class)
.from(SnomedRefSetMemberIndexEntry.class)
.fields(SnomedRefSetMemberIndexEntry.Fields.ID)
.fields(SnomedRefSetMemberIndexEntry.Fields.ID, SnomedRf2Headers.FIELD_OWL_EXPRESSION)
.where(owlMemberExpressionBuilder.build())
.limit(10_000)
.scroll("2m")
.build()

searcher.scroll(owlMemberQuery).forEach({ hits ->
hits.forEach({ id ->
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, id))
hits.forEach({ hit ->
def id = hit[0]
def owlExpression = hit[1]
def shouldValidate = unsupportedAxiomMarkers.stream().noneMatch({ marker -> owlExpression.contains(marker)});
if (shouldValidate) {
issues.add(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, id))
}
})
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,13 @@ public void rule_mrcm_constraint() throws Exception {
.referenceSetType(SnomedRefSetType.OWL_AXIOM)
.build();

indexRevision(MAIN, relationship1, relationship2, relationship3, axiomMember1, axiomMember2, axiomMember3);
SnomedRefSetMemberIndexEntry axiomMember4 = member(Concepts.CONCEPT_MODEL_ATTRIBUTE, Concepts.REFSET_OWL_AXIOM)
.classAxiomRelationships(Lists.newArrayList(SnomedOWLRelationshipDocument.create(Concepts.FINDING_SITE, Concepts.CONCEPT_MODEL_ATTRIBUTE, 0)))
.owlExpression(String.format("TransitiveObjectProperty(:%s)", Concepts.CONCEPT_MODEL_ATTRIBUTE))
.referenceSetType(SnomedRefSetType.OWL_AXIOM)
.build();

indexRevision(MAIN, relationship1, relationship2, relationship3, axiomMember1, axiomMember2, axiomMember3, axiomMember4);

ValidationIssues issues = validate(ruleId);
assertAffectedComponents(issues,
Expand Down Expand Up @@ -1324,24 +1330,24 @@ public void rule_mrcm_constraint_type() throws Exception {
.owlExpression(String.format("ObjectSomeValuesFrom(:%s :%s)", Concepts.PHYSICAL_OBJECT, Concepts.CONCEPT_MODEL_ATTRIBUTE))
.build();

SnomedRefSetMemberIndexEntry axiomMember4 = member(Concepts.ROOT_CONCEPT, Concepts.REFSET_OWL_AXIOM)
.referenceSetType(SnomedRefSetType.OWL_AXIOM)
.classAxiomRelationships(Lists.newArrayList(SnomedOWLRelationshipDocument.create(Concepts.PHYSICAL_OBJECT, Concepts.CONCEPT_MODEL_ATTRIBUTE, 0)))
.owlExpression(String.format("ReflexiveObjectProperty(:%s)", Concepts.ROOT_CONCEPT))
.build();

indexRevision(MAIN, attributeConstraint1, attributeConstraint2, attributeConstraint3,
relationship1, relationship2, relationship3, relationship4, relationship5, relationship6, relationship7, relationship8,
axiomMember1, axiomMember2, axiomMember3);
axiomMember1, axiomMember2, axiomMember3, axiomMember4);

ValidationIssues issues = validate(ruleId);
Assertions.assertThat(issues.stream().map(ValidationIssue::getAffectedComponent).collect(Collectors.toSet()))
.contains(
assertAffectedComponents(issues,
ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, axiomMember2.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, axiomMember3.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship3.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship4.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship7.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship8.getId()))
.doesNotContain(ComponentIdentifier.of(SnomedTerminologyComponentConstants.REFSET_MEMBER_NUMBER, axiomMember1.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship1.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship2.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship5.getId()),
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship6.getId()));
ComponentIdentifier.of(SnomedTerminologyComponentConstants.RELATIONSHIP_NUMBER, relationship8.getId()));
}

private SnomedRefSetMemberIndexEntry createLanguageRefsetMember(SnomedDescriptionIndexEntry description) {
Expand Down

0 comments on commit f79037c

Please sign in to comment.