On this page we'll cover how to search with the IOTICS API's Search Service.

Using the Search Service allows you to run a synchronous GLOBAL or LOCAL search with each available result returned as a chunk. The last chunk will always be a timeout error.

Any user of IOTICS can run a semantic search to find metadata and data feeds both inside their own IOTICSpace and outside, in the IOTICS ecosystem. The search will find and return all Digital Twins matching the set criteria that the user has permission to access.

You can search loosely, such as for text or location, or more specific, such as by specifying exact semantic triple matches.

Once you've found a Twin that matches your criteria you can use the Twin Service to Describe it, see our Twin guide for more information.

On this page we will cover how to:

You can see the entire IOTICS API Reference here.

Set your headers

The headers are an important part of your API request, representing the meta-data of your request. Before submitting a request you need to ensure you have set your headers:

HeaderTypeDescriptionMandatory
Iotics-ClientRefStringAny responses associated with the request will include this referenceOptional
Iotics-ClientAppIdStringUser namespace used to group all the requests/responsesMandatory
Iotics-TransactionRefStringUsed to loosely link requests/responses in a distributed environment each layer can add its own id to the list

Limited to:

- max 16 elements per list
- max 36 characters
Optional
Iotics-RequestTimeoutString (Date-Time)Used to stop request processing once timeout value is reachedMandatory

The request

To search, you just submit a search POST to the IOTICS API's Search service.

You can see an example request here.

Query parameters

You first set the parameters of your query, deciding the scope of your search and how many results to return:

ParameterTypeDescription
langStringLanguage code for properties
scopeStringChoose from:

- GLOBAL
- LOCAL
- LOCAL_OWN
- AUTO
limitInt64Limits amount of results returned.

Default = 500
Maximum = 1000
offsetInt64Sets where the results are returned from, used with limit to control what is returned

Body parameters

You then set the payload for your request, made up of:

ParameterTypeDescription
expiryTimeoutString (Date-Time)UTC time for when the search request will expire
filterObjectUsed to filter your results
filter:locationObjectUse to filter by location
filter:location:locationObjectThe location being filtered by
filter:location:location:latDoubleThe latitude of the location being filtered by
filter:location:location:lonDoubleThe longitude of the location being filtered by
filter:location:location:radiusKmDoubleThe radius in km around the location to be filtered by
filter:propertiesArray of objectsUsed to filter Twin properties
filter:properties:keyStringFilter with the key (predicate) of the properties
filter:properties:key:langLiteralValueObjectUse a langLiteralValue to filter for a property
filter:properties:key:langLiteralValue:langStringThe 2- character language code for the property
filter:properties:key:langLiteralValue:valueStringThe value of the property you're filtering by
filter:properties:key:literalValueObjectUse a literalValue to filter by a property
filter:properties:key:literalValue:dataTypeStringXSD data type.
Currently supports:
dateTime, time, date, boolean, integer, nonPositiveInteger, negativeInteger, nonNegativeInteger, positiveInteger,
long, unsignedLong, int, unsignedInt, short, unsignedShort, byte, unsignedByte, anyURI
filter:properties:key:literalValue:valueStringString representation of the value according to XSD datatype specification
filter:properties:key:stringLiteralValueObjectUse a stringLiteralValue to filter by a property
filter:properties:key:stringLiteralValue:valueStringThe value of the property you're filtering by
filter:properties:key:uriValueObjectUse a URI to filter by a property
filter:properties:key:uriValue:valueStringThe value of the property you're filtering by
filter:textStringOne or more keywords which must match text from entity

Note: Any, rather than all, of the keywords will produce a match
responseTypeStringChoose from:

- FULL
- LOCATED
- MINIMAL

Examples

Example Search Request

This is an example of a request to search for looking for all Weather related Twins within 3km of central Cambridge.

You can build your own example by going to the Search Service reference page.

# REQUIREMENTS:
# Create a python venv and activate it
# pip install -U pip setuptools wheel
# pip install iotics-identity
# USER_KEY_NAME, AGENT_KEY_NAME, USER_SEED, AGENT_SEED

import json
from datetime import datetime, timedelta, timezone
from typing import List
from uuid import uuid4

from iotics.lib.identity.api.high_level_api import get_rest_high_level_identity_api
from requests import request

RESOLVER_URL = "resolver_url"
HOST = "host_url"

USER_KEY_NAME = "user_key_name"
AGENT_KEY_NAME = "agent_key_name"
USER_SEED = bytes.fromhex("user_seed")
AGENT_SEED = bytes.fromhex("agent_seed")


def search_twins(
    headers: dict,
    properties: List[dict] = None,
    text: str = None,
    location: dict = None,
    scope: str = "GLOBAL",
    response_type: str = "FULL",
):
    payload = {"filter": {}, "responseType": response_type}

    if properties:
        payload["filter"]["properties"] = properties
    if text:
        payload["filter"]["text"] = text
    if location:
        payload["filter"]["location"] = location

    twins_list = []  # Initialise an empty list

    with request(
        method="POST",
        url=f"{HOST}/qapi/searches",
        headers=headers,
        json=payload,
        stream=True,
        verify=False,
        params={"scope": scope},
    ) as resp:
        # Raises HTTPError, if one occurred
        resp.raise_for_status()
        # Iterates over the response data, one line at a time
        for chunk in resp.iter_lines():
            response = json.loads(chunk)
            twins_found = []
            try:
                host_id = response["result"]["payload"]["remoteHostId"]["value"]
                twins_found = response["result"]["payload"]["twins"]
            except KeyError:
                host_id = "local"
            else:
                if twins_found:
                    # Add the host id value to each twin
                    for twin in twins_found:
                        twin["HostId"] = host_id

                    # Append the twins found to the list of twins
                    twins_list.extend(twins_found)

    return twins_list


def create_headers(client_app_id: uuid4, token: str, request_timeout: str):
    return {
        "accept": "application/json",
        "Iotics-ClientAppId": f"search_{client_app_id}",
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
        "Iotics-RequestTimeout": request_timeout,
    }


def create_property_dict(property_key: str, property_value: str, property_type: str):
    return {"key": property_key, property_type: {"value": property_value}}


def create_location_dict(lat: float, lon: float, radius_km: float):
    return {
        "location": {"lon": lon, "lat": lat},
        "radiusKm": radius_km,
    }


api = get_rest_high_level_identity_api(resolver_url=RESOLVER_URL)
(
    user_registered_id,
    agent_registered_id,
) = api.create_user_and_agent_with_auth_delegation(
    user_seed=USER_SEED,
    user_key_name=USER_KEY_NAME,
    agent_seed=AGENT_SEED,
    agent_key_name=AGENT_KEY_NAME,
    delegation_name="#AuthDeleg",
)

twins_list = search_twins(
    headers=create_headers(
        client_app_id=uuid4(),
        token=api.create_agent_auth_token(
            agent_registered_identity=agent_registered_id,
            user_did=user_registered_id.did,
            duration=30,
        ),
        request_timeout=(
            datetime.now(tz=timezone.utc) + timedelta(seconds=10)
        ).isoformat(),
    ),
    properties=[
        create_property_dict(
            property_key="http://data.iotics.com/public#hostAllowList",
            property_type="uriValue",
            property_value="http://data.iotics.com/public#allHosts",
        )
    ],
    location=create_location_dict(lat=52.205276, lon=0.119167, radius_km=3),
    text="Weather",
)

print(f"{len(twins_list)} twins found")
print("---")

for twin in twins_list:
    print(json.dumps(twin, indent=4))
    print("---")