Data Lake

Searching data with OpenGate API

The searching API lets you retrieve provisioned and collected information from the entities registered on the platform.

Using search API, you can manage many situations in which you need to get information, collected by OpenGate, about your remote devices.

Some examples of questions you can answer using the searching API are:

  • Where is my lost truck?
  • Is my vending machine connected to Internet?
  • What is the software version of this smart meter which is rebooting all the time?
  • How is the signal strength of this weather station which is off-line most of the time?
  • What are the latest operations launched over different devices and their current status?
  • What are the latest raised alarms associated with my in-field resources?
  • What is the latest value and history of different sensors and machine parameters?

Searching Features

Where are the FROM and WHERE?

Well, if you’re still thinking in SQL, then you’ll expect to find the word FROM anywhere. Remember, OpenGate exposes its API through a REST interface, so in this case the word FROM is in the URL suffix.

That suffix is the resource you are querying, and every available one is listed in What you can query. The WHERE — and the ORDER BY, the SELECT and the GROUP BY — is the JSON body described below.

In all response cases, you must POST a valid JSON query and you’ll get an array with the matched specific resources. The query could have next main objects:

  • filter: Allows to select the resources that meets with desired information, see Filtering
  • limit: Allows paginating the response, see Pagination
  • sort: Allows sorting the results, see Sorting
  • select: Allows selecting only the parameters you need, see Selecting
  • group: Allows grouping the results, see Grouping
{
  "filter": {}, // filter document omitted
  "limit": {}, // limit document omitted
  "sort": {}, // sort document omitted
  "select": {}, // select document omitted
  "group": {} // group document omitted
}

Procedure

Searching in OpenGate platform is pretty easy. You have to send a HTTP request to the API using the POST method, the prefix always is /north/v80/search. Optionally you can attach a JSON file (in the HTTP body) if you need to use paging, sorting, selecting, grouping or filtering features.

You can use the URL above for searching information. So for the impatient, let’s suppose you’re trying to search over your previously provisioned device list, and you’re thinking in a SQL WHERE clause like that:

name like 'device_name' AND (
    serialNumber like '82A75D494B0EBF7A95587285AE78E83F' OR
    serialNumber like '08D83B1864A1F9CFED76DAF426EB04D7')

in this case you must use https://api.opengate.es/north/v80/search/devices URL and POST a JSON payload like that:

{
  "filter": {
    "and": [
      {
        "like": {
          "device.name": "device_name"
        }
      },
      {
        "or": [
          {
            "like": {
              "device.serialNumber": "82A75D494B0EBF7A95587285AE78E83F"
            }
          },
          {
            "like": {
              "device.serialNumber": "08D83B1864A1F9CFED76DAF426EB04D7"
            }
          }
        ]
      }
    ]
  },
  "limit": {
    "start": 26,
    "size": 50
  }
}

Requesting with curl:

curl --request POST \
     --data-binary @search-query.json \
     --header "X-ApiKey: YOUR_API_KEY_HERE" \
     --verbose \
     https://[your_opengate_address]/north/v80/search/devices \
     -H "Content-type: application/json"

Then you’ll receive the device list matching with your query, the response body should be something like that.

{
  "devices": [
    {}, // device body omitted
    {}, // device body omitted
    // a number of other devices
    {}
  ]
}

Where to go next

The clauses above are documented one page each, below. Two things live outside this section:

  • Which URL to POST to: What you can query indexes every search endpoint.
  • Where the clauses behave differently: time series and data sets accept the same syntax with stricter rules and a different response shape. The differences are collected in Query dialects.

Subsections of Data Lake

Filtering

The search API uses the following filtering options to facilitate the search and allow to perform a wide range of consultations.

Several techniques solve the filtering issue when you’re querying over a RESTful interface. For example, you can use standard HTTP parameters to add filtering capabilities to your query. It’s pretty simple but doesn’t cover complex needs. We require a SQL-like approach, with typical operators like AND, OR, EQUAL, NOT EQUAL, etc. OpenGate allows you to filter your queries by sending a POST request to a specific URI. In the POST request, you must send a JSON document with a fashionable DSL structure. It is a command pattern approach in contrast with the entity/collection pattern used in the provisioning API.

Filtering operators

