cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Did you get called away in the middle of writing a post? Don't worry you can find your unfinished post later in the Drafts section of your profile page. X

More Data with the V2/REST APIs!!

No ratings


The Axeda Platform has long had the ability to write custom logic to retrieve, manipulate and create data.  In the current versions of the Platform, there are two classes of API, Version 1 (v1) and Version 2 (v2).  The v1 APIs allow a developer to work with data on the Platform, but all of the APIs are subject to the maxQueryResults configuration property, which by default limits the number of results per query to 1000. For some subsets of data, this can be inadequate to process data.  In comes the v2 API, which introduces pagination.


One of the first things a new user does when exploring the V2 API, is something like the following:

HistoricalDataItemValueCriteria criteria = new HistoricalDataItemValueCriteria()

criteria.assetId = '9701'

criteria.startDate = '2014-07-23T12:33:00Z'

criteria.endDate = '2014-07-23T12:44:00Z'

DataItemBridge dbridge = com.axeda.sdk.v2.dsl.Bridges.dataItemBridge

FindDataItemValueResult results = dbridge.findHistoricalValues(criteria)

         


And they get frustrated when they only get the same 100 rows of data.  Repeat after me: V2 API invocations (find operations) are limited to batches of 100 results at a time!


But that's not the end of the story.  With a small change, the query above can be tuned to iterate through all results that match the search criteria: 


HistoricalDataItemValueCriteria criteria = new HistoricalDataItemValueCriteria()

criteria.assetId = '9701'

criteria.startDate = '2014-07-23T12:33:00Z'

criteria.endDate = '2014-07-23T12:44:00Z'

criteria.pageNumber = 1

criteria.pageSize = 100 // Default.

DataItemBridge dbridge = com.axeda.sdk.v2.dsl.Bridges.dataItemBridge

FindDataItemValueResult results = dbridge.findHistoricalValues(criteria)

tcount = 0

while ( (results = dbridge.findHistoricalValues(criteria)) != null  && tcount < results .totalCount) {

  results.dataItems.each { res ->

    tcount++

  }

  criteria.pageNumber = criteria.pageNumber + 1

}

  

I currently recommend that people avoid using the count() or countDomainObjectByCriteria() functions if you're then going to call a find.  Currently both the count*() and find functions compute total results, and doubles execution time of just those two calls.  Total count is only computed when running the first find() operation, so the code pattern above is so far the most efficient way I've seen to run these operations on the platform.

So having covered how to do this in code (custom objects), let's turn our attention to the REST APIs - the other entry-point for using these capabilities.  The REST API doesn't offer a count*() function, but the first find() invocation (if using XML) brings back totalCount as part of the result set.  You can use this in your application to decide how many times to call the REST end-point to retrieve your data.  So for the example above:

POST:  https://customer-sandbox.axeda.com/services/v2/dataItem/findHistoricalValues

HEADERS:

Content-Type: application/xml

Accept: application/xml

BODY:

<?xml version="1.0" encoding="UTF-8"?>

<HistoricalDataItemValueCriteria xmlns="http://www.axeda.com/services/v2" pageSize="100" pageNumber="1">

<assetId>9701</assetId>

<StartDate>2014-07-23T12:33:00Z</StartDate>

<endDate>2014-07-23T12:35:02Z</endDate>

</HistoricalDataItemValueCriteria>

    



RESULTS:

<v2:FindAssetResult totalCount="1882" xmlns:v2="http://www.axeda.com/services/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

   <v2:criteria pageSize="100" pageNumber="1">

      <v2:name>*</v2:name>

      <v2:propertyNames/>

   </v2:criteria>

   <v2:assets>

   </v2:assets>

</v2:FindAssetResult>

    


Or JSON:


POST:  https://customer-sandbox.axeda.com/services/v2/dataItem/findHistoricalValues

HEADERS:

Content-Type: application/xml

Accept: application/xml

BODY:

{

  "id":  9701,

  "startDate": "2014-07-23T12:33:00Z",

  "endDate": "2014-07-23T12:35:02Z",

  "pageNumber": 1,

  "pageSize": 2

}

    

And that's how you work around the maxQueryResults limitation of the v1 APIs.  Some APIs do not currently have matching v2 Bridges (e.g. MobileLocation and DataItemAssociation), in which case the limitation will still apply.  Creative use of the query Criteria will allow you to work around these limitations as we continue to improve the V2 API.

Regards,

-Chris

Comments

Thanks Chris.

One small correction. I think you meant to write "criteria.pageNumber ++" in the below block:

while ( (results = dbridge.findHistoricalValues(criteria)) != null  && tcount < results .totalCount) { 

  results.each { res -> 

    tcount++ 

  } 

  results.pageNumber++ 

Also, for some use cases, it would be a performance gain to be able to increase the page size. Food for thought

Thank you - I have made that correction. 

-Chris Kaminski

PTC Customer Support

Chris,

I used your example and can list the historical data, see code below, but I cannot figure out how to show the date for each of the data values.

Can you show me how to retrieve the date for each historical data value?

Thanks,

John

import static com.axeda.sdk.v2.dsl.Bridges.*

import com.axeda.services.v2.*

def asset = assetBridge.find("590ND32DNZA2MNZA2B71||916512000219")

assert asset

logger.info("found asset");

def findResult = dataItemBridge.findCurrentValues(

new CurrentDataItemValueCriteria(

assetId: asset.systemId,

//name: "* level",

types: [DataItemType.ANALOG]))

findResult.dataItemValues.each { logger.info("Current Data: ${it.dataItem.label}: ${it.value}") }

HistoricalDataItemValueCriteria criteria = new HistoricalDataItemValueCriteria() 

 

criteria.assetId = asset.systemId; 

criteria.startDate = new Date() - 365;

criteria.endDate = new Date(); 

criteria.pageNumber = 1 

criteria.pageSize = 100 // Default. 

 

FindDataItemValueResult findHistResult = dataItemBridge.findHistoricalValues(criteria);

tcount = 0 

while ( (findHistResult = dataItemBridge.findHistoricalValues(criteria)) != null  && tcount < findHistResult.totalCount)

  findHistResult.dataItemValues.each {  

    tcount++;

   logger.info("Historical Data: ${it.dataItem.label}: ${it.value}");

  }

 

  criteria.pageNumber = criteria.pageNumber + 1 

  logger.info("Historical Data: ${it.dataItem.label}: ${it.value}");

should become:

  logger.info("Historical Data: ${it.dataItem.label}: ${it.value} : ${it.timestamp} ");

I'm not sure how familiar with Groovy closures you are, but 'it' is a special alias.  You can change this:

  findHistResult.dataItemValues.each {  

to this

  findHistResult.dataItemValues.each {   dataItemValue ->

If it helps (or some other name).  If you don't give one, Groovy uses 'it' as a default.

So dataItemValue.dataItem.label is the name of the data item, dataItemValue.value is it's current value, and dataItemValue.timestamp is the time in milliseconds from EPOCH (in GMT).

Version history
Last update:
‎Apr 21, 2015 10:04 AM
Updated by:
Labels (1)