Skip to content

Commit

Permalink
Add search for single objects
Browse files Browse the repository at this point in the history
A search is added to the single objects API. The results are
marked in the XML output and listed in the JSON output.
  • Loading branch information
MartinFechner committed Oct 12, 2021
1 parent 5938b40 commit a84f141
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 76 deletions.
70 changes: 44 additions & 26 deletions API.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
# API

- [API](#api)
- [1. APPCONF](#1-appconf)
- [2. List of all IDs](#2-list-of-all-ids)
- [2.1 GET-Parameters](#21-get-parameters)
- [3. List of objects or relations](#3-list-of-objects-or-relations)
- [3.1 GET-Parameters](#31-get-parameters)
- [3.2 Results](#32-results)
- [Default `/api/<object-type>`](#default-apiobject-type)
- [Default `/api/<relation-type>`](#default-apirelation-type)
- [3.3 Examples](#33-examples)
- [4. Get object](#4-get-object)
- [4.1 GET-Parameters](#41-get-parameters)
- [4.2 Results](#42-results)
- [JSON output: `/api/<object-type>/<object-id>`](#json-output-apiobject-typeobject-id)
- [4.3 Examples](#43-examples)
- [5. Get part of an object](#5-get-part-of-an-object)
- [5.1 GET-Parameters](#51-get-parameters)
- [5.2 Results](#52-results)
- [JSON output](#json-output)
- [5.3 Examples](#53-examples)
- [6. Searching](#6-searching)
- [6.1 GET-Parameters](#61-get-parameters)
- [6.2 Result](#62-result)
- [JSON output: `/api/search/<search-id>?q=<query>`](#json-output-apisearchsearch-idqquery)
- [6.3 Examples](#63-examples)
- [7. Caching](#7-caching)
- [1. APPCONF](#1-appconf)
- [2. List of all IDs](#2-list-of-all-ids)
- [2.1 GET-Parameters](#21-get-parameters)
- [3. List of objects or relations](#3-list-of-objects-or-relations)
- [3.1 GET-Parameters](#31-get-parameters)
- [3.2 Results](#32-results)
- [Default `/api/<object-type>`](#default-apiobject-type)
- [Default `/api/<relation-type>`](#default-apirelation-type)
- [3.3 Examples](#33-examples)
- [4. Get object](#4-get-object)
- [4.1 GET-Parameters](#41-get-parameters)
- [4.2 Results](#42-results)
- [XML output: `/api/<object-type>/<object-id>?output=xml`](#xml-output-apiobject-typeobject-idoutputxml)
- [JSON output: `/api/<object-type>/<object-id>`](#json-output-apiobject-typeobject-id)
- [4.3 Examples](#43-examples)
- [5. Get part of an object](#5-get-part-of-an-object)
- [5.1 GET-Parameters](#51-get-parameters)
- [5.2 Results](#52-results)
- [JSON output](#json-output)
- [5.3 Examples](#53-examples)
- [6. Searching](#6-searching)
- [6.1 GET-Parameters](#61-get-parameters)
- [6.2 Result](#62-result)
- [JSON output: `/api/search/<search-id>?q=<query>`](#json-output-apisearchsearch-idqquery)
- [6.3 Examples](#63-examples)
- [7. Caching](#7-caching)

## 1. APPCONF

Expand Down Expand Up @@ -161,9 +161,22 @@ Returns a information of a single object.
- `json-xml` some of the object information is retrieved as JSON, including the XML.
- if not set: some object information is retrieved as JSON
- `view` defines which view (see [APPCONF.md](APPCONF.md)) is used to transform the object. The result is retrieved. To be used with `output`.
- `search` searches the object.
- `search-type` optional parameter. To be used with `search`. With following values:
- with empty value the exact matches are found. Multiple words are separated with a space.
- `regex` for one or more words (separated by space) using regular expressions
- `phrase` for a query of multiple words. With `slop` the distance can be defined (default is 1).
- `lucene` for a lucene query, see <https://lucene.apache.org/core/2_9_4/queryparsersyntax.html>
- `search-xpath` optional parameter. To be used with `search`. One can specify which xpath-Elements of the object are included in the search.
- `slop` the distance of words in a phrase search. To be used with `search` and `search-type=phrase`.

### 4.2 Results

#### XML output: `/api/<object-type>/<object-id>?output=xml`

Shows the XML data. If a search was triggered the hits are marked with
`<exist:match xmlns:exist="http://exist.sourceforge.net/NS/exist">`.

#### JSON output: `/api/<object-type>/<object-id>`

- `?absolute-resource-id` eXist-db specific id of the ressource containing the object
Expand Down Expand Up @@ -193,6 +206,11 @@ Returns a information of a single object.
- `?root` definition of part root
- `?xmlid` of part
- `?("part-id")` contains definition of nested parts
- `?search-results` contains an array of hits, if a search was triggered. Each hit containing:
- `?context-following` of the found keyword
- `?context-previous` of the found keyword
- `?keyword` found by search
- `?score` of this single search hit
- `?views` contains the defined views for the object
- `?views?("view-id")` contains the following values:
- `?id` of view
Expand Down
10 changes: 10 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@

ediarum.WEB offers a API to retrieve all needed data for the frontend. Details of the API is found in [API.md](API.md).

The following features are supported:

- List of objects incl. facetted search and fulltext search
- Single object as XML
- Transform a single object with XSLT
- Metadata of an object as JSON
- Search within a list of objects or in a single object and highlight the hits
- Retrieve passages / parts of an object
- Search for relations between objects

*Setup*

To include the ediarum.web backend API add a `appconf.xml` (see [APPCONF.xml](APPCONF.xml)) to your project and include the following lines to your `controller.xql`:
Expand Down
8 changes: 4 additions & 4 deletions build.properties
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#Tue, 24 Aug 2021 10:59:22 +0200
#Tue, 12 Oct 2021 12:36:50 +0200
project.version.major=1
project.version.minor=12
project.version.minor=13
project.version.patch=0

project.app.name=ediarum.web
project.app.folder=.

repository.version=v1.11.0-8-ge3942d8
repository.version=v1.11.0-6-g3c0f3a1

project.version.date=2021-08-24.10-59-22
project.version.date=2021-10-12.12-36-50
Binary file not shown.
173 changes: 131 additions & 42 deletions content/edweb-api.xql
Original file line number Diff line number Diff line change
Expand Up @@ -297,19 +297,9 @@ declare function edwebapi:get-object(
) as map(*)
{
let $object-def := edwebapi:get-config($app-target)//appconf:object[@xml:id=$object-type]
let $namespaces :=
for $ns in $object-def/appconf:item/appconf:namespace
let $prefix := $ns/@id/string()
let $namespace-uri := $ns/string()
return util:declare-namespace($prefix, $namespace-uri)
let $data-collection := edwebapi:data-collection($app-target)
let $collection := $object-def/appconf:collection
let $root := $object-def/appconf:item/appconf:root
let $list := edwebapi:get-objects($data-collection, $collection, $root)

let $id-xpath := $object-def/appconf:item/appconf:id
let $find-expression := $id-xpath||"='"||$object-id||"'"
let $item := util:eval("$list["||$find-expression||"][1]")
let $item := edwebapi:get-object-xml($app-target, $object-type, $object-id)
let $xml := $item

let $inner-nav :=
for $n in $object-def/appconf:inner-navigation/appconf:navigation
Expand Down Expand Up @@ -352,11 +342,6 @@ declare function edwebapi:get-object(
let $prepath := ""
for $part in $object-def/appconf:parts/appconf:part
return local:get-part-map($part, $prefix, $prepath, $separator, "")
let $xml :=
if ($item[1])
then $item[1]
else error(xs:QName("edwebapi:get-object-001"), "Can't find "||$root||"["||$find-expression
||"] in collection "||$collection||" in "||$data-collection)
let $views :=
for $view at $pos in $object-def//appconf:views/appconf:view
let $id := $view/@id/string()
Expand Down Expand Up @@ -435,6 +420,133 @@ declare function edwebapi:get-object-as(
return $result
};

declare function edwebapi:get-object-with-search(
$app-target as xs:string,
$object-type as xs:string,
$object-id as xs:string,
$kwic-width as xs:string?,
$search-xpath as xs:string,
$search-query as xs:string,
$search-type as xs:string?,
$slop as xs:string?
) as map(*)
{
let $object-def := edwebapi:get-config($app-target)//appconf:object[@xml:id=$object-type]
let $xml := edwebapi:get-object-xml($app-target, $object-type, $object-id)

(: Search :)
let $init-indices := local:init-search-indices($app-target)
let $kwic-width :=
if ($kwic-width||"" = "")
then "30"
else $kwic-width
let $search-xpath :=
if ($search-xpath eq ".")
then "("||string-join($object-def/appconf:lucene/appconf:text/@qname/string(), "|")||")"
else $search-xpath

let $query := edwebapi:build-search-query($search-query, $search-type, $slop)

let $query-function := ".[.//"||$search-xpath||"[ft:query(., $query)]]"
let $search-score as xs:float :=
if ($search-xpath||$search-query||"" != "")
then xs:float(ft:score($xml))
else xs:float(0.0)
let $search-hits :=
if ($search-xpath||$search-query||"" != "")
then util:eval-inline($xml, $query-function)
else ()
let $xml :=
if (count($search-hits) > 0)
then util:expand($search-hits)
else $xml

let $map := edwebapi:get-object($app-target, $object-type, $object-id)
return
map:merge((
$map,
map:entry("xml", $xml),
map:entry(
"search-results",
for $hit in $search-hits
let $kwic := kwic:summarize($hit, <config width="{$kwic-width}"/>)
(: TODO delete following line? :)
for $item at $pos in $kwic
return
map:merge ((
map:entry("context-previous", $kwic[$pos]/span[@class='previous']/string()),
map:entry("keyword", $kwic[$pos]/span[@class='hi']/string()),
map:entry("context-following", $kwic[$pos]/span[@class='following']/string()),
map:entry("score", ft:score($hit))
))
),
map:entry("score", $search-score)
))
};

declare function edwebapi:build-search-query(
$search-query as xs:string,
$search-type as xs:string?,
$slop as xs:string?
) as node()
{
let $query :=
switch ($search-type)
case "regex" return
<bool>
{
for $word in tokenize($search-query, ' ' )
return
<regex occur="must">{$word}</regex>
}
</bool>
case "phrase" return <phrase slop="{$slop}">{$search-query}</phrase>
case "lucene" return $search-query
default return
<bool>
{
for $word in tokenize($search-query, ' ' )
return
<term occur="must">{$word}</term>
}
</bool>
let $query :=
if ($search-type = "lucene")
then $query
else
<query>{$query}</query>
return $query
};

declare function edwebapi:get-object-xml(
$app-target as xs:string,
$object-type as xs:string,
$object-id as xs:string
) as node()?
{
let $object-def := edwebapi:get-config($app-target)//appconf:object[@xml:id=$object-type]
let $namespaces :=
for $ns in $object-def/appconf:item/appconf:namespace
let $prefix := $ns/@id/string()
let $namespace-uri := $ns/string()
return util:declare-namespace($prefix, $namespace-uri)
let $data-collection := edwebapi:data-collection($app-target)
let $collection := $object-def/appconf:collection
let $root := $object-def/appconf:item/appconf:root
let $list := edwebapi:get-objects($data-collection, $collection, $root)

let $id-xpath := $object-def/appconf:item/appconf:id
let $find-expression := $id-xpath||"='"||$object-id||"'"
let $item := util:eval("$list["||$find-expression||"][1]")

let $xml :=
if ($item[1])
then $item[1]
else error(xs:QName("edwebapi:get-object-001"), "Can't find "||$root||"["||$find-expression
||"] in collection "||$collection||" in "||$data-collection)
return $xml
};

declare function edwebapi:eval-base-data-for-object(
$object-def as node(),
$object as node()
Expand Down Expand Up @@ -677,31 +789,8 @@ declare function edwebapi:get-object-list-with-search(
if ($search-xpath eq ".")
then "("||string-join($object-def/appconf:lucene/appconf:text/@qname/string(), "|")||")"
else $search-xpath
let $query :=
switch ($search-type)
case "regex" return
<bool>
{
for $word in tokenize($search-query, ' ' )
return
<regex occur="must">{$word}</regex>
}
</bool>
case "phrase" return <phrase slop="{$slop}">{$search-query}</phrase>
case "lucene" return $search-query
default return
<bool>
{
for $word in tokenize($search-query, ' ' )
return
<term occur="must">{$word}</term>
}
</bool>
let $query :=
if ($search-type = "lucene")
then $query
else
<query>{$query}</query>
let $query := edwebapi:build-search-query($search-query, $search-type, $slop)

let $objects-xml :=
if ($search-xpath||$search-query||"" != "")
then
Expand Down
2 changes: 1 addition & 1 deletion expath-pkg.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://expath.org/ns/pkg" name="http://www.bbaw.de/telota/software/ediarum/web/lib" abbrev="edweb" version="1.12.0" spec="1.0">
<package xmlns="http://expath.org/ns/pkg" name="http://www.bbaw.de/telota/software/ediarum/web/lib" abbrev="edweb" version="1.13.0" spec="1.0">
<title>ediarum.web</title>
<xquery>
<namespace>http://www.bbaw.de/telota/software/ediarum/web/lib</namespace>
Expand Down
18 changes: 17 additions & 1 deletion views/api/object-json.xql
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,26 @@ let $app-target := request:get-parameter("app-target", request:get-attribute("ap
let $object-type := request:get-parameter("object-type", request:get-attribute("object-type"))
let $object-id := request:get-parameter("object-id", request:get-attribute("object-id"))

let $search-query := request:get-parameter("search", request:get-attribute("search"))
let $search-type := request:get-parameter("search-type", request:get-attribute("search-type"))
let $search-xpath := request:get-parameter("search-xpath", request:get-attribute("search-xpath"))
let $search-xpath :=
if ($search-xpath||"" eq "")
then "."
else $search-xpath
let $slop := request:get-parameter("slop", request:get-attribute("slop"))
let $kwic-width := request:get-parameter("kwic-width", request:get-attribute("kwic-width"))

let $part := request:get-parameter("part", request:get-attribute("part"))
let $output := request:get-parameter("output", request:get-attribute("output"))

let $map := edwebapi:get-object($app-target, $object-type, $object-id)
let $map :=
if ($search-query||"" != "")
then
edwebapi:get-object-with-search($app-target, $object-type, $object-id, $kwic-width, $search-xpath, $search-query, $search-type, $slop)
else
edwebapi:get-object($app-target, $object-type, $object-id)

return
if ($part != "")
then
Expand Down
Loading

0 comments on commit a84f141

Please sign in to comment.