package org.molgenis.ontology.sorta; import org.molgenis.data.DataService; import org.molgenis.data.Entity; import org.molgenis.data.QueryRule; import org.molgenis.data.meta.model.Attribute; import org.molgenis.data.meta.model.EntityType; import org.molgenis.data.support.DynamicEntity; import org.molgenis.data.support.QueryImpl; import org.molgenis.ontology.core.meta.*; import org.molgenis.ontology.roc.InformationContentService; import org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData; import org.molgenis.ontology.sorta.service.impl.SortaServiceImpl; import org.molgenis.test.data.AbstractMolgenisSpringTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.stream.Stream; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.molgenis.data.QueryRule.Operator.*; import static org.molgenis.data.meta.AttributeType.STRING; import static org.molgenis.ontology.core.meta.OntologyMetaData.ONTOLOGY; import static org.molgenis.ontology.core.meta.OntologyTermDynamicAnnotationMetaData.ONTOLOGY_TERM_DYNAMIC_ANNOTATION; import static org.molgenis.ontology.core.meta.OntologyTermMetaData.ONTOLOGY_TERM; import static org.molgenis.ontology.sorta.meta.OntologyTermHitMetaData.COMBINED_SCORE; import static org.testng.Assert.assertEquals; @ContextConfiguration(classes = { SortaServiceImplTest.Config.class }) public class SortaServiceImplTest extends AbstractMolgenisSpringTest { private static final String ONTOLOGY_IRI = "http://www.molgenis.org/"; @Autowired private SortaServiceImpl sortaServiceImpl; @Autowired private DataService dataService; @Autowired private OntologyFactory ontologyFactory; @Autowired private OntologyTermFactory ontologyTermFactory; @Autowired private OntologyTermSynonymFactory ontologyTermSynonymFactory; @Autowired private OntologyTermDynamicAnnotationFactory ontologyTermDynamicAnnotationFactory; @BeforeClass public void beforeClass() { // Mock ontology entity Ontology ontology = ontologyFactory.create(); ontology.setOntologyIri(ONTOLOGY_IRI); // define dataService actions for test one when(dataService.findOne(ONTOLOGY, new QueryImpl<>().eq(OntologyMetaData.ONTOLOGY_IRI, ONTOLOGY_IRI))) .thenReturn(ontology); when(dataService.count(ONTOLOGY_TERM, new QueryImpl<>().eq(OntologyTermMetaData.ONTOLOGY, ontology))) .thenReturn((long) 100); QueryRule queryRule = new QueryRule( singletonList(new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH, "hear"))); queryRule.setOperator(DIS_MAX); when(dataService.count(ONTOLOGY_TERM, new QueryImpl<>(queryRule))).thenReturn((long) 50); QueryRule queryRule2 = new QueryRule( singletonList(new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH, "impair"))); queryRule2.setOperator(DIS_MAX); when(dataService.count(ONTOLOGY_TERM, new QueryImpl<>(queryRule2))).thenReturn((long) 50); when(dataService.findAll(ONTOLOGY)).thenReturn(Collections.<Entity>singletonList(ontology).stream()); // ########################### TEST ONE ########################### // Mock the first ontology term entity only with name OntologyTermSynonym ontologyTermSynonym0 = ontologyTermSynonymFactory.create(); ontologyTermSynonym0.setOntologyTermSynonym("hearing impairment"); OntologyTerm ontologyTerm0 = ontologyTermFactory.create(); ontologyTerm0.setId("1"); ontologyTerm0.setOntology(ontology); ontologyTerm0.setOntologyTermName("hearing impairment"); ontologyTerm0.setOntologyTermIri(ONTOLOGY_IRI + '1'); ontologyTerm0.setOntologyTermSynonyms(singletonList(ontologyTermSynonym0)); ontologyTerm0.setOntologyTermDynamicAnnotations(emptyList()); // Mock the second ontology term entity only with name OntologyTermSynonym ontologyTermSynonym1 = ontologyTermSynonymFactory.create(); ontologyTermSynonym1.setOntologyTermSynonym("mixed hearing impairment"); OntologyTerm ontologyTerm1 = ontologyTermFactory.create(); ontologyTerm1.setId("2"); ontologyTerm1.setOntology(ontology); ontologyTerm1.setOntologyTermName("mixed hearing impairment"); ontologyTerm1.setOntologyTermIri(ONTOLOGY_IRI + '2'); ontologyTerm1.setOntologyTermSynonyms(singletonList(ontologyTermSynonym1)); ontologyTerm1.setOntologyTermDynamicAnnotations(emptyList()); // DataService action for regular matching ontology term synonyms QueryRule disMaxRegularQueryRule = new QueryRule(singletonList( new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH, "hear~0.8 impair~0.8"))); disMaxRegularQueryRule.setOperator(DIS_MAX); List<QueryRule> finalQueryRules = asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), disMaxRegularQueryRule); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>(finalQueryRules).pageSize(50))) .thenReturn(Arrays.<Entity>asList(ontologyTerm0, ontologyTerm1).stream()); // DataService action for n-gram matching ontology term synonyms QueryRule disMaxNGramQueryRule = new QueryRule(singletonList( new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH_NGRAM, "hear impair"))); disMaxNGramQueryRule.setOperator(DIS_MAX); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>( asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), disMaxNGramQueryRule)).pageSize(10))) .thenReturn(Arrays.<Entity>asList(ontologyTerm0, ontologyTerm1).stream()); // DataService action for querying specific ontology term based on ontologyIRI and ontologyTermIRI when(dataService.findOne(ONTOLOGY_TERM, new QueryImpl<>().eq(OntologyTermMetaData.ONTOLOGY_TERM_IRI, ONTOLOGY_IRI + '1').and() .eq(OntologyTermMetaData.ONTOLOGY, ontology))).thenReturn(ontologyTerm0); when(dataService.findOne(ONTOLOGY_TERM, new QueryImpl<>().eq(OntologyTermMetaData.ONTOLOGY_TERM_IRI, ONTOLOGY_IRI + '2').and() .eq(OntologyTermMetaData.ONTOLOGY, ontology))).thenReturn(ontologyTerm1); // ########################### TEST TWO ########################### OntologyTermSynonym ontologyTermSynonym2 = ontologyTermSynonymFactory.create(); ontologyTermSynonym2.setOntologyTermSynonym("ot_3"); // Mock ontologyTermDynamicAnnotation entities OntologyTermDynamicAnnotation ontologyTermDynamicAnnotation_3_1 = ontologyTermDynamicAnnotationFactory.create(); ontologyTermDynamicAnnotation_3_1.setName("OMIM"); ontologyTermDynamicAnnotation_3_1.setValue("123456"); ontologyTermDynamicAnnotation_3_1.setLabel("OMIM:123456"); // Mock ontologyTerm entity based on the previous entities defined OntologyTerm ontologyTermEntity_3 = ontologyTermFactory.create(); ontologyTermEntity_3.setId("3"); ontologyTermEntity_3.setOntology(ontology); ontologyTermEntity_3.setOntologyTermName("ot_3"); ontologyTermEntity_3.setOntologyTermIri(ONTOLOGY_IRI + '3'); ontologyTermEntity_3.setOntologyTermSynonyms( singletonList(ontologyTermSynonym2)); // self reference intended? Arrays.asList(ontologyTermEntity_3) ontologyTermEntity_3.set(OntologyTermMetaData.ONTOLOGY_TERM_DYNAMIC_ANNOTATION, singletonList(ontologyTermDynamicAnnotation_3_1)); // DataService action for matching ontology term annotation QueryRule annotationQueryRule = new QueryRule( asList(new QueryRule(OntologyTermDynamicAnnotationMetaData.NAME, EQUALS, "OMIM"), new QueryRule(AND), new QueryRule(OntologyTermDynamicAnnotationMetaData.VALUE, EQUALS, "123456"))); when(dataService.findAll(ONTOLOGY_TERM_DYNAMIC_ANNOTATION, new QueryImpl<>(singletonList(annotationQueryRule)).pageSize(Integer.MAX_VALUE))) .thenReturn(Collections.<Entity>singletonList(ontologyTermDynamicAnnotation_3_1).stream()); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>( asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_DYNAMIC_ANNOTATION, IN, singletonList(ontologyTermDynamicAnnotation_3_1)))).pageSize(Integer.MAX_VALUE))) .thenReturn(Collections.<Entity>singletonList(ontologyTermEntity_3).stream()); // DataService action for elasticsearch regular matching ontology term synonyms QueryRule disMaxRegularQueryRule_2 = new QueryRule( singletonList(new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH, "input~0.8"))); disMaxRegularQueryRule_2.setOperator(DIS_MAX); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>( asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), disMaxRegularQueryRule_2)).pageSize(49))).thenReturn(Stream.empty()); // DataService action for n-gram matching ontology term synonyms QueryRule disMaxNGramQueryRule_2 = new QueryRule( singletonList(new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH_NGRAM, "input"))); disMaxNGramQueryRule_2.setOperator(DIS_MAX); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>( asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), disMaxNGramQueryRule_2)).pageSize(10))).thenReturn(Stream.empty()); // ########################### TEST THREE ########################### // Define the input for test three OntologyTermSynonym ontologyTermSynonym_4_1 = ontologyTermSynonymFactory.create(); ontologyTermSynonym_4_1.setOntologyTermSynonym("protruding eye"); OntologyTermSynonym ontologyTermSynonym_4_2 = ontologyTermSynonymFactory.create(); ontologyTermSynonym_4_2.setOntologyTermSynonym("proptosis"); OntologyTermSynonym ontologyTermSynonym_4_3 = ontologyTermSynonymFactory.create(); ontologyTermSynonym_4_3.setOntologyTermSynonym("Exophthalmos"); // Mock ontologyTerm entity based on the previous entities defined OntologyTerm ontologyTermEntity_4 = ontologyTermFactory.create(); ontologyTermEntity_4.setId("4"); ontologyTermEntity_4.setOntology(ontology); ontologyTermEntity_4.setOntologyTermName("protruding eye"); ontologyTermEntity_4.setOntologyTermIri(ONTOLOGY_IRI + '4'); ontologyTermEntity_4.setOntologyTermSynonyms( asList(ontologyTermSynonym_4_1, ontologyTermSynonym_4_2, ontologyTermSynonym_4_3)); ontologyTermEntity_4.setOntologyTermDynamicAnnotations(emptyList()); // DataService action for elasticsearch regular matching ontology term synonyms QueryRule disMaxRegularQueryRule_3 = new QueryRule(singletonList( new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH, "proptosi~0.8 protrud~0.8 ey~0.8 exophthalmo~0.8"))); disMaxRegularQueryRule_3.setOperator(DIS_MAX); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>( asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), disMaxRegularQueryRule_3)).pageSize(50))) .thenReturn(Collections.<Entity>singletonList(ontologyTermEntity_4).stream()); // DataService action for elasticsearch ngram matching ontology term synonyms QueryRule disMaxNGramQueryRule_3 = new QueryRule(singletonList( new QueryRule(OntologyTermMetaData.ONTOLOGY_TERM_SYNONYM, FUZZY_MATCH_NGRAM, "proptosi protrud ey exophthalmo"))); disMaxNGramQueryRule_3.setOperator(QueryRule.Operator.DIS_MAX); when(dataService.findAll(ONTOLOGY_TERM, new QueryImpl<>( asList(new QueryRule(OntologyTermMetaData.ONTOLOGY, EQUALS, ontology), new QueryRule(AND), disMaxNGramQueryRule_3)).pageSize(10))) .thenReturn(Collections.<Entity>singletonList(ontologyTermEntity_4).stream()); } @Test public void findOntologyTermEntities() { Attribute nameAttr = when(mock(Attribute.class).getName()).thenReturn("Name").getMock(); when(nameAttr.getDataType()).thenReturn(STRING); Attribute omimAttr = when(mock(Attribute.class).getName()).thenReturn("OMIM").getMock(); when(omimAttr.getDataType()).thenReturn(STRING); EntityType entityType = mock(EntityType.class); when(entityType.getAtomicAttributes()).thenReturn(asList(nameAttr, omimAttr)); when(entityType.getAttribute("Name")).thenReturn(nameAttr); when(entityType.getAttribute("OMIM")).thenReturn(omimAttr); // Test one: match only the name of input with ontology terms Entity firstInput = new DynamicEntity(entityType); firstInput.set("Name", "hearing impairment"); Iterable<Entity> ontologyTerms_test1 = sortaServiceImpl.findOntologyTermEntities(ONTOLOGY_IRI, firstInput); Iterator<Entity> iterator_test1 = ontologyTerms_test1.iterator(); assertEquals(iterator_test1.hasNext(), true); Entity firstMatch_test1 = iterator_test1.next(); assertEquals(firstMatch_test1.getDouble(COMBINED_SCORE).intValue(), 100); assertEquals(iterator_test1.hasNext(), true); Entity secondMatch_test1 = iterator_test1.next(); assertEquals(secondMatch_test1.getDouble(COMBINED_SCORE).intValue(), new Double(85).intValue()); assertEquals(iterator_test1.hasNext(), false); // Test two: match the database annotation of input with ontology terms Entity secondInput = new DynamicEntity(entityType); secondInput.set("Name", "input"); secondInput.set("OMIM", "123456"); Iterable<Entity> ontologyTerms_test2 = sortaServiceImpl.findOntologyTermEntities(ONTOLOGY_IRI, secondInput); Iterator<Entity> iterator_test2 = ontologyTerms_test2.iterator(); assertEquals(iterator_test2.hasNext(), true); Entity firstMatch_test2 = iterator_test2.next(); assertEquals(firstMatch_test2.getDouble(COMBINED_SCORE).intValue(), 100); assertEquals(iterator_test2.hasNext(), false); // Test three: match only the name of input with ontology terms, since the name contains multiple synonyms // therefore add up all the scores from synonyms Entity thirdInput = new DynamicEntity(entityType); thirdInput.set("Name", "proptosis, protruding eye, Exophthalmos "); Iterable<Entity> ontologyTerms_test3 = sortaServiceImpl.findOntologyTermEntities(ONTOLOGY_IRI, thirdInput); Iterator<Entity> iterator_test3 = ontologyTerms_test3.iterator(); assertEquals(iterator_test3.hasNext(), true); Entity firstMatch_test3 = iterator_test3.next(); assertEquals(firstMatch_test3.getDouble(COMBINED_SCORE).intValue(), 100); assertEquals(iterator_test3.hasNext(), false); } @Test public void getAllOntologyEntities() { Iterable<Entity> allOntologyEntities = sortaServiceImpl.getAllOntologyEntities(); Iterator<Entity> iterator = allOntologyEntities.iterator(); assertEquals(iterator.hasNext(), true); Entity ontologyEntity = iterator.next(); assertEquals(ontologyEntity.getString(OntologyMetaData.ONTOLOGY_IRI), ONTOLOGY_IRI); assertEquals(iterator.hasNext(), false); } @Test public void getOntologyEntity() { Entity ontologyEntity = sortaServiceImpl.getOntologyEntity(ONTOLOGY_IRI); assertEquals(ONTOLOGY_IRI, ontologyEntity.getString(OntologyMetaData.ONTOLOGY_IRI)); } @Test public void getOntologyTermEntity() { Entity firstOntologyTermEntity = sortaServiceImpl.getOntologyTermEntity(ONTOLOGY_IRI + 1, ONTOLOGY_IRI); assertEquals(firstOntologyTermEntity.getString(OntologyTermMetaData.ONTOLOGY_TERM_NAME), "hearing impairment"); Entity secondOntologyTermEntity = sortaServiceImpl.getOntologyTermEntity(ONTOLOGY_IRI + 2, ONTOLOGY_IRI); assertEquals(secondOntologyTermEntity.getString(OntologyTermMetaData.ONTOLOGY_TERM_NAME), "mixed hearing impairment"); } @Configuration @ComponentScan({ "org.molgenis.ontology.core.meta", "org.molgenis.ontology.core.model", "org.molgenis.ontology.sorta.meta", "org.molgenis.data.jobs.model" }) public static class Config { @Autowired public OntologyTermHitMetaData ontologyTermHitMetaData; @Autowired private OntologyTermSynonymFactory ontologyTermSynonymFactory; @Bean public DataService dataService() { return mock(DataService.class); } @Bean public InformationContentService informationContentService() { return mock(InformationContentService.class); } @Bean public SortaServiceImpl sortaServiceImpl() { return new SortaServiceImpl(dataService(), informationContentService(), ontologyTermHitMetaData, ontologyTermSynonymFactory); } } }