Skip to content

Commit

Permalink
Support label_fieldName templates, e.g. *_dateOfBirth (RPB-169)
Browse files Browse the repository at this point in the history
For autocomplete format: https://lobid.org/gnd/api#auto-complete,
e.g. `format=json:preferredName,*_dateOfBirth+in_placeOfBirth`
  • Loading branch information
fsteeg committed Jun 3, 2024
1 parent 92d96db commit 88da5ae
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 18 deletions.
11 changes: 5 additions & 6 deletions app/controllers/HomeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ public Result search(String q, String filter, String sort, int from, int size, S
}
} catch (Throwable t) {
String message = t.getMessage() + (t.getCause() != null ? ", cause: " + t.getCause().getMessage() : "");
Logger.error("Error: {}", message);
Logger.error("Error: " + message, t);
return internalServerError(views.html.error.render(q, "Error: " + message));
}
}
Expand Down Expand Up @@ -406,17 +406,16 @@ static String toSuggestions(JsonNode json, String labelFields) {
"placeOfBusiness", "firstAuthor", "firstComposer", "dateOfProduction");
String fields = labelFields.equals("suggest") ? defaultFields.collect(Collectors.joining(",")) : labelFields;
Stream<JsonNode> documents = Lists.newArrayList(json.elements()).stream();
Stream<JsonNode> suggestions = documents.map((JsonNode document) -> {
Stream<Map<String, Object>> suggestions = documents.map((JsonNode document) -> {
Optional<JsonNode> id = getOptional(document, "id");
Optional<JsonNode> type = getOptional(document, "type");
Stream<String> labels = Arrays.asList(fields.split(",")).stream().map(String::trim)
.map(field -> AuthorityResource.fieldValues(field, document).map((JsonNode node) -> //
(node.isTextual() ? Optional.ofNullable(node) : Optional.ofNullable(node.findValue("label")))
.orElseGet(() -> Json.toJson("")).asText()).collect(Collectors.joining(", ")));
.map(field -> AuthorityResource.fieldValues(field, document)
.collect(Collectors.joining(", ")));
List<String> categories = filtered(Lists.newArrayList(type.orElseGet(() -> Json.toJson("[]")).elements())
.stream().map(JsonNode::asText).filter(t -> !t.equals("AuthorityResource"))
.collect(Collectors.toList()));
return Json.toJson(toSuggestionsMap(document, id, labels, categories));
return toSuggestionsMap(document, id, labels, categories);
});
return Json.toJson(suggestions.distinct().collect(Collectors.toList())).toString();
}
Expand Down
65 changes: 53 additions & 12 deletions app/models/AuthorityResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import java.util.Scanner;
import java.util.TreeSet;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
Expand Down Expand Up @@ -93,7 +95,7 @@ public String title() {
}

public String subTitle() {
String lifeDates = fieldValues("dateOfBirth-dateOfDeath", json).map(JsonNode::asText)
String lifeDates = fieldValues("dateOfBirth-dateOfDeath", json)
.collect(Collectors.joining());
String details = find("definition", "biographicalOrHistoricalInformation");
return Stream.of(lifeDates, details).filter(s -> !s.isEmpty()).collect(Collectors.joining(" | "));
Expand Down Expand Up @@ -449,24 +451,63 @@ private String withDefaultHidden(String field, int size, int i, String result) {
return result;
}

public static Stream<JsonNode> fieldValues(String field, JsonNode document) {
if (field.contains("-")) {
String[] fields = field.split("-");
String v1 = year(document.findValue(fields[0]));
String v2 = year(document.findValue(fields[1]));
return v1.isEmpty() && v2.isEmpty() ? Stream.empty()
: Stream.of(Json.toJson(String.format("%s-%s", v1, v2)));
public static Stream<String> fieldValues(String field, JsonNode document) {
// standard case: `field` is a plain field name, use that:
List<String> result = flatStrings(document.findValues(field));
if (result.isEmpty()) {
if (field.contains("_")) {
// `label_fieldName` template, e.g. `*_dateOfBirth`
Matcher matcher = Pattern.compile("([^_]+)_([A-Za-z]+)").matcher(field);
while (matcher.find()) {
String label = matcher.group(1);
String fieldName = matcher.group(2);
List<JsonNode> findValues = document.findValues(fieldName);
if (!findValues.isEmpty()) {
String values = flatStrings(findValues).stream()
.collect(Collectors.joining());
field = field.replace(matcher.group(), label + " " + values);
} else {
field = field.replace(matcher.group(), "");
}
}
result = field.trim().isEmpty() ? Arrays.asList() : Arrays.asList(field);
}
// TODO replace with template usage?: `_dateOfBirth-_dateOfDeath`
else if (field.contains("-")) {
String[] fields = field.split("-");
String v1 = year(document.findValue(fields[0]));
String v2 = year(document.findValue(fields[1]));
result = v1.isEmpty() && v2.isEmpty() ? Lists.newArrayList()
: Arrays.asList(String.format("%s-%s", v1, v2));
}
}
return document.findValues(field).stream().flatMap((node) -> {
return node.isArray() ? Lists.newArrayList(node.elements()).stream() : Arrays.asList(node).stream();
});
return result.stream();
}

private static List<String> flatStrings(List<JsonNode> values) {
return values.stream().flatMap(node -> toArray(node)).map(node -> toString(node))
.collect(Collectors.toList());
}

private static Stream<JsonNode> toArray(JsonNode node) {
return node.isArray() ? Lists.newArrayList(node.elements()).stream()
: Arrays.asList(node).stream();
}

private static String toString(JsonNode node) {
return year((node.isTextual() ? Optional.ofNullable(node)
: Optional.ofNullable(node.findValue("label"))).orElseGet(() -> Json.toJson(""))
.asText());
}

private static String year(JsonNode node) {
if (node == null || !node.isArray() || node.size() == 0) {
return "";
}
String text = node.elements().next().asText();
return year(node.elements().next().asText());
}

private static String year(String text) {
return text.matches("\\d{4}-\\d{2}-\\d{2}") ? text.split("-")[0] : text;
}
}
17 changes: 17 additions & 0 deletions test/controllers/SuggestionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,21 @@ public void suggestionsCorsHeader() {

}

@Test
public void suggestionsTemplate() {
Application application = fakeApplication();
running(application, () -> {
String format = "json:preferredName,*_dateOfBirth+in_placeOfBirth";
Result result = route(application,
fakeRequest(GET, "/gnd/search?q=*&filter=type:Person&format=" + format));
assertNotNull("We have a result", result);
assertThat(result.contentType().get(), is(equalTo("application/json")));
String content = contentAsString(result);
assertNotNull("We can parse the result as JSON", Json.parse(content));
assertTrue("We replaced the field names in the template with their values",
Json.parse(content).findValues("label").stream()
.anyMatch(label -> label.asText()
.contains("* 1923 in https://d-nb.info/gnd/4005728-8")));
});
}
}

0 comments on commit 88da5ae

Please sign in to comment.