Search¶
Content in a Plone site can be searched for by invoking the /@search
endpoint on any context:
GET /plone/@search HTTP/1.1
Accept: application/json
A search is contextual by default, i.e. it is bound to a specific context (a collection in HTTP REST terms) and searches within that collection and any sub-collections.
Since a Plone site is also a collection, we therefore have a global search (by invoking the /@search
endpoint on the site root) and contextual searches (by invoking that endpoint on any other context) all using the same pattern.
In terms of the resulting catalog query this means that, by default, a search will be constrained by the path to the context it’s invoked on, unless you explicitly supply your own path
query.
Search results are represented similar to collections:
GET /plone/@search?sort_on=path HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i 'http://nohost/plone/@search?sort_on=path' -H 'Accept: application/json' --user admin:secret
http 'http://nohost/plone/@search?sort_on=path' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?sort_on=path', headers={
'Accept': 'application/json',
}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search",
"items": [
{
"@id": "http://localhost:55001/plone/front-page",
"@type": "Document",
"description": "Congratulations! You have successfully installed Plone.",
"review_state": "private",
"title": "Welcome to Plone"
}
],
"items_total": 1
}
The default representation for search results is a summary that contains only the most basic information.
In order to return specific metadata columns, see the documentation of the metadata_fields
parameter below.
Note
A search invoked on a container will by default include that container
itself as part of the search results. This is the same behavior as displayed by
ZCatalog, which is used internally.
If you add the query string
parameter depth=1
to your search, you will only get immediate
children of the container, and the container itself also won’t be part
of the results. See the Plone docs on
searching for content within a folder.
for more details.
Note
Search results results will be batched if the size of the resultset exceeds the batch size. See Batching for more details on how to work with batched results.
Warning
The @@search view or in Plone LiveSearch widget are coded in a way that the SearchableText parameter is expanded by including a * wildcard at the end. This is done in order to match also the partial results of the beginning of a search term(s). plone.restapi @search endpoint will not do that for you. You’ll have to add it if you want to keep this feature.
Query format¶
Queries and query-wide options (like sort_on
) are submitted as query string parameters to the /@search
request:
GET /plone/@search?SearchableText=lorem HTTP/1.1
This is nearly identical to the way that queries are passed to the Plone @@search
browser view, with only a few minor differences.
For general information on how to query the Plone catalog, please refer to the Plone Documentation on Querying.
Query options¶
In case you want to supply query options to a query against a particular index, you’ll need to flatten the corresponding query dictionary and use a dotted notation to indicate nesting.
For example, to specify the depth
query option for a path query, the original query as a Python dictionary would look like this:
query = {'path': {'query': '/folder',
'depth': 2}}
This dictionary will need to be flattened in dotted notation in order to pass it in a query string:
GET /plone/@search?path.query=%2Fplone%2Ffolder1&sort_on=path&path.depth=1 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i 'http://nohost/plone/@search?path.query=%2Fplone%2Ffolder1&sort_on=path&path.depth=1' -H 'Accept: application/json' --user admin:secret
http 'http://nohost/plone/@search?path.query=%2Fplone%2Ffolder1&sort_on=path&path.depth=1' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?path.query=%2Fplone%2Ffolder1&sort_on=path&path.depth=1', headers={
'Accept': 'application/json',
}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search?path.query=%2Fplone%2Ffolder1&path.depth=1",
"items": [
{
"@id": "http://localhost:55001/plone/folder1/folder2",
"@type": "Folder",
"description": "",
"review_state": "private",
"title": "Folder 2"
}
],
"items_total": 1
}
Again, this is very similar to how Record Arguments are parsed by ZPublisher, except that you can omit the :record
suffix.
Restricting search to multiple paths¶
To restrict search to multiple paths, the original query as a Python dictionary would look like this (with an optional depth and sort_on):
query = {'path': {'query': ('/folder', '/folder2'),
'depth': 2},
'sort_on': 'path'}
This dictionary will need to be flattened in dotted notation in order to pass it in a query string. In order to specify multiple paths, simply repeat the query string parameter (the requests
module will do this automatically for you if you pass it a list of values for a query string parameter).
GET /plone/@search?path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&sort_on=path&path.depth=2 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i 'http://nohost/plone/@search?path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&sort_on=path&path.depth=2' -H 'Accept: application/json' --user admin:secret
http 'http://nohost/plone/@search?path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&sort_on=path&path.depth=2' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&sort_on=path&path.depth=2', headers={
'Accept': 'application/json',
}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search?path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&path.depth=2",
"items": [
{
"@id": "http://localhost:55001/plone/folder1",
"@type": "Folder",
"description": "",
"review_state": "private",
"title": "Folder 1"
},
{
"@id": "http://localhost:55001/plone/folder1/doc1",
"@type": "Document",
"description": "",
"review_state": "private",
"title": "Lorem Ipsum"
},
{
"@id": "http://localhost:55001/plone/folder2",
"@type": "Folder",
"description": "",
"review_state": "private",
"title": "Folder 2"
},
{
"@id": "http://localhost:55001/plone/folder2/doc2",
"@type": "Document",
"description": "",
"review_state": "private",
"title": "Lorem Ipsum"
}
],
"items_total": 4
}
Data types in queries¶
Because HTTP query strings contain no information about data types, any query string parameter value ends up as a string in the Zope’s request. This means, that for values types that aren’t string, these data types need to be reconstructed on the server side in plone.restapi.
For most index types and their query values and query options, plone.restapi can handle this for you.
If you pass it path.query=foo&path.depth=1
, it has the necessary knowledge about the ExtendedPathIndex
’s options to turn the string 1
for the depth
argument back into an integer before passing the query on to the catalog.
However, certain index types (a FieldIndex
for example) may take arbitrary data types as query values.
In that case, plone.restapi
simply can’t know what data type to cast your query value to, and you’ll need to specify it using ZPublisher type hints:
GET /plone/@search?numeric_field=42:int HTTP/1.1
Accept: application/json
Please refer to the Documentation on Argument Conversion in ZPublisher for details.
Retrieving additional metadata¶
By default the results are represented as summaries that only contain the most basic information about the items, like their URL and title.
If you need to retrieve additional metadata columns, you can do so by specifying the additional column names in the metadata_fields
parameter:
GET /plone/@search?metadata_fields=modified&metadata_fields=created&SearchableText=lorem HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i 'http://nohost/plone/@search?metadata_fields=modified&metadata_fields=created&SearchableText=lorem' -H 'Accept: application/json' --user admin:secret
http 'http://nohost/plone/@search?metadata_fields=modified&metadata_fields=created&SearchableText=lorem' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?metadata_fields=modified&metadata_fields=created&SearchableText=lorem', headers={
'Accept': 'application/json',
}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search?metadata_fields=modified&metadata_fields=created&SearchableText=lorem",
"items": [
{
"@id": "http://localhost:55001/plone/doc1",
"@type": "Document",
"created": "2016-10-21T19:00:00+00:00",
"description": "",
"modified": "2016-10-21T19:00:00+00:00",
"review_state": "private",
"title": "Lorem Ipsum"
}
],
"items_total": 1
}
The metadata from those columns then will be included in the results.
In order to specify multiple columns, simply repeat the query string parameter once for every column name (the requests
module will do this automatically for you if you pass it a list of values for a query string parameter).
In order to retrieve all metadata columns that the catalog knows about, use metadata_fields=_all
.
Retrieving full objects¶
If the data provided as metadata is not enough, you can retrieve search results as full serialized objects equivalent to what the resource GET request would produce.
You do so by specifying the fullobjects
parameter:
GET /plone/@search?fullobjects=1&SearchableText=lorem HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i 'http://nohost/plone/@search?fullobjects=1&SearchableText=lorem' -H 'Accept: application/json' --user admin:secret
http 'http://nohost/plone/@search?fullobjects=1&SearchableText=lorem' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?fullobjects=1&SearchableText=lorem', headers={
'Accept': 'application/json',
}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search?fullobjects=1&SearchableText=lorem",
"items": [
{
"@components": {
"actions": {
"@id": "http://localhost:55001/plone/doc1/@actions"
},
"breadcrumbs": {
"@id": "http://localhost:55001/plone/doc1/@breadcrumbs"
},
"navigation": {
"@id": "http://localhost:55001/plone/doc1/@navigation"
},
"workflow": {
"@id": "http://localhost:55001/plone/doc1/@workflow"
}
},
"@id": "http://localhost:55001/plone/doc1",
"@type": "Document",
"UID": "SomeUUID000000000000000000000002",
"allow_discussion": false,
"changeNote": "",
"contributors": [],
"created": "2016-10-21T19:00:00+00:00",
"creators": [
"test_user_1_"
],
"description": "",
"effective": null,
"exclude_from_nav": false,
"expires": null,
"id": "doc1",
"is_folderish": false,
"language": "",
"layout": "document_view",
"modified": "2016-10-21T19:00:00+00:00",
"parent": {
"@id": "http://localhost:55001/plone",
"@type": "Plone Site",
"description": "",
"title": "Plone site"
},
"relatedItems": [],
"review_state": "private",
"rights": "",
"subjects": [],
"table_of_contents": null,
"text": null,
"title": "Lorem Ipsum",
"version": "current",
"versioning_enabled": true
}
],
"items_total": 1
}
Warning
Be aware that this might induce performance issues when retrieving a lot of resources. Normally the search just serializes catalog brains, but with full objects we wake up all the returned objects.