diff --git a/profile/graphdb/query/fulltextsearch.rq b/profile/graphdb/query/fulltextsearch.rq index f03cf04ee..b020dc7d5 100644 --- a/profile/graphdb/query/fulltextsearch.rq +++ b/profile/graphdb/query/fulltextsearch.rq @@ -7,8 +7,9 @@ PREFIX inst: PREFIX rdf: PREFIX rdfs: PREFIX dc: +PREFIX skos: -SELECT DISTINCT ?entity ?label ?vocabularyUri ?state ?type ?snippetField ?snippetText ?score { +SELECT DISTINCT ?entity ?label ?description ?vocabularyUri ?state ?type ?snippetField ?snippetText ?score { { ?search a inst:label_index . } @@ -17,12 +18,21 @@ SELECT DISTINCT ?entity ?label ?vocabularyUri ?state ?type ?snippetField ?snippe ?search a inst:defcom_index . } { - ?entity rdfs:label ?label . + ?entity skos:prefLabel ?label . + OPTIONAL { + ?entity skos:definition ?definition . + } + OPTIONAL { + ?entity skos:scopeNote ?scopeNote . + } } UNION { ?entity dc:title ?label . + OPTIONAL { + ?entity dc:description ?dcDescription . + } } ?search :query ?wildCardSearchString ; - :snippetSize 2000 ; + :snippetSize 250 ; :entities ?entity . ?entity a ?type ; :score ?initScore ; @@ -38,7 +48,9 @@ SELECT DISTINCT ?entity ?label ?vocabularyUri ?state ?type ?snippetField ?snippe FILTER (?type = ?term || ?type = ?vocabulary) FILTER NOT EXISTS { ?entity a ?snapshot . } FILTER (lang(?label) = ?langTag) - BIND(IF(lcase(str(?snippetText)) = lcase(str(?splitExactMatch)), ?initScore * 2, IF(CONTAINS(lcase(str(?snippetText)), ?searchString), IF(?snippetField = "label", ?initScore * 1.5, ?initScore), ?initScore)) as ?exactMatchScore) - BIND(IF(?snippetField = "label", ?exactMatchScore * 2, IF(?snippetField = "definition", ?exactMatchScore * 1.2, ?exactMatchScore)) as ?score) + BIND(COALESCE(?definition, COALESCE(?scopeNote, ?dcDescription)) AS ?description) + FILTER (!BOUND(?description) || lang(?description) = ?langTag) + BIND(IF(lcase(str(?snippetText)) = lcase(str(?splitExactMatch)), ?initScore * 2, IF(CONTAINS(lcase(str(?snippetText)), ?searchString), IF(?snippetField = "prefLabel", ?initScore * 1.5, ?initScore), ?initScore)) as ?exactMatchScore) + BIND(IF(?snippetField = "prefLabel", ?exactMatchScore * 2, IF(?snippetField = "definition", ?exactMatchScore * 1.2, ?exactMatchScore)) as ?score) } ORDER BY desc(?score) diff --git a/src/main/java/cz/cvut/kbss/termit/dto/search/FullTextSearchResult.java b/src/main/java/cz/cvut/kbss/termit/dto/search/FullTextSearchResult.java index c107aea5d..76184e013 100644 --- a/src/main/java/cz/cvut/kbss/termit/dto/search/FullTextSearchResult.java +++ b/src/main/java/cz/cvut/kbss/termit/dto/search/FullTextSearchResult.java @@ -26,6 +26,7 @@ import cz.cvut.kbss.jopa.model.annotations.SparqlResultSetMapping; import cz.cvut.kbss.jopa.model.annotations.Types; import cz.cvut.kbss.jopa.model.annotations.VariableResult; +import cz.cvut.kbss.jopa.vocabulary.DC; import cz.cvut.kbss.jopa.vocabulary.RDFS; import cz.cvut.kbss.termit.model.util.HasIdentifier; import cz.cvut.kbss.termit.model.util.HasTypes; @@ -41,6 +42,7 @@ variables = { @VariableResult(name = "entity", type = URI.class), @VariableResult(name = "label", type = String.class), + @VariableResult(name = "description", type = String.class), @VariableResult(name = "vocabularyUri", type = URI.class), @VariableResult(name = "state", type = URI.class), @VariableResult(name = "type", type = String.class), @@ -59,6 +61,10 @@ public class FullTextSearchResult implements HasIdentifier, HasTypes, Serializab @OWLAnnotationProperty(iri = RDFS.LABEL) private String label; + // This could be term definition, scope note or vocabulary description + @OWLAnnotationProperty(iri = DC.Terms.DESCRIPTION) + private String description; + @OWLDataProperty(iri = Vocabulary.ONTOLOGY_IRI_TERMIT + "/fts/snippet-text") private String snippetText; @@ -80,10 +86,11 @@ public class FullTextSearchResult implements HasIdentifier, HasTypes, Serializab public FullTextSearchResult() { } - public FullTextSearchResult(URI uri, String label, URI vocabulary, URI state, String type, String snippetField, - String snippetText, Double score) { + public FullTextSearchResult(URI uri, String label, String description, URI vocabulary, URI state, String type, + String snippetField, String snippetText, Double score) { this.uri = uri; this.label = label; + this.description = description; this.vocabulary = vocabulary; this.state = state; this.types = Collections.singleton(type); @@ -110,6 +117,14 @@ public void setLabel(String label) { this.label = label; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public URI getVocabulary() { return vocabulary; } diff --git a/src/main/resources/query/fulltextsearch.rq b/src/main/resources/query/fulltextsearch.rq index b6a2255b6..fc4050588 100644 --- a/src/main/resources/query/fulltextsearch.rq +++ b/src/main/resources/query/fulltextsearch.rq @@ -15,15 +15,26 @@ SELECT ?entity ?label ?vocabularyUri ?state ?type ?snippetField ?snippetText WHE skos:prefLabel ?label ; ?inVocabulary ?vocabularyUri . OPTIONAL { ?entity ?hasState ?state . } + OPTIONAL { + ?entity skos:definition ?definition . + } + OPTIONAL { + ?entity skos:scopeNote ?scopeNote . + } BIND (?term as ?type) . } UNION { ?entity a ?vocabulary ; dc:title ?label . + OPTIONAL { + ?entity dc:description ?dcDescription . + } BIND (?vocabulary as ?type) . } BIND (?label as ?snippetText) . - BIND (str("label") as ?snippetField) . + BIND (str("prefLabel") as ?snippetField) . FILTER CONTAINS(LCASE(?label), LCASE(?searchString)) . FILTER (lang(?label) = ?langTag) FILTER NOT EXISTS { ?entity a ?snapshot . } + BIND(COALESCE(?definition, COALESCE(?scopeNote, ?dcDescription)) AS ?description) + FILTER (!BOUND(?description) || lang(?description) = ?langTag) } ORDER BY ?label diff --git a/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java b/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java index d8abed694..a25fb2c9c 100644 --- a/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java +++ b/src/test/java/cz/cvut/kbss/termit/rest/SearchControllerTest.java @@ -75,7 +75,7 @@ void setUp() { void fullTextSearchExecutesSearchOnService() throws Exception { final List expected = Collections .singletonList( - new FullTextSearchResult(Generator.generateUri(), "test", null, null, SKOS.CONCEPT, + new FullTextSearchResult(Generator.generateUri(), "test", null, null, null, SKOS.CONCEPT, "test", "test", 1.0)); when(searchServiceMock.fullTextSearch(any())).thenReturn(expected); final String searchString = "test"; @@ -95,7 +95,7 @@ void fullTextSearchExecutesSearchOnService() throws Exception { void fullTextSearchOfTermsWithoutVocabularySpecificationExecutesSearchOnService() throws Exception { final URI vocabularyIri = URI.create("https://test.org/vocabulary"); final List expected = Collections - .singletonList(new FullTextSearchResult(Generator.generateUri(), "test", vocabularyIri, null, + .singletonList(new FullTextSearchResult(Generator.generateUri(), "test", "Term definition", vocabularyIri, null, SKOS.CONCEPT, "test", "test", 1.0)); when(searchServiceMock.fullTextSearchOfTerms(any(), any())).thenReturn(expected); final String searchString = "test"; diff --git a/src/test/java/cz/cvut/kbss/termit/service/business/SearchServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/business/SearchServiceTest.java index a4e7fcccb..9c8c8d6dc 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/business/SearchServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/business/SearchServiceTest.java @@ -68,6 +68,7 @@ void fullTextSearchFiltersResultsFromNonMatchingVocabularies() { final FullTextSearchResult ftsr = new FullTextSearchResult( Generator.generateUri(), "test", + "Term definition", vocabulary, null, SKOS.CONCEPT, @@ -88,6 +89,7 @@ void fullTextSearchReturnsResultsFromMatchingVocabularies() { final FullTextSearchResult ftsr = new FullTextSearchResult( Generator.generateUri(), "test", + "Term definition", vocabulary, Generator.generateUri(), SKOS.CONCEPT, diff --git a/src/test/java/cz/cvut/kbss/termit/service/security/authorization/SearchAuthorizationServiceTest.java b/src/test/java/cz/cvut/kbss/termit/service/security/authorization/SearchAuthorizationServiceTest.java index 9ffe2c3f3..641c64716 100644 --- a/src/test/java/cz/cvut/kbss/termit/service/security/authorization/SearchAuthorizationServiceTest.java +++ b/src/test/java/cz/cvut/kbss/termit/service/security/authorization/SearchAuthorizationServiceTest.java @@ -45,10 +45,11 @@ class SearchAuthorizationServiceTest { @Test void canReadChecksIfVocabularyIsReadableForTermResult() { - final FullTextSearchResult res = new FullTextSearchResult(Generator.generateUri(), "test string", - Generator.generateUri(), Generator.generateUri(), - SKOS.CONCEPT, "label", "test", - (double) Generator.randomInt()); + final FullTextSearchResult res = new FullTextSearchResult(Generator.generateUri(), "Term label", + "Term definition", + Generator.generateUri(), Generator.generateUri(), + SKOS.CONCEPT, "label", "test", + (double) Generator.randomInt()); when(vocabularyAuthorizationService.canRead(any(Vocabulary.class))).thenReturn(true); assertTrue(sut.canRead(res)); verify(vocabularyAuthorizationService).canRead(new Vocabulary(res.getVocabulary())); @@ -56,11 +57,12 @@ void canReadChecksIfVocabularyIsReadableForTermResult() { @Test void canReadChecksIfVocabularyIsReadableForVocabularyResult() { - final FullTextSearchResult res = new FullTextSearchResult(Generator.generateUri(), "test label", - null, null, - cz.cvut.kbss.termit.util.Vocabulary.s_c_slovnik, - "label", "test", - (double) Generator.randomInt()); + final FullTextSearchResult res = new FullTextSearchResult(Generator.generateUri(), "Vocabulary title", + "Vocabulary description", + null, null, + cz.cvut.kbss.termit.util.Vocabulary.s_c_slovnik, + "label", "test", + (double) Generator.randomInt()); assertFalse(sut.canRead(res)); verify(vocabularyAuthorizationService).canRead(new Vocabulary(res.getUri())); }