Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json request response #12

Merged
merged 1 commit into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
705 changes: 705 additions & 0 deletions JSONS.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ services:
- dictionary-db
restart: always
env_file: .env
ports:
- "8080:8080"
networks:
- dictionary
- hpdsNet
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20240303</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,20 @@ public ResponseEntity<Page<Concept>> listConcepts(
return ResponseEntity.ok(pageResp);
}

@GetMapping(path = "/concepts/detail/{dataset}/{conceptPath}")
@PostMapping(path = "/concepts/detail/{dataset}")
public ResponseEntity<Concept> conceptDetail(
@PathVariable(name = "dataset") String dataset,
@PathVariable(name = "conceptPath") String conceptPath
@RequestBody() String conceptPath
) {
return conceptService.conceptDetail(dataset, conceptPath)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@GetMapping(path = "/concepts/tree/{dataset}/{conceptPath}")
@PostMapping(path = "/concepts/tree/{dataset}")
public ResponseEntity<Concept> conceptTree(
@PathVariable(name = "dataset") String dataset,
@PathVariable(name = "conceptPath") String conceptPath,
@RequestBody() String conceptPath,
@RequestParam(name = "depth", required = false, defaultValue = "2") Integer depth
) {
if (depth < 0 || depth > MAX_DEPTH) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ public List<Concept> 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);
Expand All @@ -69,13 +71,15 @@ public Optional<Concept> 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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
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;

import java.sql.ResultSet;
import java.sql.SQLException;
Expand All @@ -14,7 +17,7 @@ public class ConceptRowMapper implements RowMapper<Concept> {

@Override
public Concept mapRow(ResultSet rs, int rowNum) throws SQLException {
return switch (ConceptType.valueOf(rs.getString("concept_type"))) {
return switch (ConceptType.toConcept(rs.getString("concept_type"))) {
case Categorical -> mapCategorical(rs);
case Continuous -> mapContinuous(rs);
};
Expand All @@ -23,8 +26,8 @@ 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"),
List.of(rs.getString("values").split(",")),
rs.getString("display"), rs.getString("dataset"), rs.getString("description"),
rs.getString("values") == null ? List.of() : List.of(rs.getString("values").split(",")),
null,
null
);
Expand All @@ -33,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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String> values,

Expand All @@ -19,10 +20,11 @@ public record CategoricalConcept(
) implements Concept {

public CategoricalConcept(CategoricalConcept core, Map<String, String> meta) {
this(core.conceptPath, core.name, core.display, core.dataset, core.values, core.children, core.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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package edu.harvard.dbmi.avillach.dictionary.concept.model;

import org.springframework.util.StringUtils;

public enum ConceptType {
/**
* i.e. Eye color: brown, blue, hazel, etc.
Expand All @@ -10,6 +12,13 @@ public enum ConceptType {
* i.e. Age: 0 - 150
* Also known as numeric (to me)
*/
Continuous,
Continuous;

public static ConceptType toConcept(String in) {
return switch (StringUtils.capitalize(in)) {
case "Continuous" -> Continuous;
default -> Categorical;
};
}

}
Original file line number Diff line number Diff line change
@@ -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<String, String> meta
) implements Concept {

public ContinuousConcept(ContinuousConcept core, Map<String, String> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public List<FacetCategory> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, 0 as rank
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()) {
Expand All @@ -48,20 +62,25 @@ public QueryParamPair generateFilterQuery(Filter filter, Pageable pageable) {
.addValue("offset", pageable.getOffset());
}

String superQuery = """
WITH q AS (%s) SELECT concept_node_id FROM q GROUP BY concept_node_id ORDER BY sum(rank) DESC"
""".formatted(query);


return new QueryParamPair(query, params);
return new QueryParamPair(superQuery, params);
}

private String createSearchFilter(String search, MapSqlParameterSource params) {
params.addValue("search", "%" + search + "%");
params.addValue("search", search);
return """
(
SELECT
concept_node.concept_node_id AS concept_node_id
concept_node.concept_node_id AS concept_node_id,
ts_rank(searchable_fields, (phraseto_tsquery(:search)::text || ':*')::tsquery) as rank
FROM
concept_node
WHERE
concept_node.concept_path LIKE :search
concept_node.searchable_fields @@ (phraseto_tsquery(:search)::text || ':*')::tsquery
)
""";
}
Expand All @@ -78,7 +97,7 @@ private List<String> createFacetFilter(List<Facet> facets, MapSqlParameterSource
return """
(
SELECT
facet__concept_node.concept_node_id AS concept_node_id
facet__concept_node.concept_node_id AS concept_node_id , 0 as rank
FROM facet
LEFT JOIN facet__concept_node ON facet__concept_node.facet_id = facet.facet_id
LEFT JOIN facet_category ON facet_category.facet_category_id = facet.facet_category_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public MapExtractor(String keyName, String valueName) {
@Override
public Map<String, String> extractData(ResultSet rs) throws SQLException, DataAccessException {
Map<String, String> map = new HashMap<>();
while (rs.next()) {
while (rs.next() && rs.getString(keyName) != null) {
map.put(rs.getString(keyName), rs.getString(valueName));
}
return map;
Expand Down
6 changes: 4 additions & 2 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
spring.application.name=dictionary
spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST}:5432/${POSTGRES_DB}
spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST}:5432/${POSTGRES_DB}?currentSchema=dict
spring.datasource.username=${POSTGRES_USER}
spring.datasource.password=${POSTGRES_PASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.driver-class-name=org.postgresql.Driver
server.port=80

Loading