Filtering comparison operator list

  • eq: Equals.
  • neq: Not equals.
  • like: Regex pattern like.
  • gt: Greater than.
  • lt: Lower than.
  • gte: Greater than or equals.
  • lte: Lower than or equals.
  • in[]: Included in a concrete group.
  • nin[]: Not included in a concrete group.
  • exists: Exists.
  • within: Included in an areas.geometry GeoJson (exclusive for Area search).
See supported identifiers for existing comparison operator.
  • asset: identifier
  • channel: identifier
  • device:
    • identifier
    • serialNumber
  • device.communicationModules[]:
    • identifier
    • mobile.imei
  • device.communicationModules[].subscriber:
    • identifier
  • device.communicationModules[].subscription:
    • identifier
    • address
    • mobile.imsi
    • mobile.msisdn
    • mobile.icc
    • presence.ipRtt
    • presence.unifiedPresence
  • entity:
    • areas
    • location
  • organization: identifier
  • provision.asset:
    • identifier
    • location
  • provision.device:
    • identifier
    • location
  • provision.device.communicationModules[]: identifier
  • provision.device.communicationModules[].subscriber: identifier
  • provision.device.communicationModules[].subscription:
    • identifier
    • address
    • mobile
    • imsi
    • mobile
    • msisdn
    • mobile.icc
  • provision.organization:
    • identifier
    • plan
  • provision.ticket:
    • identifier
    • location
    • owner
    • assignee
    • specificType
    • section
    • entity
    • assignedDate
    • answeredDate
    • updatedDate
    • restorationDate
    • resolutionDate
    • closedDate
    • parentTicket

Filtering logical operators list

  • and[]: And
  • or[]: Or

How to use filters

Let’s suppose we want to filter devices with device.operationalStatus equals to NORMAL and with device.communicationModules[].mobile.imei starting with 351873000102290.

If we were dealing with a SQL database we’d write the following SQL sentence:

SELECT * FROM device
WHERE device.operationalStatus LIKE 'NORMAL'
AND device.communicationModules[].mobile.imei LIKE '351873000102290'
Note

Remember, you can use all the data streams defined in the default data models and your own data streams in the WHERE clause.

Translating the previous SQL sentence to OpenGate searching API we’ll have:

{
  "filter": {
    "and": [
      {
        "like": {
          "provision.device.administrativeState": "NORMAL"
        }
      },
      {
        // The result will contain all devices with collected operational Status that
        // contains NORMAL and are related with communications modules with collected
        // imei containing 351873000102290
        "like": {
          "provision.device.communicationModules[].mobile.imei": "351873000102290"
        }
      }
    ]
  }
}

The result will contain all devices with collected operational Status that contains NORMAL and are related to communications modules with collected imei containing 351873000102290.

Another example comparing SQL to JSON, searching all devices except the one with serialNumber equal to 82A75D494B0EBF7A95587285AE78E83F:

SELECT * FROM device WHERE serialNumber <> '82A75D494B0EBF7A95587285AE78E83F'
/north/v80/search/devices
{
  "filter": {
    "neq": { "device.serialNumber": "82A75D494B0EBF7A95587285AE78E83F" }
  },
  "limit": {
    "start": 2
  }
}

More filtering examples

Click on any of the following links to expand the contents.

Searching using in and nin operators like an aggregation of ids
{
  "filter": {
    "and": [
      { "in": { "device.name": ["device_1", "device_2"] } },
      { "nin": { "device.description": ["device default description"] } }
    ]
  },
  "limit": {
    "start": 26,
    "size": 50
  }
}
Searching using the tag attribute
{
  "filter": {
    "eq": { "tag": "my_sticky_tag" }
  },
  "limit": {
    "start": 26,
    "size": 50
  }
}
Searching for collected hardware fields
{
  "filter": {
    "and": [
      { "like": { "device.model": "EF5" } },
      { "exists": { "device.identifier": true } }
    ]
  },
  "limit": {
    "start": 1,
    "size": 10
  }
}
Searching for software field (device whose software has this regex chain)
{
  "filter": {
    "like": { "device.software": "1.0" }
  },
  "limit": {
    "start": 1,
    "size": 10
  }
}
Retrieve all the operations in the catalog applicable to the Subscription & Asset entities
{
  "filter": {
    "in": { "operationEntityType": ["SUBSCRIPTION", "ASSET"] }
  },
  "limit": {
    "start": 1,
    "size": 10
  }
}
Searching for device alarms, /north/v80/search/entities/devices/alarms
{
  "filter": {
    "in": { "device.name": ["DEVICE22"] }
  },
  "limit": {
    "start": 1,
    "size": 10
  }
}
Searching for a particular data of a complex value
{
  "filter": {
    "like": {
      "provision.device.location._current.value.postal": "41015"
    }
  }
}

