Search#
Content in a Plone site can be searched for by invoking the /@search
endpoint in any context:
GET /plone/@search HTTP/1.1
Accept: application/json
A search is contextual by default. In other words, it is bound to a specific context—a collection in HTTP REST terms—and searches within that collection and any sub-collections.
A Plone site is also a collection.
We therefore have a global search by invoking the /@search
endpoint on the site root.
We also have contextual searches by invoking that endpoint on any other context.
All searches use 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 on which it is invoked, 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 -X GET '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",
"@type": "Plone Site",
"description": "",
"review_state": null,
"title": "Plone site",
"type_title": "Plone Site"
},
{
"@id": "http://localhost:55001/plone/front-page",
"@type": "Document",
"description": "Congratulations! You have successfully installed Plone.",
"review_state": "private",
"title": "Welcome to Plone",
"type_title": "Page"
}
],
"items_total": 2
}
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 path.depth=1
to your search, you will only get the immediate children of the container, and the container itself won't be part of the results.
See the Plone documentation on searching for content within a folder
for more details.
Note
Search 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 the Plone LiveSearch
widget are coded in a way that the SearchableText
parameter is expanded by including a *
wildcard at the end.
This is done to also match the partial results of the beginning of search terms.
The plone.restapi
@search
endpoint will not do that for you.
You will have to add it if you want to keep this feature.
Query format#
Queries and query-wide options, such as 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 will 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": "/folder1",
"depth": 2}}
This dictionary will need to be flattened in dotted notation to pass it into a query string:
GET /plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.depth=1 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i -X GET 'http://nohost/plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.depth=1' -H "Accept: application/json" --user admin:secret
http 'http://nohost/plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.depth=1' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&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",
"type_title": "Folder"
}
],
"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 a 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 to pass it into a query string.
To specify multiple paths, repeat the query string parameter.
The requests
module will automatically do this for you if you pass it a list of values for a query string parameter.
GET /plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&path.depth=2 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i -X GET 'http://nohost/plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&path.depth=2' -H "Accept: application/json" --user admin:secret
http 'http://nohost/plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&path.depth=2' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?sort_on=path&path.query=%2Fplone%2Ffolder1&path.query=%2Fplone%2Ffolder2&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",
"type_title": "Folder"
},
{
"@id": "http://localhost:55001/plone/folder1/doc1",
"@type": "Document",
"description": "",
"review_state": "private",
"title": "Lorem Ipsum",
"type_title": "Page"
},
{
"@id": "http://localhost:55001/plone/folder2",
"@type": "Folder",
"description": "",
"review_state": "private",
"title": "Folder 2",
"type_title": "Folder"
},
{
"@id": "http://localhost:55001/plone/folder2/doc2",
"@type": "Document",
"description": "",
"review_state": "private",
"title": "Lorem Ipsum",
"type_title": "Page"
}
],
"items_total": 4
}
Sorting on multiple indexes#
Sorting can happen on multiple indexes, as the underlying catalog supports it. To do so the query has to contain the list of indexes to be used for sorting in the sort_on
parameter. If wanted the ordering of the sorting can also be added in the query in the sort_order
parameter.
GET /plone/@search?sort_on=portal_type&sort_on=sortable_title HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i -X GET 'http://nohost/plone/@search?sort_on=portal_type&sort_on=sortable_title' -H "Accept: application/json" --user admin:secret
http 'http://nohost/plone/@search?sort_on=portal_type&sort_on=sortable_title' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?sort_on=portal_type&sort_on=sortable_title', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
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 request.
This means that for value types that are not strings, these data types need to be reconstructed on the server side in plone.restapi
.
For most index types, 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, such as a FieldIndex
, may take arbitrary data types as query values.
In that case, plone.restapi
cannot know to what data type to cast your query value.
You will need to specify it using ZPublisher type hints:
GET /plone/@search?numeric_field:int=42 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 contain only the most basic information about the items, such as 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?SearchableText=lorem&metadata_fields=modified&metadata_fields=created HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i -X GET 'http://nohost/plone/@search?SearchableText=lorem&metadata_fields=modified&metadata_fields=created' -H "Accept: application/json" --user admin:secret
http 'http://nohost/plone/@search?SearchableText=lorem&metadata_fields=modified&metadata_fields=created' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?SearchableText=lorem&metadata_fields=modified&metadata_fields=created', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search?SearchableText=lorem&metadata_fields=modified&metadata_fields=created",
"items": [
{
"@id": "http://localhost:55001/plone/doc1",
"@type": "Document",
"created": "1995-07-31T13:45:00+00:00",
"description": "",
"modified": "1995-07-31T17:30:00+00:00",
"review_state": "private",
"title": "Lorem Ipsum",
"type_title": "Page"
}
],
"items_total": 1
}
The metadata from those columns will then be included in the results.
To specify multiple columns, repeat the query string parameter once for every column name.
The requests
module will automatically do this for you if you pass it a list of values for a query string parameter.
To retrieve all metadata columns that the catalog knows about, use metadata_fields=_all
.
Note
There is a difference between the full set of fields contained in an object and the set of all possible metadata columns that can be specified with metadata_fields
.
In other words, using metadata_fields=_all
will produce objects with a set of fields that is generally smaller than the set of fields produced by fullobjects
(see next section).
Briefly, the fields in metadata_fields=_all
are a subset of fullobjects
.
A consequence of this is that certain fields can not be specifed with metadata_fields
.
Doing so will result in a TypeError "No converter for making <...> JSON compatible."
In ZCatalog terms, this reflects the difference between catalog brains and objects that have been woken up.
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?SearchableText=lorem&fullobjects=1 HTTP/1.1
Accept: application/json
Authorization: Basic YWRtaW46c2VjcmV0
curl -i -X GET 'http://nohost/plone/@search?SearchableText=lorem&fullobjects=1' -H "Accept: application/json" --user admin:secret
http 'http://nohost/plone/@search?SearchableText=lorem&fullobjects=1' Accept:application/json -a admin:secret
requests.get('http://nohost/plone/@search?SearchableText=lorem&fullobjects=1', headers={'Accept': 'application/json'}, auth=('admin', 'secret'))
HTTP/1.1 200 OK
Content-Type: application/json
{
"@id": "http://localhost:55001/plone/@search?SearchableText=lorem&fullobjects=1",
"items": [
{
"@components": {
"actions": {
"@id": "http://localhost:55001/plone/doc1/@actions"
},
"aliases": {
"@id": "http://localhost:55001/plone/doc1/@aliases"
},
"breadcrumbs": {
"@id": "http://localhost:55001/plone/doc1/@breadcrumbs"
},
"contextnavigation": {
"@id": "http://localhost:55001/plone/doc1/@contextnavigation"
},
"navigation": {
"@id": "http://localhost:55001/plone/doc1/@navigation"
},
"navroot": {
"@id": "http://localhost:55001/plone/doc1/@navroot"
},
"types": {
"@id": "http://localhost:55001/plone/doc1/@types"
},
"workflow": {
"@id": "http://localhost:55001/plone/doc1/@workflow"
}
},
"@id": "http://localhost:55001/plone/doc1",
"@type": "Document",
"UID": "SomeUUID000000000000000000000002",
"allow_discussion": false,
"changeNote": "",
"contributors": [],
"created": "1995-07-31T13:45: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",
"lock": {
"locked": false,
"stealable": true
},
"modified": "1995-07-31T17:30:00+00:00",
"next_item": {},
"parent": {
"@id": "http://localhost:55001/plone",
"@type": "Plone Site",
"description": "",
"title": "Plone site",
"type_title": "Plone Site"
},
"previous_item": {},
"relatedItems": [],
"review_state": "private",
"rights": "",
"subjects": [],
"table_of_contents": null,
"text": null,
"title": "Lorem Ipsum",
"type_title": "Page",
"version": "current",
"versioning_enabled": true,
"working_copy": null,
"working_copy_of": null
}
],
"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 fullobjects
, we wake up all the returned objects.
Restrict search results to Plone's search settings#
By default, the search endpoint does not exclude any types from its results.
To allow the search to follow Plone's search settings schema, pass the use_site_search_settings=1
to the @search
endpoint request.
By doing this, the search results will be filtered based on the defined types to be searched, and will be sorted according to the default sorting order.