diff --git a/db/schema.sql b/db/schema.sql
index 7d787dd..65fb0f6 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -37,6 +37,7 @@ CREATE TABLE dict.concept_node (
CONCEPT_TYPE VARCHAR(32) NOT NULL DEFAULT 'Interior',
CONCEPT_PATH VARCHAR(10000) NOT NULL DEFAULT 'INVALID',
PARENT_ID INT,
+ SEARCHABLE_FIELDS TSVECTOR,
PRIMARY KEY (CONCEPT_NODE_ID),
CONSTRAINT fk_parent FOREIGN KEY (PARENT_ID) REFERENCES dict.CONCEPT_NODE(CONCEPT_NODE_ID),
CONSTRAINT fk_study FOREIGN KEY (DATASET_ID) REFERENCES dict.dataset(DATASET_ID)
diff --git a/pom.xml b/pom.xml
index ccad679..92114a3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,11 @@
spring-boot-starter-test
test
+
+ org.json
+ json
+ 20240303
+
org.springframework.boot
spring-boot-testcontainers
diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java
index 6307c8e..3743710 100644
--- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java
+++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepository.java
@@ -40,13 +40,15 @@ public List getConcepts(Filter filter, Pageable pageable) {
concept_node.*,
ds.REF as dataset,
continuous_min.VALUE as min, continuous_max.VALUE as max,
- categorical_values.VALUE as values
+ categorical_values.VALUE as values,
+ meta_description.VALUE AS description
FROM
concept_node
LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id
- LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'MIN'
- LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'MAX'
- LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'VALUES'
+ LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description'
+ LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min'
+ LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max'
+ LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values'
WHERE concept_node.concept_node_id IN
""";
QueryParamPair filterQ = filterGen.generateFilterQuery(filter, pageable);
@@ -69,13 +71,15 @@ public Optional getConcept(String dataset, String conceptPath) {
concept_node.*,
ds.REF as dataset,
continuous_min.VALUE as min, continuous_max.VALUE as max,
- categorical_values.VALUE as values
+ categorical_values.VALUE as values,
+ meta_description.VALUE AS description
FROM
concept_node
LEFT JOIN dataset AS ds ON concept_node.dataset_id = ds.dataset_id
- LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'MIN'
- LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'MAX'
- LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'VALUES'
+ LEFT JOIN concept_node_meta AS meta_description ON concept_node.concept_node_id = meta_description.concept_node_id AND meta_description.KEY = 'description'
+ LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min'
+ LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max'
+ LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values'
WHERE
concept_node.concept_path = :conceptPath
AND ds.REF = :dataset
diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRowMapper.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRowMapper.java
index b5671bd..fc632d0 100644
--- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRowMapper.java
+++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRowMapper.java
@@ -1,6 +1,8 @@
package edu.harvard.dbmi.avillach.dictionary.concept;
import edu.harvard.dbmi.avillach.dictionary.concept.model.*;
+import org.json.JSONArray;
+import org.json.JSONException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@@ -24,7 +26,7 @@ public Concept mapRow(ResultSet rs, int rowNum) throws SQLException {
private CategoricalConcept mapCategorical(ResultSet rs) throws SQLException {
return new CategoricalConcept(
rs.getString("concept_path"), rs.getString("name"),
- rs.getString("display"), rs.getString("dataset"),
+ rs.getString("display"), rs.getString("dataset"), rs.getString("description"),
rs.getString("values") == null ? List.of() : List.of(rs.getString("values").split(",")),
null,
null
@@ -34,9 +36,27 @@ private CategoricalConcept mapCategorical(ResultSet rs) throws SQLException {
private ContinuousConcept mapContinuous(ResultSet rs) throws SQLException {
return new ContinuousConcept(
rs.getString("concept_path"), rs.getString("name"),
- rs.getString("display"), rs.getString("dataset"),
- rs.getInt("min"), rs.getInt("max"),
+ rs.getString("display"), rs.getString("dataset"), rs.getString("description"),
+ parseMin(rs.getString("values")), parseMax(rs.getString("values")),
null
);
}
+
+ private Integer parseMin(String valuesArr) {
+ try {
+ JSONArray arr = new JSONArray(valuesArr);
+ return arr.length() == 2 ? arr.getInt(0) : 0;
+ } catch (JSONException ex) {
+ return 0;
+ }
+ }
+
+ private Integer parseMax(String valuesArr) {
+ try {
+ JSONArray arr = new JSONArray(valuesArr);
+ return arr.length() == 2 ? arr.getInt(1) : 0;
+ } catch (JSONException ex) {
+ return 0;
+ }
+ }
}
diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/CategoricalConcept.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/CategoricalConcept.java
index f700424..26b8485 100644
--- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/CategoricalConcept.java
+++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/CategoricalConcept.java
@@ -1,12 +1,13 @@
package edu.harvard.dbmi.avillach.dictionary.concept.model;
+import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Map;
public record CategoricalConcept(
- String conceptPath, String name, String display, String dataset,
+ String conceptPath, String name, String display, String dataset, String description,
List values,
@@ -19,10 +20,11 @@ public record CategoricalConcept(
) implements Concept {
public CategoricalConcept(CategoricalConcept core, Map meta) {
- this(core.conceptPath, core.name, core.display, core.dataset, core.values, core.children, meta);
+ this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.values, core.children, meta);
}
+ @JsonProperty("type")
@Override
public ConceptType type() {
return ConceptType.Categorical;
diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ContinuousConcept.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ContinuousConcept.java
index db338b3..3b6c93c 100644
--- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ContinuousConcept.java
+++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ContinuousConcept.java
@@ -1,20 +1,22 @@
package edu.harvard.dbmi.avillach.dictionary.concept.model;
+import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nullable;
import java.util.Map;
public record ContinuousConcept(
- String conceptPath, String name, String display, String dataset,
+ String conceptPath, String name, String display, String dataset, String description,
@Nullable Integer min, @Nullable Integer max,
Map meta
) implements Concept {
public ContinuousConcept(ContinuousConcept core, Map meta) {
- this(core.conceptPath, core.name, core.display, core.dataset, core.min, core.max, meta);
+ this(core.conceptPath, core.name, core.display, core.dataset, core.description, core.min, core.max, meta);
}
+ @JsonProperty("type")
@Override
public ConceptType type() {
return ConceptType.Continuous;
diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java
index 5a9a738..2921dec 100644
--- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java
+++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/facet/FacetRepository.java
@@ -46,7 +46,7 @@ public List getFacets(Filter filter) {
facet
LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id
LEFT JOIN facet as parent_facet ON facet.parent_id = parent_facet.facet_id
- LEFT JOIN (
+ INNER JOIN (
SELECT
count(*) as facet_count, inner_facet_q.facet_id AS inner_facet_id
FROM
diff --git a/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/FilterQueryGenerator.java b/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/FilterQueryGenerator.java
index a159b0d..bfcfc59 100644
--- a/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/FilterQueryGenerator.java
+++ b/src/main/java/edu/harvard/dbmi/avillach/dictionary/filter/FilterQueryGenerator.java
@@ -34,9 +34,23 @@ public QueryParamPair generateFilterQuery(Filter filter, Pageable pageable) {
if (StringUtils.hasText(filter.search())) {
clauses.add(createSearchFilter(filter.search(), params));
}
- if (clauses.isEmpty()) {
- clauses = List.of("\tSELECT concept_node.concept_node_id FROM concept_node\n");
- }
+ clauses.add("""
+ (
+ SELECT
+ concept_node.concept_node_id
+ FROM
+ concept_node
+ LEFT JOIN concept_node_meta AS continuous_min ON concept_node.concept_node_id = continuous_min.concept_node_id AND continuous_min.KEY = 'min'
+ LEFT JOIN concept_node_meta AS continuous_max ON concept_node.concept_node_id = continuous_max.concept_node_id AND continuous_max.KEY = 'max'
+ LEFT JOIN concept_node_meta AS categorical_values ON concept_node.concept_node_id = categorical_values.concept_node_id AND categorical_values.KEY = 'values'
+ WHERE
+ continuous_min.value <> '' OR
+ continuous_max.value <> '' OR
+ categorical_values.value <> ''
+ )
+ """
+ );
+
String query = "(\n" + String.join("\n\tINTERSECT\n", clauses) + "\n) ORDER BY concept_node_id\n";
if (pageable.isPaged()) {
@@ -53,7 +67,7 @@ public QueryParamPair generateFilterQuery(Filter filter, Pageable pageable) {
}
private String createSearchFilter(String search, MapSqlParameterSource params) {
- params.addValue("search", "%" + search + "%");
+ params.addValue("search", search);
return """
(
SELECT
@@ -61,7 +75,7 @@ private String createSearchFilter(String search, MapSqlParameterSource params) {
FROM
concept_node
WHERE
- concept_node.concept_path LIKE :search
+ concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery
)
""";
}
diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java
index b6baef6..878fe3f 100644
--- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java
+++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptControllerTest.java
@@ -32,9 +32,9 @@ class ConceptControllerTest {
@Test
void shouldListConcepts() {
List expected = List.of(
- new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", List.of(), null, Map.of()),
- new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", List.of("a", "b"), List.of(), Map.of()),
- new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", 0, 100, Map.of())
+ new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), null, Map.of()),
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), List.of(), Map.of()),
+ new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", 0, 100, Map.of())
);
Filter filter = new Filter(
List.of(new Facet("questionare", "Questionare", "?", 1, null, "category", null)),
@@ -54,7 +54,7 @@ void shouldListConcepts() {
@Test
void shouldGetConceptDetails() {
CategoricalConcept expected =
- new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", List.of("a", "b"), List.of(), Map.of());
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), List.of(), Map.of());
Mockito.when(conceptService.conceptDetail("my_dataset", "/foo//bar"))
.thenReturn(Optional.of(expected));
@@ -77,11 +77,11 @@ void shouldNotGetConceptDetails() {
@Test
void shouldGetConceptTree() {
Concept fooBar =
- new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", List.of("a", "b"), List.of(), Map.of());
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), List.of(), Map.of());
Concept fooBaz =
- new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", 0, 100, Map.of());
+ new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", 0, 100, Map.of());
CategoricalConcept foo =
- new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", List.of(), List.of(fooBar, fooBaz), Map.of());
+ new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), List.of(fooBar, fooBaz), Map.of());
Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1))
.thenReturn(Optional.of(foo));
@@ -95,11 +95,11 @@ void shouldGetConceptTree() {
@Test
void shouldGetNotConceptTreeForLargeDepth() {
Concept fooBar =
- new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", List.of("a", "b"), List.of(), Map.of());
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), List.of(), Map.of());
Concept fooBaz =
- new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", 0, 100, Map.of());
+ new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", 0, 100, Map.of());
CategoricalConcept foo =
- new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", List.of(), List.of(fooBar, fooBaz), Map.of());
+ new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), List.of(fooBar, fooBaz), Map.of());
Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1))
.thenReturn(Optional.of(foo));
@@ -113,11 +113,11 @@ void shouldGetNotConceptTreeForLargeDepth() {
@Test
void shouldGetNotConceptTreeForNegativeDepth() {
Concept fooBar =
- new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", List.of("a", "b"), List.of(), Map.of());
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), List.of(), Map.of());
Concept fooBaz =
- new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", 0, 100, Map.of());
+ new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", 0, 100, Map.of());
CategoricalConcept foo =
- new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", List.of(), List.of(fooBar, fooBaz), Map.of());
+ new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), List.of(fooBar, fooBaz), Map.of());
Mockito.when(conceptService.conceptTree("my_dataset", "/foo", -1))
.thenReturn(Optional.of(foo));
@@ -130,11 +130,11 @@ void shouldGetNotConceptTreeForNegativeDepth() {
@Test
void shouldNotGetConceptTreeWhenConceptDNE() {
Concept fooBar =
- new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", List.of("a", "b"), List.of(), Map.of());
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "my_dataset", "foo!", List.of("a", "b"), List.of(), Map.of());
Concept fooBaz =
- new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", 0, 100, Map.of());
+ new ContinuousConcept("/foo//baz", "baz", "Baz", "my_dataset", "foo!", 0, 100, Map.of());
CategoricalConcept foo =
- new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", List.of(), List.of(fooBar, fooBaz), Map.of());
+ new CategoricalConcept("/foo", "foo", "Foo", "my_dataset", "foo!", List.of(), List.of(fooBar, fooBaz), Map.of());
Mockito.when(conceptService.conceptTree("my_dataset", "/foo", 1))
.thenReturn(Optional.of(foo));
diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepositoryTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepositoryTest.java
index ba46693..7ea3b5a 100644
--- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepositoryTest.java
+++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptRepositoryTest.java
@@ -48,21 +48,21 @@ static void mySQLProperties(DynamicPropertyRegistry registry) {
void shouldListAllConcepts() {
List actual = subject.getConcepts(new Filter(List.of(), ""), Pageable.unpaged());
List extends Record> expected = List.of(
- new CategoricalConcept("\\\\\\\\A\\\\\\\\", "a", "A", "invalid.invalid", List.of("0", "1"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\", "1", "1", "invalid.invalid", List.of("X", "Z"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", List.of("X", "Y"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("foo", "bar"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", List.of("foo", "bar", "baz"), null, null),
- new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", 0, 0, null),
- new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", 0, 0, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\", "b", "B", "invalid.invalid", List.of("0", "2"), null, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", List.of("X", "Y", "Z"), null, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\", "2", "2", "invalid.invalid", List.of("Y", "Z"), null, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("bar", "baz"), null, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", List.of("bar", "baz", "qux"), null, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", List.of("foo", "bar", "baz", "qux"), null, null),
- new ContinuousConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", 0, 0, null),
- new ContinuousConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", 0, 0, null)
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\", "a", "A", "invalid.invalid", null, List.of("0", "1"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\", "1", "1", "invalid.invalid", null, List.of("X", "Z"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", null, List.of("X", "Y"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("foo", "bar"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", null, List.of("foo", "bar", "baz"), null, null),
+ new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, 0, 0, null),
+ new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", null, 0, 0, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\", "b", "B", "invalid.invalid", null, List.of("0", "2"), null, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", null, List.of("X", "Y", "Z"), null, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\", "2", "2", "invalid.invalid", null, List.of("Y", "Z"), null, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("bar", "baz"), null, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", null, List.of("bar", "baz", "qux"), null, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", null, List.of("foo", "bar", "baz", "qux"), null, null),
+ new ContinuousConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", null, 0, 0, null),
+ new ContinuousConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", null, 0, 0, null)
);
Assertions.assertEquals(expected, actual);
@@ -72,8 +72,8 @@ void shouldListAllConcepts() {
void shouldListFirstTwoConcepts() {
List actual = subject.getConcepts(new Filter(List.of(), ""), Pageable.ofSize(2).first());
List extends Record> expected = List.of(
- new CategoricalConcept("\\\\\\\\A\\\\\\\\", "a", "A", "invalid.invalid", List.of("0", "1"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\", "1", "1", "invalid.invalid", List.of("X", "Z"), null, null)
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\", "a", "A", "invalid.invalid", null, List.of("0", "1"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\", "1", "1", "invalid.invalid", null, List.of("X", "Z"), null, null)
);
Assertions.assertEquals(expected, actual);
@@ -83,8 +83,8 @@ void shouldListFirstTwoConcepts() {
void shouldListNextTwoConcepts() {
List actual = subject.getConcepts(new Filter(List.of(), ""), Pageable.ofSize(2).first().next());
List extends Record> expected = List.of(
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", List.of("X", "Y"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("foo", "bar"), null, null)
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", null, List.of("X", "Y"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("foo", "bar"), null, null)
);
Assertions.assertEquals(expected, actual);
@@ -95,13 +95,13 @@ void shouldFilterConceptsByFacet() {
List actual =
subject.getConcepts(new Filter(List.of(new Facet("bch", "", "", 1, null, "site", null)), ""), Pageable.unpaged());
List extends Record> expected = List.of(
- new CategoricalConcept("\\\\\\\\A\\\\\\\\", "a", "A", "invalid.invalid", List.of("0", "1"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\", "1", "1", "invalid.invalid", List.of("X", "Z"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", List.of("X", "Y"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("foo", "bar"), null, null),
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", List.of("foo", "bar", "baz"), null, null),
- new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", 0, 0, null),
- new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", 0, 0, null)
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\", "a", "A", "invalid.invalid", null, List.of("0", "1"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\", "1", "1", "invalid.invalid", null, List.of("X", "Z"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\", "0", "0", "invalid.invalid", null, List.of("X", "Y"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("foo", "bar"), null, null),
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\Y\\\\\\\\", "y", "Y", "invalid.invalid", null, List.of("foo", "bar", "baz"), null, null),
+ new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, 0, 0, null),
+ new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", null, 0, 0, null)
);
Assertions.assertEquals(expected, actual);
@@ -111,9 +111,9 @@ void shouldFilterConceptsByFacet() {
void shouldFilterBySearch() {
List actual = subject.getConcepts(new Filter(List.of(), "X"), Pageable.unpaged());
List extends Record> expected = List.of(
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("foo", "bar"), null, null),
- new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", 0, 0, null),
- new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("bar", "baz"), null, null)
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("foo", "bar"), null, null),
+ new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, 0, 0, null),
+ new CategoricalConcept("\\\\\\\\B\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("bar", "baz"), null, null)
);
Assertions.assertEquals(expected, actual);
@@ -124,8 +124,8 @@ void shouldFilterByBothSearchAndFacet() {
List actual =
subject.getConcepts(new Filter(List.of(new Facet("bch", "", "", 1, null, "site", null)), "X"), Pageable.unpaged());
List extends Record> expected = List.of(
- new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", List.of("foo", "bar"), null, null),
- new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", 0, 0, null)
+ new CategoricalConcept("\\\\\\\\A\\\\\\\\0\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, List.of("foo", "bar"), null, null),
+ new ContinuousConcept("\\\\\\\\A\\\\\\\\1\\\\\\\\X\\\\\\\\", "x", "X", "invalid.invalid", null, 0, 0, null)
);
Assertions.assertEquals(expected, actual);
@@ -147,7 +147,7 @@ void shouldGetCountWithFilter() {
@Test
void shouldGetDetailForConcept() {
ContinuousConcept expected =
- new ContinuousConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", 0, 0, null);
+ new ContinuousConcept("\\\\\\\\B\\\\\\\\2\\\\\\\\Z\\\\\\\\", "z", "Z", "invalid.invalid", null, 0, 0, null);
Optional actual = subject.getConcept(expected.dataset(), expected.conceptPath());
Assertions.assertEquals(Optional.of(expected), actual);
diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java
index 22113cc..f47920f 100644
--- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java
+++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/ConceptServiceTest.java
@@ -29,7 +29,7 @@ class ConceptServiceTest {
@Test
void shouldListConcepts() {
List expected = List.of(
- new CategoricalConcept("A", "a", "A", "invalid.invalid", List.of(), null, null)
+ new CategoricalConcept("A", "a", "A", "invalid.invalid", null, List.of(), null, null)
);
Filter filter = new Filter(List.of(), "");
Pageable page = Pageable.ofSize(10).first();
@@ -54,7 +54,7 @@ void shouldCountConcepts() {
@Test
void shouldShowDetailForContinuous() {
- ContinuousConcept concept = new ContinuousConcept("path", "", "", "dataset", 0, 1, null);
+ ContinuousConcept concept = new ContinuousConcept("path", "", "", "dataset", null, 0, 1, null);
Map meta = Map.of("MIN", "0", "MAX", "1", "stigmatizing", "true");
Mockito.when(repository.getConcept("dataset", "path"))
.thenReturn(Optional.of(concept));
@@ -69,7 +69,7 @@ void shouldShowDetailForContinuous() {
@Test
void shouldShowDetailForCategorical() {
- CategoricalConcept concept = new CategoricalConcept("path", "", "", "dataset", List.of("a"), List.of(), null);
+ CategoricalConcept concept = new CategoricalConcept("path", "", "", "dataset", null, List.of("a"), List.of(), null);
Map meta = Map.of("VALUES", "a", "stigmatizing", "true");
Mockito.when(repository.getConcept("dataset", "path"))
.thenReturn(Optional.of(concept));
diff --git a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java
index 45ac68e..c48b501 100644
--- a/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java
+++ b/src/test/java/edu/harvard/dbmi/avillach/dictionary/concept/model/ConceptTest.java
@@ -14,7 +14,7 @@ class ConceptTest {
@Test
void shouldRoundTrip() throws JsonProcessingException {
- Concept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", List.of("a", "b"), List.of(), Map.of());
+ Concept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), List.of(), Map.of());
String json = objectMapper.writeValueAsString(expected);
Concept actual = objectMapper.readValue(json, Concept.class);
@@ -37,7 +37,7 @@ void shouldReadCategorical() throws JsonProcessingException {
}
""";
- CategoricalConcept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", List.of("a", "b"), null, Map.of());
+ CategoricalConcept expected = new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), null, Map.of());
Concept actual = new ObjectMapper().readValue(json, Concept.class);
Assertions.assertEquals(expected, actual);
@@ -60,10 +60,23 @@ void shouldReadContinuous() throws JsonProcessingException {
}
""";
- ContinuousConcept expected = new ContinuousConcept("/foo//baz", "baz", "Baz", "study_a", 0, 1, Map.of());
+ ContinuousConcept expected = new ContinuousConcept("/foo//baz", "baz", "Baz", "study_a", null, 0, 1, Map.of());
Concept actual = new ObjectMapper().readValue(json, Concept.class);
Assertions.assertEquals(expected, actual);
Assertions.assertEquals(ConceptType.Continuous, actual.type());
}
+
+ @Test
+ void shouldIncludeTypeInList() throws JsonProcessingException {
+ List concepts = List.of(
+ new ContinuousConcept("/foo//baz", "baz", "Baz", "study_a", null, 0, 1, Map.of()),
+ new CategoricalConcept("/foo//bar", "bar", "Bar", "study_a", null, List.of("a", "b"), null, Map.of())
+ );
+
+ String actual = new ObjectMapper().writeValueAsString(concepts);
+ String expected = "";
+
+ Assertions.assertEquals(expected, actual);
+ }
}
\ No newline at end of file