Selecting

By default, the search response includes all the data streams of the searched entities. You can retrieve only the information you need using the select sub-document in the search JSON.

The select sub-document can be used only on entity searching and must not be empty.

You can also use this sub-document when you search for information in CSV format.

Warning

If the size of the CSV file exceeds 18MB, you must paginate your searchings using the following parameters as HTTP headers:

  • page: It sets the CSV page you want.
  • size: It sets the number of rows you want in the CSV.

If the select clause isn’t in the filter, the behavior is the following:

  • In JSON format, the response will contain all the data streams collected or provisioned in the devices you are searching.
  • In CSV format, the search API raises an error in the response, asking for the select clause.

As described above, any data stream of the default data models or data models defined by the user can be used as select fields.

The order to apply the filters is securitization and next the following fields whenever there are resourceType, sort, filter, select (the data streams to show)

Select JSON object

  • select[]: Array of parameters to be selected.

    • name: String. Data stream name in the default or user-defined data models.

    • fields[]: Array of strings with the name of the fields to be retrieved.

      The possible values are: (See current object attributes table for field description):

      • value
      • date
      • at
      • from
      • tags
      • feedId
      • scoring.performance
      • scoring.qrating
      • provType
      • value.simplexAttribute: where simplexAttribute is an attribute of the complex object. For example, the provision.device.location is a complex data stream. If you need only de postal code, the value would be value.postal
    • alias: String. Shortname replaces the parameter’s full name when a CSV format is required. Example:

      • Using “alias”=“imei”
      • The device.communicationModules[].mobile.imei becomes imei in the CSV header

      The complete data stream name in the CSV header will appear if this field doesn’t exist. The CSV format shows this field, but the JSON format ignores it.

Select examples

Here’s how to search devices with a filter with a select clause

The following snippet shows the request using curl:

curl --request POST \
     --data-binary @subscription.json \
     --header "X-ApiKey: YOUR_API_KEY_HERE" \
     --verbose \
     https://[your_opengate_address]/north/v80/search/devices \
     -H "Content-type: application/json"

You include the filter with the select clause in the request’s body.

Filter using predefined device fields

{
  "select": [
    {
      "name": "provision.device.identifier",
      "fields": [
        {
          "field": "value",
          "alias": "id"
        },
        {
          "field": "date",
          "alias": "date"
        }
      ]
    }
  ]
}

Filter obtaining a field of a complex value

{
  "select": [
    {
      "name": "provision.device.location",
      "fields": [
        {
          "field": "value.postal",
          "alias": "Postal code"
        }
      ]
    }
  ]
}

Depending on the parameter header, the response can be in two different formats, see HTTP Header Options:

  • JSON Format (Default)
  • CSV Format

Response to the filter in JSON format example

{
  "devices": [
    {
      "id": "bca8cbbb-b151-442b-8b0d-96ed77789c45",
      "device": {
        "operationalStatus": {
          "current": {
            "value": "NORMAL",
            "date": "2017-02-02T09:05:58Z",
            "performance": 75
          }
        },
        "communicationsModules": [
          {
            "identifier": "351873000102290",
            "mobile": {
              "imei": {
                "current": {
                  "value": "351873000102290",
                  "date": "2017-02-02T09:05:58Z"
                }
              }
            }
          }
        ]
      },
      "health": {
        "heart": {
          "rate": {
            "current": {
              "value": 60,
              "date": "2017-02-02T09:05:58Z",
              "performance": 78
            }
          }
        }
      }
    }
  ]
}

Response to the Filter in CSV format example

device.operationalStatus.current.value;device.operationalStatus.current.updated;device.operationalStatus.current.performance;imei.current.value;imei.current.updated;imei.current.performance;rate.current.value;rate.current.updated;rate.current.performance;
"NORMAL";"2017-02-02T09:05:58Z";75;"351873000102290";"2017-02-02T09:05:58Z";;60;"2017-02-02T09:05:58Z";78

Sorting

The searching API allows ordering of the results. You can sort your search results, including an order object to the JSON.

