Edit this page

Ditto provides a search service that lets you query across all managed digital twins using RQL expressions.

TL;DR: You search Things using RQL filter expressions, with results sorted and paged. The search index updates asynchronously (eventual consistency) – typically within 1-2 seconds.

Overview

You can access the search functionality through three APIs:

API Access Method Characteristics
HTTP HTTP request-response Stateless
Ditto protocol WebSocket and connections Reactive-streams compatible
Server-sent events HTML5 server-sent events Streaming with resumption

How it works

Search index

Ditto’s things-search microservice automatically consumes all events emitted for changes to Things and Policies. It updates a search-optimized representation in its own database.

You do not need to define custom indexes. The database structure is “flattened” so that all data in Things can be searched efficiently.

Consistency

The search index provides eventual consistency. Updates are written at a default interval of 1 second (configurable via THINGS_SEARCH_UPDATER_STREAM_WRITE_INTERVAL).

When you update a Thing and receive a success response, the search index does not reflect that change immediately. The change typically appears within 1-2 seconds.

If you need to know when a modification is reflected in the search index, request the built-in acknowledgement search-persisted in the command. A status code of 204 confirms successful index update. Status codes at or above 400 indicate failure.

Search queries

You formulate search queries using the Ditto-supported subset of RQL. Queries work through the HTTP API or the Ditto Protocol (e.g., via WebSocket).

Querying scalar JSON values

The query property can target scalar JSON values: booleans, numbers, or strings.

Example – find all Things located in “living-room”, sorted ascending by Thing ID, returning the first 5 results:

Filter:     eq(attributes/location,"living-room")
Sorting:    sort(+thingId)
Paging:     size(5),cursor(CURSOR_ID)

Querying JSON arrays

The query property can also target JSON arrays. Ditto indexes all values in the array, including mixed types.

Given a Thing with tags of different types:

{
  "thingId": "org.eclipse.ditto:tagged-thing-1",
  "policyId": "org.eclipse.ditto:tagged-thing-1",
  "attributes": {
    "tags": [
      "misc", "no-due-date", "high-priority",
      2, 3, 5, false,
      { "room": "kitchen", "floor": 2 }
    ]
  }
}

You can query against scalar values in the array:

eq(attributes/tags,"high-priority")
-> match:       "high-priority" is contained

ne(attributes/tags,"high-priority")
-> no match:    "high-priority" is contained, so "ne" will not match

in(attributes/tags,"misc","something-non-matching")
-> match:       "misc" is a match

like(attributes/tags,"*-priority")
-> match:       "high-priority" string matches

ne(attributes/tags,1)
-> match:       as 1 is not part of the tags

gt(attributes/tags,6)
-> no match:    as no number > 6 is contained

You can also query JSON objects inside the array:

exists(attributes/tags/room)            -> match
eq(attributes/tags/room,"kitchen")      -> match
ge(attributes/tags/floor,2)             -> match

Search count queries

Count queries use the same filter syntax. Sorting and paging options are not applicable for count queries.

Namespaces

You can restrict the search to specific namespaces. This can significantly improve performance when many Things from different namespaces exist in the index.

RQL

Ditto uses RQL notation for search queries and other scenarios such as filtering notifications.

Sorting and paging options

Sorting

The sort option controls the order of search results:

sort(<+|-><property1>,<+|-><property2>,...)

If not specified, results are sorted ascending by Thing ID: sort(+thingId).

Paging with cursor

The size option limits results per response:

size(<count>)

Default: 25. Maximum: 200.

Use cursor-based paging to iterate through large result sets:

cursor(<cursor-id>)

The cursor ID comes from the cursor field of a previous response and marks the position after the last returned entry. No cursor in the response means no more results.

If a request includes a cursor, the filter and sort options must match the original request that produced the cursor.

Example – return ten items with a cursor:

option=size(10),cursor(<cursor-from-previous-result>)

RQL paging (deprecated)

The limit option specifies which page to return:

limit(<offset>,<count>)

Default: limit(0,25). Maximum count: 200.

Example – return the first ten items:

limit(0,10)

Example – return items 11 to 20:

limit(10,10)

Further reading

Tags: search rql