Skip to content

Commit 01f59bd

Browse files
author
Benjamin Geer
authored
feat(api-v2): Optionally return file values in full-text search results (DSP-1191) (#1776)
1 parent 9a5fb77 commit 01f59bd

12 files changed

Lines changed: 695 additions & 18 deletions

File tree

‎docs/03-apis/api-v2/reading-and-searching-resources.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ in `app/v2` in `application.conf`.
509509
If the parameter `limitToStandoffClass` is provided, Knora will look for search terms
510510
that are marked up with the indicated standoff class.
511511

512+
If the parameter `returnFiles=true` is provided, Knora will return any
513+
file value attached to each matching resource.
514+
512515
To request the number of results rather than the results themselves, you can
513516
do a count query:
514517

‎test_data/searchR2RV2/FulltextSearchWithImage.jsonld‎

Lines changed: 579 additions & 0 deletions
Large diffs are not rendered by default.

‎webapi/src/main/scala/org/knora/webapi/messages/v2/responder/searchmessages/SearchMessagesV2.scala‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ case class FullTextSearchCountRequestV2(searchValue: String,
6060
* @param offset the offset to be used for paging.
6161
* @param limitToProject limit search to given project.
6262
* @param limitToResourceClass limit search to given resource class.
63+
* @param returnFiles if true, return any file value value attached to each matching resource.
6364
* @param targetSchema the target API schema.
6465
* @param schemaOptions the schema options submitted with the request.
6566
* @param featureFactoryConfig the feature factory configuration.
@@ -70,6 +71,7 @@ case class FulltextSearchRequestV2(searchValue: String,
7071
limitToProject: Option[IRI],
7172
limitToResourceClass: Option[SmartIri],
7273
limitToStandoffClass: Option[SmartIri],
74+
returnFiles: Boolean,
7375
targetSchema: ApiV2Schema,
7476
schemaOptions: Set[SchemaOption],
7577
featureFactoryConfig: FeatureFactoryConfig,

‎webapi/src/main/scala/org/knora/webapi/responders/v2/SearchResponderV2.scala‎

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,34 +80,42 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
8080
limitToStandoffClass,
8181
featureFactoryConfig,
8282
requestingUser)
83+
8384
case FulltextSearchRequestV2(searchValue,
8485
offset,
8586
limitToProject,
8687
limitToResourceClass,
8788
limitToStandoffClass,
89+
returnFiles,
8890
targetSchema,
8991
schemaOptions,
9092
featureFactoryConfig,
9193
requestingUser) =>
92-
fulltextSearchV2(searchValue,
93-
offset,
94-
limitToProject,
95-
limitToResourceClass,
96-
limitToStandoffClass,
97-
targetSchema,
98-
schemaOptions,
99-
featureFactoryConfig,
100-
requestingUser)
94+
fulltextSearchV2(
95+
searchValue,
96+
offset,
97+
limitToProject,
98+
limitToResourceClass,
99+
limitToStandoffClass,
100+
returnFiles,
101+
targetSchema,
102+
schemaOptions,
103+
featureFactoryConfig,
104+
requestingUser
105+
)
106+
101107
case GravsearchCountRequestV2(query, featureFactoryConfig, requestingUser) =>
102108
gravsearchCountV2(inputQuery = query,
103109
featureFactoryConfig = featureFactoryConfig,
104110
requestingUser = requestingUser)
111+
105112
case GravsearchRequestV2(query, targetSchema, schemaOptions, featureFactoryConfig, requestingUser) =>
106113
gravsearchV2(inputQuery = query,
107114
targetSchema = targetSchema,
108115
schemaOptions = schemaOptions,
109116
featureFactoryConfig = featureFactoryConfig,
110117
requestingUser = requestingUser)
118+
111119
case SearchResourceByLabelCountRequestV2(searchValue,
112120
limitToProject,
113121
limitToResourceClass,
@@ -118,6 +126,7 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
118126
limitToResourceClass,
119127
featureFactoryConfig,
120128
requestingUser)
129+
121130
case SearchResourceByLabelRequestV2(searchValue,
122131
offset,
123132
limitToProject,
@@ -132,8 +141,10 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
132141
targetSchema,
133142
featureFactoryConfig,
134143
requestingUser)
144+
135145
case resourcesInProjectGetRequestV2: SearchResourcesByProjectAndClassRequestV2 =>
136146
searchResourcesByProjectAndClassV2(resourcesInProjectGetRequestV2)
147+
137148
case other => handleUnexpectedMessage(other, log, this.getClass.getName)
138149
}
139150

@@ -167,7 +178,8 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
167178
searchTerms = searchTerms,
168179
limitToProject = limitToProject,
169180
limitToResourceClass = limitToResourceClass.map(_.toString),
170-
limitToStandoffClass.map(_.toString),
181+
limitToStandoffClass = limitToStandoffClass.map(_.toString),
182+
returnFiles = false, // not relevant for a count query
171183
separator = None, // no separator needed for count query
172184
limit = 1,
173185
offset = 0,
@@ -197,6 +209,7 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
197209
* @param offset the offset to be used for paging.
198210
* @param limitToProject limit search to given project.
199211
* @param limitToResourceClass limit search to given resource class.
212+
* @param returnFiles if true, return any file value attached to each matching resource.
200213
* @param targetSchema the target API schema.
201214
* @param schemaOptions the schema options submitted with the request.
202215
* @param featureFactoryConfig the feature factory configuration.
@@ -208,6 +221,7 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
208221
limitToProject: Option[IRI],
209222
limitToResourceClass: Option[SmartIri],
210223
limitToStandoffClass: Option[SmartIri],
224+
returnFiles: Boolean,
211225
targetSchema: ApiV2Schema,
212226
schemaOptions: Set[SchemaOption],
213227
featureFactoryConfig: FeatureFactoryConfig,
@@ -227,6 +241,7 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
227241
limitToProject = limitToProject,
228242
limitToResourceClass = limitToResourceClass.map(_.toString),
229243
limitToStandoffClass = limitToStandoffClass.map(_.toString),
244+
returnFiles = returnFiles,
230245
separator = Some(groupConcatSeparator),
231246
limit = settings.v2ResultsPerPage,
232247
offset = offset * settings.v2ResultsPerPage, // determine the actual offset
@@ -364,7 +379,6 @@ class SearchResponderV2(responderData: ResponderData) extends ResponderWithStand
364379
* @return a [[ResourceCountV2]] representing the number of resources that have been found.
365380
*/
366381
private def gravsearchCountV2(inputQuery: ConstructQuery,
367-
apiSchema: ApiV2Schema = ApiV2Simple,
368382
featureFactoryConfig: FeatureFactoryConfig,
369383
requestingUser: UserADM): Future[ResourceCountV2] = {
370384

‎webapi/src/main/scala/org/knora/webapi/routing/v2/SearchRouteV2.scala‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
4141
private val LIMIT_TO_RESOURCE_CLASS = "limitToResourceClass"
4242
private val OFFSET = "offset"
4343
private val LIMIT_TO_STANDOFF_CLASS = "limitToStandoffClass"
44+
private val RETURN_FILES = "returnFiles"
4445

4546
/**
4647
* Returns the route.
@@ -236,6 +237,11 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
236237

237238
val limitToStandoffClass: Option[SmartIri] = getStandoffClass(params)
238239

240+
val returnFiles: Boolean = stringFormatter.optionStringToBoolean(
241+
params.get(RETURN_FILES),
242+
throw BadRequestException(s"Invalid boolean value for '$RETURN_FILES'")
243+
)
244+
239245
val targetSchema: ApiV2Schema = RouteUtilV2.getOntologySchema(requestContext)
240246
val schemaOptions: Set[SchemaOption] = RouteUtilV2.getSchemaOptions(requestContext)
241247

@@ -250,7 +256,8 @@ class SearchRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit
250256
offset = offset,
251257
limitToProject = limitToProject,
252258
limitToResourceClass = limitToResourceClass,
253-
limitToStandoffClass,
259+
limitToStandoffClass = limitToStandoffClass,
260+
returnFiles = returnFiles,
254261
featureFactoryConfig = featureFactoryConfig,
255262
requestingUser = requestingUser,
256263
targetSchema = targetSchema,

‎webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v1/searchFulltextStandard.scala.txt‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ WHERE {
147147
OPTIONAL {
148148
?resource knora-base:hasStillImageFileValue ?fileValue .
149149
?fileValue a knora-base:StillImageFileValue .
150-
?fileValue knora-base:isPreview true .
151150
?fileValue knora-base:internalFilename ?previewPath .
152151

153152
OPTIONAL {

‎webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/searchFulltext.scala.txt‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
* @param limitToProject limit search to the given project.
3333
* @param limitToResourceClass limit search to given resource class.
3434
* @param limitToStandoffClass limit the search to given standoff class.
35+
* @param returnFiles if true, return any file value attached to each matching resource.
3536
* @param separator the separator to be used in aggregation functions.
3637
* @param limit maximal amount of rows to be returned
3738
* @param offset offset for paging (starts with 0)
@@ -42,6 +43,7 @@
4243
limitToProject: Option[IRI],
4344
limitToResourceClass: Option[IRI],
4445
limitToStandoffClass: Option[IRI],
46+
returnFiles: Boolean,
4547
separator: Option[Char],
4648
limit: Int,
4749
offset: Int,
@@ -53,6 +55,7 @@
5355
limitToProject = limitToProject,
5456
limitToResourceClass = limitToResourceClass,
5557
limitToStandoffClass = limitToStandoffClass,
58+
returnFiles = returnFiles,
5659
separator = separator,
5760
limit = limit,
5861
offset = offset,
@@ -65,6 +68,7 @@
6568
limitToProject = limitToProject,
6669
limitToResourceClass = limitToResourceClass,
6770
limitToStandoffClass = limitToStandoffClass,
71+
returnFiles = returnFiles,
6872
separator = separator,
6973
limit = limit,
7074
offset = offset,

‎webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/searchFulltextGraphDB.scala.txt‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @param limitToProject limit search to the given project.
3737
* @param limitToResourceClass limit search to given resource class.
3838
* @param limitToStandoffClass limit the search to given standoff class.
39+
* @param returnFiles if true, return any file value attached to each matching resource.
3940
* @param limit maximal amount of rows to be returned
4041
* @param offset offset for paging (starts with 0)
4142
* @param countQuery indicates whether it is a count query or the actual resources should be returned.
@@ -44,6 +45,7 @@
4445
limitToProject: Option[IRI],
4546
limitToResourceClass: Option[IRI],
4647
limitToStandoffClass: Option[IRI],
48+
returnFiles: Boolean,
4749
separator: Option[Char],
4850
limit: Int,
4951
offset: Int,
@@ -53,7 +55,10 @@ PREFIX knora-base: <http://www.knora.org/ontology/knora-base#>
5355
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5456

5557
@if(!countQuery) {
56-
SELECT DISTINCT ?resource (GROUP_CONCAT(IF(BOUND(?valueObject), STR(?valueObject), ""); separator="@separator.getOrElse(throw SparqlGenerationException("Separator expected for non count query, but none given"))") AS ?valueObjectConcat)
58+
SELECT DISTINCT ?resource
59+
(GROUP_CONCAT(IF(BOUND(?valueObject), STR(?valueObject), "");
60+
separator="@separator.getOrElse(throw SparqlGenerationException("Separator expected for non count query, but none given"))")
61+
AS ?valueObjectConcat)
5762
} else {
5863
SELECT (count(distinct ?resource) as ?count)
5964
}
@@ -133,6 +138,11 @@ WHERE {
133138
case None => {}
134139
}
135140

141+
@if(returnFiles) {
142+
OPTIONAL {
143+
?resource knora-base:hasFileValue ?valueObject .
144+
}
145+
}
136146
}
137147
@if(!countQuery) {
138148
GROUP BY ?resource

‎webapi/src/main/twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/searchFulltextStandard.scala.txt‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @param limitToProject limit search to the given project.
3737
* @param limitToResourceClass limit search to given resource class.
3838
* @param limitToStandoffClass limit the search to given standoff class.
39+
* @param returnFiles if true, return any file value attached to each matching resource.
3940
* @param separator the separator to be used in aggregation functions.
4041
* @param limit maximal amount of rows to be returned
4142
* @param offset offset for paging (starts with 0)
@@ -46,6 +47,7 @@
4647
limitToProject: Option[IRI],
4748
limitToResourceClass: Option[IRI],
4849
limitToStandoffClass: Option[IRI],
50+
returnFiles: Boolean,
4951
separator: Option[Char],
5052
limit: Int,
5153
offset: Int,
@@ -55,7 +57,10 @@ PREFIX knora-base: <http://www.knora.org/ontology/knora-base#>
5557
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5658

5759
@if(!countQuery) {
58-
SELECT DISTINCT ?resource (GROUP_CONCAT(IF(BOUND(?valueObject), STR(?valueObject), ""); separator="@separator.getOrElse(throw SparqlGenerationException("Separator expected for non count query, but none given"))") AS ?valueObjectConcat)
60+
SELECT DISTINCT ?resource
61+
(GROUP_CONCAT(IF(BOUND(?valueObject), STR(?valueObject), "");
62+
separator="@separator.getOrElse(throw SparqlGenerationException("Separator expected for non count query, but none given"))")
63+
AS ?valueObjectConcat)
5964
} else {
6065
SELECT (count(distinct ?resource) as ?count)
6166
}
@@ -135,6 +140,13 @@ WHERE {
135140
case None => {}
136141
}
137142

143+
@if(returnFiles) {
144+
OPTIONAL {
145+
?fileValueProp rdfs:subPropertyOf* knora-base:hasFileValue .
146+
?resource ?fileValueProp ?valueObject .
147+
}
148+
}
149+
138150
FILTER NOT EXISTS {
139151
?resource knora-base:isDeleted true .
140152
}

‎webapi/src/test/scala/org/knora/webapi/e2e/v2/SearchRouteV2R2RSpec.scala‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,22 @@ class SearchRouteV2R2RSpec extends R2RSpec {
216216
}
217217
}
218218

219+
"return files attached to full-text search results" in {
220+
221+
Get("/v2/search/p7v?returnFiles=true") ~> searchPath ~> check {
222+
223+
assert(status == StatusCodes.OK, response.toString)
224+
225+
val expectedAnswerJSONLD =
226+
readOrWriteTextFile(responseAs[String],
227+
Paths.get("test_data/searchR2RV2/FulltextSearchWithImage.jsonld"),
228+
writeTestDataFiles)
229+
230+
compareJSONLDForResourcesResponse(expectedJSONLD = expectedAnswerJSONLD, receivedJSONLD = responseAs[String])
231+
232+
}
233+
}
234+
219235
"not accept a fulltext query containing http://api.knora.org" in {
220236
val invalidSearchString: String =
221237
URLEncoder.encode("PREFIX knora-api: <http://api.knora.org/ontology/knora-api/v2#>", "UTF-8")

0 commit comments

Comments
 (0)