diff --git a/snomed/com.b2international.snowowl.snomed.common/src/com/b2international/snowowl/snomed/common/SnomedConstants.java b/snomed/com.b2international.snowowl.snomed.common/src/com/b2international/snowowl/snomed/common/SnomedConstants.java index b4979531921..30685d97a87 100644 --- a/snomed/com.b2international.snowowl.snomed.common/src/com/b2international/snowowl/snomed/common/SnomedConstants.java +++ b/snomed/com.b2international.snowowl.snomed.common/src/com/b2international/snowowl/snomed/common/SnomedConstants.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2023 B2i Healthcare, https://b2ihealthcare.com + * Copyright 2011-2024 B2i Healthcare, https://b2ihealthcare.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -277,6 +277,9 @@ private Concepts() { } //Unapproved attribute hierarchy root public static final String UNAPPROVED_ATTRIBUTE = "408739003"; + + // Concept model attribute hierarchy roots, starting with INT 20240301 + public static final String ANNOTATION_ATTRIBUTE = "1295447006"; // Concepts that require special care when classifying public static final String ROLE_GROUP = "609096000"; diff --git a/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/AllSnomedDatastoreTests.java b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/AllSnomedDatastoreTests.java index c3f631919ab..1549e206524 100644 --- a/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/AllSnomedDatastoreTests.java +++ b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/AllSnomedDatastoreTests.java @@ -29,6 +29,8 @@ import com.b2international.snowowl.snomed.datastore.internal.id.SnomedIdentifierTest; import com.b2international.snowowl.snomed.datastore.internal.id.reservations.ReservationImplTest; import com.b2international.snowowl.snomed.datastore.internal.id.reservations.SnomedIdentifierReservationServiceImplTest; +import com.b2international.snowowl.snomed.datastore.request.SnomedOWLExpressionConverterTest; +import com.b2international.snowowl.snomed.datastore.request.SnomedOWLRelationshipConverterTest; import com.b2international.snowowl.snomed.validation.SnomedQueryValidationRuleEvaluatorTest; /** @@ -67,6 +69,9 @@ SnomedQueryValidationRuleEvaluatorTest.class, // Query optimization SnomedQueryOptimizerTest.class, + // OWL expression conversion tests + SnomedOWLExpressionConverterTest.class, + SnomedOWLRelationshipConverterTest.class, }) public class AllSnomedDatastoreTests { diff --git a/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverterTest.java b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverterTest.java new file mode 100644 index 00000000000..82860c35231 --- /dev/null +++ b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverterTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2024 B2i Healthcare Ltd, http://b2ihealthcare.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.b2international.snowowl.snomed.datastore.request; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.elasticsearch.core.Set; +import org.junit.Test; + +import com.b2international.snowowl.snomed.common.SnomedConstants.Concepts; +import com.b2international.snowowl.snomed.datastore.index.entry.SnomedOWLRelationshipDocument; +import com.google.common.collect.Iterables; + +/** + * @since 7.24.3 + */ +public class SnomedOWLExpressionConverterTest { + + @Test + public void testSubObjectPropertyOf() { + testSubPropertyOf("SubObjectPropertyOf"); + } + + @Test + public void testSubDataPropertyOf() { + testSubPropertyOf("SubDataPropertyOf"); + } + + @Test + public void testSubAnnotationPropertyOf() { + testSubPropertyOf("SubAnnotationPropertyOf"); + } + + private void testSubPropertyOf(String subPropertyOfAxiom) { + SnomedOWLExpressionConverter converter = new SnomedOWLExpressionConverter(() -> Set.of()); + + // XXX: Using nonsensical but valid SCTIDs + SnomedOWLExpressionConverterResult converterResult = converter.toSnomedOWLRelationships( + Concepts.FINDING_SITE, + String.format("%s(:%s :%s)", subPropertyOfAxiom, Concepts.FINDING_SITE, Concepts.AMBIGUOUS)); + + assertNull(converterResult.getGciAxiomRelationships()); + + final List relationships = converterResult.getClassAxiomRelationships(); + assertEquals(1, relationships.size()); + + final SnomedOWLRelationshipDocument relationship = Iterables.getOnlyElement(relationships); + assertEquals(Concepts.IS_A, relationship.getTypeId()); + assertEquals(Concepts.AMBIGUOUS, relationship.getDestinationId()); + } +} diff --git a/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverterTest.java b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverterTest.java new file mode 100644 index 00000000000..f22fbb86cd5 --- /dev/null +++ b/snomed/com.b2international.snowowl.snomed.datastore.tests/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverterTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2024 B2i Healthcare Ltd, http://b2ihealthcare.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.b2international.snowowl.snomed.datastore.request; + +import static org.junit.Assert.assertEquals; + +import java.util.Collection; +import java.util.List; + +import org.elasticsearch.core.Set; +import org.junit.Test; + +import com.b2international.snowowl.snomed.common.SnomedConstants.Concepts; +import com.b2international.snowowl.snomed.datastore.index.entry.SnomedOWLRelationshipDocument; + +/** + * @since 7.24.3 + */ +public class SnomedOWLRelationshipConverterTest { + + @Test + public void testSubClassOf() { + testSubPropertyOf( + "SubClassOf", + Set.of(), + Set.of(), + Set.of(), + Concepts.DEVICE + ); + } + + @Test + public void testSubObjectPropertyOf() { + testSubPropertyOf( + "SubObjectPropertyOf", + Set.of(Long.parseLong(Concepts.CONCEPT_MODEL_OBJECT_ATTRIBUTE)), + Set.of(), + Set.of(), + Concepts.CONCEPT_MODEL_OBJECT_ATTRIBUTE + ); + } + + @Test + public void testSubDataPropertyOf() { + testSubPropertyOf( + "SubDataPropertyOf", + Set.of(), + Set.of(Long.parseLong(Concepts.CONCEPT_MODEL_DATA_ATTRIBUTE)), + Set.of(), + Concepts.CONCEPT_MODEL_DATA_ATTRIBUTE + ); + } + + @Test + public void testSubAnnotationPropertyOf() { + testSubPropertyOf( + "SubAnnotationPropertyOf", + Set.of(), + Set.of(), + Set.of(Long.parseLong(Concepts.ANNOTATION_ATTRIBUTE)), + Concepts.ANNOTATION_ATTRIBUTE + ); + } + + private void testSubPropertyOf( + String subPropertyOfAxiom, + Collection objectAttributes, + Collection dataAttributes, + Collection annotationAttributes, + String destinationId + ) { + SnomedOWLRelationshipConverter converter = new SnomedOWLRelationshipConverter( + Set.of(), + objectAttributes, + dataAttributes, + annotationAttributes); + + // XXX: Using nonsensical but valid SCTIDs + final List owlRelationships = List.of( + SnomedOWLRelationshipDocument.create(Concepts.IS_A, destinationId, 0) + ); + + final String expectedExpression = String.format("%s(:%s :%s)", subPropertyOfAxiom, Concepts.FINDING_SITE, destinationId); + final String actualExpression = converter.fromSnomedOwlRelationships( + false, + true, + Concepts.FINDING_SITE, + owlRelationships + ); + + assertEquals(expectedExpression, actualExpression); + } +} diff --git a/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverter.java b/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverter.java index bdc9874df55..7f10b311e2d 100644 --- a/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverter.java +++ b/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLExpressionConverter.java @@ -68,7 +68,9 @@ public final class SnomedOWLExpressionConverter { "SubClassOf", "EquivalentClasses", "SubObjectPropertyOf", - "SubDataPropertyOf"); + "SubDataPropertyOf", + "SubAnnotationPropertyOf" + ); private final Supplier conversionService; diff --git a/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverter.java b/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverter.java index ae4b37126cb..47a7f10e5d2 100644 --- a/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverter.java +++ b/snomed/com.b2international.snowowl.snomed.datastore/src/com/b2international/snowowl/snomed/datastore/request/SnomedOWLRelationshipConverter.java @@ -48,10 +48,21 @@ public final class SnomedOWLRelationshipConverter { private final Supplier conversionService; - public SnomedOWLRelationshipConverter(final Set ungroupedAttributes, final Collection objectAttributes, final Collection dataAttributes) { + public SnomedOWLRelationshipConverter( + final Set ungroupedAttributes, + final Collection objectAttributes, + final Collection dataAttributes, + final Collection annotationAttributes + ) { this.conversionService = Suppliers.memoize(() -> { final Stopwatch stopwatch = Stopwatch.createStarted(); - final AxiomRelationshipConversionService service = withTccl(() -> new AxiomRelationshipConversionService(ungroupedAttributes, objectAttributes, dataAttributes)); + final AxiomRelationshipConversionService service = withTccl(() -> new AxiomRelationshipConversionService( + ungroupedAttributes, + objectAttributes, + dataAttributes, + annotationAttributes) + ); + LOG.debug("SNOMED OWL Toolkit conversion service initialization took {}", TimeUtil.toString(stopwatch)); return service; });