Quick sort example

{
  "filter": {},
  "sort": {
    // Array of ordering parameters
    "parameters": [
      {
        // Name of parameter defined in the Default Datamodels
        "name": "provision.administration.identifier",
        // Enumeration string with ordering type. Valid values are:
        // - ASCENDING
        // - DESCENDING
        "type": "ASCENDING"
      }
    ]
  }
}

Grouping

Thanks to the group keyword in the search JSON, it is possible to group by some parameters of the default data models. This keyword is optional.

Quick grouping example:

{
  "filter": {},
  // Group sub-document
  "group": {
    // List of data streams to group by
    "parameters": [
      {
        "name": "provision.device.model"
      }
    ]
  }
}
List of data streams with grouping support
  • All enum values
  • device.model
  • device.software
  • device.communicationModules[].model
  • device.communicationModules[].software
  • device.communicationModules[].subscriber.model
  • device.communicationModules[].subscriber.software
  • device.communicationModules[].subscription.mobile.homePlmn
  • device.communicationModules[].subscription.mobile.homeOperator
  • device.communicationModules[].subscription.mobile.vlr.plmn
  • device.communicationModules[].subscription.mobile.vlr.operatorName
  • device.communicationModules[].subscription.mobile.vlr.countryName
  • device.communicationModules[].subscription.mobile.vlr.countryCode
  • device.communicationModules[].subscription.mobile.msc.plmn
  • device.communicationModules[].subscription.mobile.msc.operatorName
  • device.communicationModules[].subscription.mobile.msc.countryName
  • device.communicationModules[].subscription.mobile.msc.countryCode
  • device.communicationModules[].subscription.mobile.sgsn.plmnd
  • device.communicationModules[].subscription.mobile.sgsn.operatorName
  • device.communicationModules[].subscription.mobile.sgsn.countryName
  • device.communicationModules[].subscription.mobile.sgsn.countryCode
  • device.communicationModules[].subscription.mobile.ggsn.plmn
  • device.communicationModules[].subscription.mobile.ggsn.operatorName
  • device.communicationModules[].subscription.mobile.ggsn.countryName
  • device.communicationModules[].subscription.mobile.ggsn.countryCode
  • device.communicationModules[].subscription.mobile.registeredPlmn
  • device.communicationModules[].subscription.mobile.registeredOperator
  • provision.device.model
  • provision.device.software
  • provision.device.communicationModules[].model
  • provision.device.communicationModules[].software
  • provision.device.communicationModules[].subscriber.model
  • provision.device.communicationModules[].subscriber.software
  • provision.device.communicationModules[].subscription.mobile.homeOperator
  • provision.device.communicationModules[].subscription.mobile.registeredOperator
  • provision.administration.channel
  • provision.administration.organization
  • provision.administration.serviceGroup
  • provision.administration.plan

Group examples

Click on any of the following links to expand the contents.

Group Search Request example
{
  "filter": {
    "and": [
      {
        "like": {
          "provision.administration.organization": "battery_organization"
        }
      }
    ]
  },
  "group": {
    "parameters": [
      {
        "name": "provision.device.model"
      }
    ]
  }
}
Group Search Response example
{
  "summary": {
    "count": 6,
    "summaryGroup": [
      {
        "provision.administration.organization": {
          "count": 6,
          "list": [
            {
              "count": 6,
              "name": "battery_organization"
            }
          ]
        }
      },
      {
        "provision.administration.channel": {
          "count": 6,
          "list": [
            {
              "count": 6,
              "name": "battery_channel"
            }
          ]
        }
      },
      {
        "provision.device.model.name": {
          "count": 6,
          "list": [
            {
              "count": 1,
              "name": "model_name-7"
            },
            {
              "count": 2,
              "name": "model_name-4"
            },
            {
              "count": 1,
              "name": "model_name-10"
            },
            {
              "count": 1,
              "name": "model_name-5"
            },
            {
              "count": 1,
              "name": "model_name-11"
            }
          ]
        }
      }
    ]
  }
}

Pagination

The API allows you obtaining the response to a search in blocks with predefined number of results.

  • limit:
    • start: Page number you request. The count starts with the number 1
    • size: The number of entities that you can see on the page
Default number of items returned

The search API limits the page size to 50 items by default, but you probably have thousands of devices. How do you walk through all your devices?

