Search resources
A developer can search for Things via the Bosch IoT Things HTTP API. The search criteria can be defined using a filter (RQL). Further, he can define a sort order and the pagination of the search results.
Search - root resource
The root resource itself is the endpoint for further search
operations.
The filter
parameter is optional. If omitted, the search will return
all Things on which the currently authenticated user has read
permission.
https://<host>/api/2/search/things?filter=<your-search-criteria>
Example - all things in the living room
https://<host>/search/things?filter=eq(attributes/location,"living-room")
Starting with HTTP API v2 the search result is limited
to the Things within the namespace (or namespaces) the solution is
configured for (see
Manage your solution. With the
namespaces
parameter it is possible to search in other known
namespaces.
Search - count resource
The search/things/count
resource be used to count things.
The filter
parameter is optional. If omitted, the search will return
all Things on which the currently authenticated user has read
permission.
Example - count the things in the living room
https://search/things/count?filter=eq(attributes/location,"living-room")
The following operations are available over the HTTP
API:
The operators provided to compose a search request are described in the following sections.
Search defaults
- Namespace – if you don’t explicitly request for things of specific namespaces, only the things of the default namespace are listed (respectively of all namespaces of your solution in case you have booked a Standard plan)
- Filter – if you don’t use the RQL filter, you will get all things
- Sorting – if you don’t specify any sort criteria, the things will appear in alphabetical order - sorted by their
thingId
- Page size - by default, one page of search result will contain 25 things (max. is 200)
Search restrictions
- The search is case sensitive.
In case you don’t know how exactly the spelling of the namespace, name, attribute, feature etc. is, use thelike
notation for filtering - The internal search index is “eventually consistent”.
Consistency with the latest thing updates should recover after a while. - The search index can hold max. 950 Byte for the path and the value combined.
SearchIndex of namespaces
If a filter is applied to the search request, the fields that are filtered upon
have to be included in the SearchIndex
of the namespaces
set to the request.
If your subscription has multiple namespaces and no namespace is set in the search request, the search will apply to all namespaces. For such a case, the specific fields used in the filter need to be indexed in all namespaces of the corresponding subscription, otherwise you will run into an error. The error message will tell you which namespaces' index needs to be updated respectively. In case you search in a namespace which belongs to another subscription, please inform the respective owner about which fields you would need as search criteria.
SearchIndex defaults
The fields thingId
, policyId
, definition
, attributes
and _created
are indexed per default. This allows an out-of-the-box possibility to filter
on these fields and always allows the search with an empty filter.
See further information on how the UI supports you to
Manage the search index of a namespace.
Search - fields
Following fields help you to more precisely describe what you are actually searching for.
thingId
policyId
- all feature IDs
- all feature properties
- all feature desiredProperties
- all attributes
_namespace
_created
_modified
_policy
_revision
_metadata
The fields can be used in a search filter as well as for sorting the results.
See Examples
Parameter: filter
This filter
parameter defines the search query by aggregation of the
following operators.
Please note that whitespaces are only allowed inside
of quoted string values.
For example: eq(attributes/manufacturer, "ACME")
is not allowed because of the whitespace after the comma,
but
eq(attributes/manufacturer,"ACME")
and
eq(attributes/manufacturer,"ACME Holding")
are okay.
Relational operators
eq
eq(<property>, <value>) = <query>
(i.e. equal to the given value)
Example - things owned by “SID123”
filter=eq(attributes/manufacturer,"SID123")
ne
ne(<property>, <value>) = <query>
(i.e. not equal to the given value)
Example - things with manufacturer different than “SID123”
filter=ne(attributes/manufacturer,"SID123")
The response will contain only things which do provide an manufacturer attribute (in this case with value 0 or not SID123).
gt
gt(<property>, <value>) = <query>
(i.e. greater than to the given value)
Example - things with thing ID greater than “A000”
filter=gt(thingId,"A000")
ge
ge(<property>, <value>) = <query>
(i.e. equal to the given value or greater than it)
Example - things with thing ID “A000” or greater
filter=ge(thingId,"A000")
lt
lt(<property>, <value>) = <query>
(i.e. lower than the given value)
Example - things with thing ID lower than “A000”
filter=lt(thingId,"A000")
le
le(<property>, <value>) = <query>
(i.e. lower than the given value or equal to it)
Example - things with thing ID “A000” or lower
filter=le(thingId,"A000")
in
in(<property>, <value>, <value> , ...) = <query>
(i.e. contains at least one of the values listed)
Example - things with thing ID “A000” or “AB00” or “AZ99”
filter=in(thingId,"A000","AB00","AZ99")
like
like(<property>, <value>) = <query>
(i.e. contains values similar to the expressions listed)
Details concerning the like-operator
The like
operator provides some regular expression capabilities for
pattern matching Strings.
The following expressions are supported:
- startswith* => match at the start of a specific String.
- *endswith => match at the end of a specific String.
- *contains* => match if contains a specific String.
- Th?ng => match for a wildcard character.
Examples
filter=like(attributes/key1,"known-chars-at-start*")
filter=like(attributes/key1,"*known-chars-at-end")
filter=like(attributes/key1,"*known-chars-in-between*")
filter=like(attributes/key1,"just-som?-char?-unkn?wn")
exists
exists(<property>) = <query>
(i.e. all things in which the given path exists)
Example - things which have a Feature with ID “feature_1”
filter=exists(features/feature_1)
Example - lamp features which are located in the living-room
filter=and(exists(features/lamp), eq(attributes/location,"living-room"))
Details on property
<property> = url-encoded-string
To search for nested properties, we use JSON Pointer notation (RFC-6901
The following example shows how to search for the sub property location of the parent property attributes with a forward slash as separator:
eq(attributes/location,"kitchen")
Details on value
<value> = <number>, <string>, true, false, null
<number> = double, integer
<string> = ", url-encoded-string, "
Comparison of string values
Comparison operators such as
gt
, ge
, lt
and
le
, do not support a special “semantics” of string comparison (e.g.
regarding alphabetical or lexicographical ordering). However, you can
rely on the alphabetical sorting of strings with the same length (e.g.
“aaa” < “zzz”) and that the order stays the same over
multiple/different search requests.
Comparison of other data types
Please note that
the comparison of other data types is supported by the API, but it only
supports comparison of same data types, and does not do any conversion
during comparison.
Logical operators
and
and(<query>,<query>, ...) = <query>
(i.e. all given values match)
Example - things which are located on the “upper floor” in the “living-room”
filter=and(eq(attributes/floor,"upper floor"),eq(attributes/location,"living-room"))
or
or(<query>,<query>, ...) = <query>
(i.e. at least one of the given values match)
Example - all things located on the “upper floor”, and all things with location “living-room”
filter=or(eq(attributes/floor,"upper floor"),eq(attributes/location,"living-room"))
not
not(<query>) = <query>
(i.e. the negation/opposite of the given query)
Example - search for all things with floor different than “upper floor” or without any floor attribute, and location different than “living-room” or without any location attribute
filter=not(eq(attributes/floor,"upper floor"),eq(attributes/location,"living-room"))
When using a negating expression, such as
not
or
ne
, (not equal) please keep in mind that you might experience a longer
time to get the response. Compared to other search operators we observed
longer response times ourselves.
Parameter: namespaces
The namespaces
parameter is optional.
If omitted, the search field is restricted to all namespaces registered
for the solution (correlating to the API token used for the request).
Setting the namespaces
parameter allows to specify one or multiple
namespaces where to apply the search criteria.
Example - things in specified namespaces
namespaces=com.bosch.si,org.eclipse.ditto
Parameter: option
Optional parameter defining one or more options like sorting or pagination:
Sort option
sort(<+|-><property>,<+|-><property>,...)
- Use + for an ascending sort order (URL encoded character %2B)
- Use - for a descending sort order (URL encoded character %2D)
Example - sort the list ascending by the thing ID
sort(+thingId)
Example - sort the list ascending by an attribute
sort(+attributes/location)
Example - multiple sort options
sort(-attributes/location,+thingId)
This expression will sort the list descending by location attribute.
In case there are multiple things with the same location attribute,
these are sorted ascending by their ID.
Sorting of string values
Sorting does not support a special “semantics” of
string comparison (e.g. regarding alphabetical or lexicographical
ordering). However, you can rely on the alphabetical sorting of strings
with the same length (e.g. “aaa” < “zzz”) and that the order stays the
same over multiple/different search requests.
Sorting of other values
Sorting does not
support a special “semantics” of comparison for fields with values of
different data types (e.g. numbers vs. strings). However, you can rely
on the fact that values of the same type are sorted respectively.
Paging options
size(<count>)
Limits the search results to <count>
items.
- If the paging option is not explicitly specified, the default
value
size(25)
is used,
i.e. the first25
results are returned. - The maximum allowed count is
200
.
cursor(<cursor-id>)
Starts the search at the position of the cursor with ID <cursor-id>
.
The cursor ID is obtained from the field cursor
of a previous response
and marks the position after the last entry of the previous search.
A response includes no cursor if there are no more results.
If a request has a cursor
option, then any included filter
or sort
option may not differ from the original request of the cursor.
Otherwise, the request is rejected.
The meaning of cursor IDs is undefined.
Complex search examples
Example 1
Search for all things located in “living-room”, reorder the list to start with the lowest thing ID as the first element, and return the first 5 results.
/search/things?filter=eq(attributes/location,"living-room")&option=sort(+thingId),size(5)
Example 2
Report of all things which have been modified after January 21, 2019.
The result should contain the thing IDs, the revision number, and the
timestamp of the latest
modification.
/search/things?filter=gt(_modified,"2019-01-21")&fields=thingId,_revision,_modified
Example 3
Report of all things which have been created after August 24, 2020.
The result should contain the thing IDs, the revision number, and the
timestamp of the creation.
/search/things?filter=gt(_created,"2020-08-24")&fields=thingId,_revision,_created
Eventual consistency of search results
The search functionality is highly dedicated in updating its internal index as fast as possible. However, while working on distributed systems, beeing “eventually consistent” seems to fulfill the need of reaching a consistent state at some point in time.
The consistency should recover after some time. Request the search-persisted acknowledgement in order to know exactly when a twin modification is reflected in the search index.
See also our FAQ section.