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

Add RPPD advanced search (RPB-38) #367

Merged
merged 6 commits into from
Dec 19, 2023
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
69 changes: 55 additions & 14 deletions app/controllers/HomeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ public HomeController(WSClient httpClient) {
}

public static final String[] AGGREGATIONS = new String[] { "type", "gndSubjectCategory.id", "geographicAreaCode.id",
"professionOrOccupation.id", "professionOrOccupation.label", "placeOfActivity.label", "dateOfBirth" };
"professionOrOccupation.id", "professionOrOccupation.label.keyword", "placeOfActivity.label.keyword",
"dateOfBirth", "gender.id" };

@Inject
Environment env;
Expand Down Expand Up @@ -148,22 +149,34 @@ public Result index() {
public Result api() {
String format = "json";
ImmutableMap<String, String> searchSamples = ImmutableMap.<String, String>builder()
.put("Alles", controllers.routes.HomeController.search("*", "", "", 0, 10, format).toString())
.put("Alles", controllers.routes.HomeController
.search("*", "", "", "", "", "", "", "", 0, 10, format).toString())
.put("Alle Felder",
controllers.routes.HomeController.search("london", "", "", 0, 10, format).toString())
controllers.routes.HomeController
.search("london", "", "", "", "", "", "", "", 0, 10, format)
.toString())
.put("Feldsuche",
controllers.routes.HomeController.search("preferredName:Twain", "", "", 0, 10, format)
controllers.routes.HomeController.search("preferredName:Twain", "", "", "",
"", "", "", "", 0, 10, format)
.toString())
.put("Filter",
controllers.routes.HomeController
.search("preferredName:Twain", "type:Person", "", 0, 10, format).toString())
.search("preferredName:Twain", "", "", "", "", "", "type:Person",
"", 0, 10, format)
.toString())
.put("Paginierung",
controllers.routes.HomeController.search("london", "", "", 50, 100, format).toString())
controllers.routes.HomeController
.search("london", "", "", "", "", "", "", "", 50, 100, format)
.toString())
.put("Sortierung",
controllers.routes.HomeController
.search("scholl", "", "preferredName.keyword:asc", 0, 10, format).toString())
.search("scholl", "", "", "", "", "", "",
"preferredName.keyword:asc", 0, 10, format)
.toString())
.put("ASCII", controllers.routes.HomeController
.search("preferredName.ascii:Chor OR variantName.ascii:Chor", "", "", 0, 10, format).toString())
.search("preferredName.ascii:Chor OR variantName.ascii:Chor", "", "", "",
"", "", "", "", 0, 10, format)
.toString())
.build();
ImmutableMap<String, String> getSamples = ImmutableMap.of(//
"London", controllers.routes.HomeController.authorityDotFormat("4074335-4", "json").toString(), //
Expand Down Expand Up @@ -322,14 +335,20 @@ public Result gnd(String id) {
return ok(prettyJsonString(Json.parse(jsonLd))).as(config("index.content"));
}

public Result search(String q, String filter, String sort, int from, int size, String format) {
public Result advanced() {
return ok(views.html.advanced.render(allHits()));
}

public Result search(String q, String name, String place, String subject, String publication,
String date, String filter, String sort, int from, int size, String format) {
Format responseFormat = Accept.formatFor(format, request().acceptedTypes());
if (responseFormat == null || Stream.of(RdfFormat.values()).map(RdfFormat::getParam)
.anyMatch(f -> f.equals(responseFormat.queryParamString))) {
return unsupportedMediaType(views.html.error.render(q,
String.format("Unsupported for search: format=%s, accept=%s", format, request().acceptedTypes())));
}
String queryString = (q == null || q.isEmpty()) ? "*" : q;
String queryString = buildQueryString(q, name, place, subject, publication, date);
queryString = (queryString == null || queryString.isEmpty()) ? "*" : queryString;
try {
SearchResponse response = index.query(queryString, filter, sort, from, size);
response().setHeader("Access-Control-Allow-Origin", "*");
Expand All @@ -342,7 +361,8 @@ public Result search(String q, String filter, String sort, int from, int size, S
}
switch (responseFormat) {
case HTML: {
return htmlSearch(q, filter, from, size, responseFormat.queryParamString, response);
return htmlSearch(q, name, place, subject, publication, date, filter, from, size,
responseFormat.queryParamString, response);
}
case JSON_LINES: {
response().setHeader("Content-Disposition",
Expand All @@ -360,6 +380,24 @@ public Result search(String q, String filter, String sort, int from, int size, S
}
}

private String buildQueryString(String q, String name, String place, String subject,
String publication, String date) {
q += add(name, "preferredName", "variantName");
q += add(place, "placeOfBirth.label", "placeOfActivity.label", "placeOfDeath.label");
q += add(subject, "professionOrOccupation.label", "gndSubjectCategory.label");
q += add(publication, "publication");
q += add(date + "*", "dateOfBirth", "dateOfDeath");
return q;
}

private String add(String value, String... fields) {
if (!value.replace("*", "").isEmpty()) {
Stream<String> segments = Arrays.asList(fields).stream().map(s -> s + ":" + value);
return "+(" + segments.collect(Collectors.joining(" OR ")) + ") ";
}
return "";
}

private long allHits() {
return index.query("*").getHits().getTotalHits();
}
Expand Down Expand Up @@ -398,9 +436,12 @@ public ByteString next() {
};
}

private Result htmlSearch(String q, String type, int from, int size, String format, SearchResponse response) {
return ok(views.html.search.render(q, type, from, size, returnAsJson(q, response),
response == null ? 0 : response.getHits().getTotalHits(), allHits()));
private Result htmlSearch(String q, String name, String place, String subject,
String publication, String date, String type, int from, int size, String format,
SearchResponse response) {
return ok(views.html.search.render(q, name, place, subject, publication, date, type, from,
size, returnAsJson(q, response),
response == null ? 0 : response.getHits().getTotalHits(), allHits()));
}

static Result withCallback(final String json) {
Expand Down
13 changes: 9 additions & 4 deletions app/models/AuthorityResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,9 @@ private String addLiterals(String field, String result) {
String literalField = field + "AsLiteral";
for (Object literal : get(literalField)) {
String search = controllers.routes.HomeController
.search("", literalField + ":\"" + literal + "\"", "", 0, 10, "html").toString();
.search("", "", "", "", "", "", literalField + ":\"" + literal + "\"", "", 0,
10, "html")
.toString();
result = result + "&nbsp;" + "|" + "&nbsp;" + literal + "&nbsp;"
+ String.format(
"<a title='Weitere Einträge mit %s \"%s\" suchen' href='%s'>"
Expand All @@ -385,7 +387,9 @@ private List<String> typeLinks() {
.collect(Collectors.toList());
List<String> typeLinks = (subTypes.isEmpty() ? getType() : subTypes).stream()
.map(t -> String.format("<a href='%s'>%s</a>",
controllers.routes.HomeController.search("", "+(type:" + t + ")", "", 0, 10, "").toString(),
controllers.routes.HomeController
.search("", "", "", "", "", "", "+(type:" + t + ")", "", 0, 10, "")
.toString(),
models.GndOntology.label(t)))
.collect(Collectors.toList());
return typeLinks;
Expand Down Expand Up @@ -460,7 +464,7 @@ private String process(String field, String value, String label, int i, int size
boolean labelBasedFacet = facets.contains(field + ".label");
boolean qBasedSearch = facets.stream().noneMatch(s -> s.startsWith(field));
String search = controllers.routes.HomeController
.search(qBasedSearch ? field + ".id:\"" + value + "\"" : "",
.search(qBasedSearch ? field + ".id:\"" + value + "\"" : "", "", "", "", "", "",
labelBasedFacet ? field + ".label:\"" + label + "\""
: field + ".id:\"" + value + "\"", "", 0, 10, "html")
.toString();
Expand All @@ -476,7 +480,8 @@ private String process(String field, String value, String label, int i, int size
result = searchLink + "&nbsp;" + (linkableEntity ? entityLink : "");
} else if (field.endsWith("AsLiteral")) {
String search = controllers.routes.HomeController
.search("", field + ":\"" + value + "\"", "", 0, 10, "html").toString();
.search("", "", "", "", "", "", field + ":\"" + value + "\"", "", 0, 10, "html")
.toString();
result = result + "&nbsp;"
+ String.format(
"<a title='Weitere Einträge mit %s \"%s\" suchen' href='%s'>"
Expand Down
11 changes: 11 additions & 0 deletions app/views/advanced.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@* Copyright 2014 Fabian Steeg, hbz. Licensed under the GPLv2 *@

@(allHits: Long)

@import helper._
@import play.api.libs.json.Json
@import play.api.libs.json.JsValue

@main("", "RPPD - Erweiterte Suche", allHits) {
@search_advanced("Suchen", place="_", name="_", subject="_")
}
3 changes: 3 additions & 0 deletions app/views/main.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<li @if(title=="RPPD - Ergebnisliste"){class="active"}>
<a href="@routes.HomeController.search()">Suche</a>
</li>
<li @if(title=="RPPD - Erweiterte Suche"){class="active"}>
<a href="@routes.HomeController.advanced()">Erweiterte Suche</a>
</li>
@*
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Ergänzende Suche<b class="caret"></b></a>
Expand Down
28 changes: 16 additions & 12 deletions app/views/search.scala.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@* Copyright 2015-2018 Fabian Steeg, hbz. Licensed under the EPL 2.0 *@

@(q: String, f: String, from: Int, size: Int, result: String, searchHits: Long, allHits: Long)
@(q: String, name: String = "", place: String = "", subject: String = "", publication: String = "", date: String = "", f: String, from: Int, size: Int, result: String, searchHits: Long, allHits: Long)

@import play.api.libs.json._
@import controllers.HomeController.CONFIG
Expand All @@ -10,25 +10,25 @@
<nav>
<ul class="pagination">
<li class="previous @if(from==0){disabled}">
<a href="@if(from==0){#} else {@routes.HomeController.search(q,f,"",from-size,size,"html")}">&larr;</a>
<a href="@if(from==0){#} else {@routes.HomeController.search(q,name,place,subject,publication,date,f,"",from-size,size,"html")}">&larr;</a>
</li>
@defining((((from+1)/size)+1,(if(searchHits%size==0) searchHits/size else searchHits/size+1).toInt)) { case (currentPage,lastPage) =>
@defining(Math.min(Math.max(1,currentPage-4)+9,lastPage)) { toPage =>
@for(i <- Math.max(1,toPage-9) to toPage){
<li @if(currentPage==i){class="active"}><a href="@routes.HomeController.search(q,f,"",(i*size)-size,size,"html")">@i</a></li>
<li @if(currentPage==i){class="active"}><a href="@routes.HomeController.search(q,name,place,subject,publication,date,f,"",(i*size)-size,size,"html")">@i</a></li>
}
}
}
<li class="next @if(from+size >= searchHits){disabled}">
<a href="@if(from+size >= allHits){#} else {@routes.HomeController.search(q,f,"",from+size,size,"html")}">&rarr;</a>
<a href="@if(from+size >= allHits){#} else {@routes.HomeController.search(q,name,place,subject,publication,date,f,"",from+size,size,"html")}">&rarr;</a>
</li>
</ul>
</nav>
}

@pageLink(num: Int)={
<li role="tab" @if(size==num){class="active" aria-selected="true"} else {aria-selected="false" tabindex="-1" aria-controls="@num"}>
<a href="@routes.HomeController.search(q,f,"",from,num,"html")">@num</a>
<a href="@routes.HomeController.search(q,name,place,subject,publication,date,f,"",from,num,"html")">@num</a>
</li>
}

Expand Down Expand Up @@ -101,19 +101,19 @@
@facetLink(key: String, term: String, label:String, count: Int) = {
@if(f.contains(key+":"+term)){
@Html(label)
<a href='@routes.HomeController.search(q, f.replace(s"+($key:$term)", "").replace(s"$key:$term","").trim)'>
<a href='@routes.HomeController.search(q, name, place, subject, publication, date, f.replace(s"+($key:$term)", "").replace(s"$key:$term","").trim)'>
<span class="label label-default">Filter entfernen <span class="glyphicon glyphicon-remove"></span></span>
</a>
} else {
<a href='@routes.HomeController.search(q, s"$f +($key:$term)".trim)'>@Html(label) (@formatCount(count))</a>
<a href='@routes.HomeController.search(q, name, place, subject, publication, date, s"$f +($key:$term)".trim)'>@Html(label) (@formatCount(count))</a>
}
}

@facet(key: String) = {
@defining((Json.parse(result) \ "aggregation" \ key).asOpt[Seq[JsValue]].getOrElse(Seq())) { buckets =>
@if(!buckets.isEmpty) {
@defining(key.replace(".id", "").replace(".label", "")) { simpleKey =>
<h5>@models.GndOntology.label(simpleKey) @if(buckets.size>7){
@defining(key.replace(".id", "").replace(".label.keyword", "")) { simpleKey =>
<h5>@models.GndOntology.label(simpleKey) @if(buckets.size>5){
<a id="@simpleKey-more-link" href="javascript:void(0)" title="Mehr Werte anzeigen"
onclick="$('.@simpleKey-more-item').show(); $('#@simpleKey-more-link').hide(); $('#@simpleKey-less-link').show();">
<span class="octicon octicon-diff-added" aria-hidden="true"></span>
Expand All @@ -127,7 +127,7 @@ <h5>@models.GndOntology.label(simpleKey) @if(buckets.size>7){
(bucket, bIndex) <- buckets.zipWithIndex;
term = (bucket \ "key").as[String];
count = (bucket \ "doc_count").as[Int]) {
<span @if(bIndex>6){style="display:none" class="@simpleKey-more-item"}>
<span @if(bIndex>4){style="display:none" class="@simpleKey-more-item"}>
@facetLink(key, "\"" + term + "\"", models.GndOntology.label(term), count) <br/>
</span>
}
Expand Down Expand Up @@ -171,6 +171,9 @@ <h5>@models.GndOntology.label("type")</h5>

@main(q, "RPPD - Ergebnisliste", allHits) {
<!-- <code><pre>@result</pre></code> -->
@if(Seq(name, place, subject, publication, date).exists(!_.isEmpty)){
@search_advanced("Suche aktualisieren", q=q, name=name, place=place, subject=subject, publication=publication, date=date)
}
@defining((Json.parse(result) \ "member").asOpt[Seq[JsValue]].getOrElse(Seq()).zipWithIndex) { hits =>
<div class="row" id="search-results">
<div class="col-md-@if(searchHits > 0){8}else{12}">
Expand Down Expand Up @@ -227,9 +230,10 @@ <h5>@models.GndOntology.label("type")</h5>
@if(!buckets.isEmpty) {
<h4>Ergebnisse eingrenzen:</h4>
@*@typeFacet(buckets)*@
@facet("professionOrOccupation.label")
@facet("professionOrOccupation.label.keyword")
@facet("gndSubjectCategory.id")
@facet("placeOfActivity.label")
@facet("gender.id")
@facet("placeOfActivity.label.keyword")
@facet("geographicAreaCode.id")
@*@facet("professionOrOccupation.id")*@
@*@facet("dateOfBirth")*@
Expand Down
95 changes: 95 additions & 0 deletions app/views/search_advanced.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
@* Copyright 2014 Fabian Steeg, hbz. Licensed under the GPLv2 *@

@(label:String, q:String = "", place: String = "", name: String = "", subject: String = "", nwbibspatial: String = "", nwbibsubject: String = "", date: String = "", sortParam: String = "", publication: String = "")

@import scala.collection.immutable.TreeMap

<script>
function addSearchWidget(c){
var form = $("#nwbib-form");
var last = $(".search-field").last();
last.find(".rem").removeClass("disabled");
var add = last.clone().attr("id","search-field-" + c);
add.find(".rem").attr("onclick", "remSearchWidget("+(c)+")");
add.find("#nwbib-query-advanced").val("");
last.find("#and-search-field-"+(c-1)).show();
add.find("#and-search-field-"+(c-1)).attr("id", "and-search-field-"+c).hide();
add.find("#add-search-field-"+(c-1)).attr("id", "add-search-field-"+c)
last.find("#add-search-field-"+(c-1)).hide();
form.append(add);
$("#add-search-field-"+c).attr("onclick", "addSearchWidget("+(c+1)+")");
form.append($("#nwbib-form > #search-button"));
}

function remSearchWidget(c) {
$("#search-field-"+c).remove();
$("#search-button").show();
var rows = $(".search-field");
var last = rows.last();
last.find(".and").hide();
last.find(".add").show();
if(rows.size() == 1){
last.find(".rem").addClass("disabled");
}
}

function addParameters(){
var ps = {};
$(".search-field").each(function( index ) {
var param = $(this).find("#query-select").val();
var value = $(this).find("#nwbib-query-advanced").val();
ps[param] = (ps[param] ? ps[param] + " " : "") + value;
});
for (var key in ps) {
$("#nwbib-form").append("<input type='hidden' name='"+key+"' value='"+ps[key]+"'/>");
}
}
</script>

@helper.form(action = controllers.routes.HomeController.search(),
'id -> "nwbib-form", 'role -> "form", 'class -> "form-inline", 'autocomplete -> "off") {
@defining(TreeMap("name"->name,"place"->place,"publication"->publication,"subject"->subject,"date"->date).filter({case (k,v) => !v.isEmpty})) { fields =>
@for(((k,v),i)<- (if(fields.isEmpty) TreeMap("q" -> "") else fields).zipWithIndex){
<div class="search-field row" id="search-field-@i">
<div class="form-group col col-md-3">
<select id="query-select" title="Suchtyp" class="form-control">
<option value="name" @if(k=="name"){selected="selected"}>Namen</option>
<option value="place" @if(k=="place"){selected="selected"}>Orte</option>
<option value="subject" @if(k=="subject"){selected="selected"}>Berufe, Fachgebiete</option>
<option value="publication" @if(k=="publication"){selected="selected"}>Werke</option>
<option value="date" @if(k=="date"){selected="selected"}>Lebensdaten, Jubiläen</option>
</select>
</div>
<div class="form-group col col-md-8">
<div class="input-group">
<input type="text" id="nwbib-query-advanced" title="Suchtext" class="form-control" placeholder='@if(v.startsWith("_")){@v.drop(1)}else{Suchanfrage}' value="@if(v.startsWith("_")){}else{@v}" onFocus="$('#search-button').show();"/>
<span class="input-group-btn">
<a href="#" title="Kriterium entfernen" class="btn btn-default rem @if(fields.isEmpty||fields.size==1){disabled}" onclick="remSearchWidget(@i)">
<span class="octicon octicon-x"></span>
</a>
</span>
</div>
</div>
<div class="form-group col col-md-1">
<a href="#" id="and-search-field-@i" class="btn btn-default add-and-button and disabled" title="UND">UND</a>
<a href="#" id="add-search-field-@i" class="btn btn-default add-and-button add" onclick="addSearchWidget(@(i+1))" title="Kriterium hinzufügen">
<span class="glyphicon glyphicon-plus-sign"></span></a>
</div>
<script>
$("#add-search-field-@(i-1)").hide();
</script>
</div>
}
<div class="row" id="search-button">
<div class="form-group col-md-2 col-md-offset-5">
<button type=submit class="btn btn-link" onclick="addParameters()" aria-label="@label">
<span class="glyphicon glyphicon-search"></span> @label</button>
</div>
</div>
<script>
$("#and-search-field-@(if(fields.isEmpty) 0 else fields.size-1)").hide();
$("#search-button").hide();
</script>
@if(!sortParam.isEmpty()){<input type="hidden" name="sort" value="@sortParam"/>}
}
}
Loading
Loading