Well, let’s suppose you have exactly 2500 devices matching your query. Obviously, your result exceeds the default limit. In this case, you’ll find a page object in your response.

Paginated examples

Paginated example response

{
    "page" : { "number" : 1 },
    "resources" : [
        { ... },
        { ... },
        { ... },
        { ... },
        ...
        { ... }
    ]
}

Warning about the previous example

Warning

Please, take the resources field on the previous example as a placeholder for any reserved word into the scope of the searched resource: entities, devices, subscriptions, data models, bundles, data streams, data points, etc.

The number attribute is the current number of pages based on the limit setup.

What can you do to get the following page? It’s easy. You only have to include a limit object in your query. See next example.

Pagination example request

{
  "limit": {
    "start": 2
  }
}

Paginated example generic response

{
   "page" : { "number" : 2 },
   "resources" : [
       { ... },
       { ... },
       { ... },
       { ... },
       ...
       { ... }
   ]
}

See previous warning about the resources word in the example.

You can change the page limit from the beginning. Supposing you want to retrieve 50 items per query, you must set up the limit object with a starting point and the page size you want.

Paginated example request

Changing the starting page and the limit

{
  "limit": {
    "start": 2,
    "size": 50
  }
}

The top margin for the page size in the limit object is 1000. You’ll receive a server error response if you set up a size attribute over this limit.

Another example of pagination response

With the starting page and the limit changed

{
    "page" : { "number" : 2},
    "resources" : [
         { ... },
         { ... },
         { ... },
         { ... },
         ...
         { ... }
    ]
}

See previous warning about the resources word in the example.

Summary

Responses to all search requests include a summary object with different counters regarding the results obtained. It is closely related to the grouping feature.

By default, the summary always shows the total count, the organization’s grouping counter, and the channel grouping counter.

  • count (field): number of occurrences found in the whole search
  • summaryGroup []: array of type of summarized specific object structure
    • SpecificObjectParameterDatamodel: object inside the Parameter of the data models
      • count: number of these specific elements found
      • list: array of each type of summarized element
        • count: number of these specific elements found
        • name: value of the parameter of the data model

Here’s how to search devices with a summary without a group clause

This is the request using curl:

curl --request POST \
     --data-binary @subscription.json \
     --header "X-ApiKey: YOUR_API_KEY_HERE" \
     --verbose \
     https://[your_opengate_address]/north/v80/search/devices/summary \
     -H "Content-type: application/json"

Some grouping examples

Response summary without grouping

{
  "summary": {
    "count": 6,
    "summaryGroup": [
      {
        "provision.administration.organization": {
          "count": 6,
          "list": [
            {
              "count": 6,
              "name": "organization1"
            }
          ]
        }
      },
      {
        "provision.administration.channel": {
          "count": 6,
          "list": [
            {
              "count": 6,
              "name": "channel1"
            }
          ]
        }
      }
    ]
  }
}

This is the request using curl:

curl --request POST \
     --data-binary @subscription.json \
     --header "X-ApiKey: YOUR_API_KEY_HERE" \
     --verbose \
     https://[your_opengate_address]/north/v80/search/devices/summary \
     -H "Content-type: application/json"

In the body of the request, you include the filter with the select clause.

Example Filter using group clause

{
  "filter": {
    "and": [
      {
        "like": {
          "provision.administration.organization": "battery_organization"
        }
      }
    ]
  },
  "group": {
    "parameters": [
      {
        "name": "provision.device.model"
      }
    ]
  }
}

Response summary with group clause

{
  "summary": {
    "count": 6,
    "summaryGroup": [
      {
        "provision.administration.organization": {
          "count": 6,
          "list": [
            {
              "count": 6,
              "name": "battery_organization"
            }
          ]
        }
      },
      {
        "provision.administration.channel": {
          "count": 6,
          "list": [
            {
              "count": 6,
              "name": "battery_channel"
            }
          ]
        }
      },
      {
        "provision.device.model.name": {
          "count": 6,
          "list": [
            {
              "count": 1,
              "name": "model_name-7"
            },
            {
              "count": 2,
              "name": "model_name-4"
            },
            {
              "count": 1,
              "name": "model_name-10"
            },
            {
              "count": 1,
              "name": "model_name-5"
            },
            {
              "count": 1,
              "name": "model_name-11"
            }
          ]
        }
      }
    ]
  }
}