OData Reference
$filter
GET /trestle/odata/Property?$filter=ListingKey%20eq%20%27123456%27 HTTP/1.1
Host: api-prod.corelogic.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni...3wQJVBA
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.filter("ListingKey eq '123456'")
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.filter(oDataClient.getFilterFactory().and(
oDataClient.getFilterFactory().ge("BedroomsTotal", 4),
oDataClient.getFilterFactory().ge("BathroomsTotalInteger", 2)
))
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
var serviceUri = "https://api-prod.corelogic.com/trestle/odata/";
var client = new RestClient(serviceUri);
var request = new RestRequest("Property");
request.AddParameter("$filter", "ListingKey eq '123456'", ParameterType.QueryString);
var response = client.Execute(request);
The Basics
| Operator | Meaning | Example |
|---|---|---|
eq |
Equal To | ListingKey eq '123456' |
ne |
Not Equal To | AssociationYN ne True |
gt |
Greater Than | BedroomsTotal gt 3 |
ge |
Greater Than or Equal To | BathroomsTotalInteger ge 2 |
lt |
Less Than | ListPrice lt 350000 |
le |
Less Than or Equal To | LotSizeAcres le 5 |
and |
Both conditions are true | BedroomsTotal eq 3 and BathroomsTotalInteger eq 2 |
or |
Either condition is true | BedroomsTotal ge 3 or BathroomsTotalInteger ge 3 |
not |
Condition is not true | not (BedroomsTotal eq 3) |
has |
Multi-Select Has Value | WaterfrontFeatures has CanalAccess |
in |
Value is in List | StandardStatus in ('Active', 'Pending') |
These operators can be combined to form very complex queries, e.g.
((ListPrice lt 300000 and LotSizeAcres lt 3) or (BedroomsTotal gt 4 and StandardStatus eq 'Active'))
Working with numbers
There are three basic types of numbers represented in OData: integer, decimal, and floating point. The integers are all signed and range from 8-bit (Edm.SByte) to 64-bit (Edm.Int64). The floating point numbers can be 32-bit single precision (Edm.Single) or 64-bit double precision (Edm.Double).
When working with mixed numeric types (e.g. Edm.Int32 and Edm.Double) values get “promoted” to larger types according to the following precedence table:
| Type | Description |
|---|---|
Edm.Double |
64-bit double precision floating point |
Edm.Single |
32-bit single precision floating point |
Edm.Decimal |
Numeric value with a decimal |
Edm.Int64 |
64-bit signed integer |
Edm.Int32 |
32-bit signed integer |
Edm.Int16 |
16-bit signed integer |
Edm.SByte |
8-bit signed integer |
The standard arithmetic operators are available in OData:
| Operation | OData representation |
|---|---|
| Addition | Value1 add Value2 |
| Subtraction | Value1 sub Value2 |
| Multiplication | Value1 mul Value2 |
| Division | Value1 div Value2 |
| Modulo | Value1 mod Value2 |
| Negation | -Value1 |
Working with strings
Strings are relatively straight forward in Trestle. They are always enclosed in single quotes (') and if you need a single quote in the string, you simply double it up, e.g. 'O''Brien'.
In addition to all of the basic operators that you can use with strings, there are functions to help out as well:
| Function | Description |
|---|---|
contains(haystack, needle) |
Returns true if needle is found in haystack |
endswith(haystack, needle) |
Returns true if haystack ends with needle |
startswith(haystack, needle) |
Returns true if haystack starts with needle |
length(value) |
Returns the length of the string value |
indexof(haystack, needle) |
Returns the 0-based position of needle in haystack |
tolower(value) |
Returns the string with all characters lower-cased |
toupper(value) |
Returns the string with all characters upper-cased |
trim(value) |
Returns the string with all leading and trailing whitespace removed |
Working with dates and times
The dates and timestamps exposed through Trestle are exposed in UTC. You can use the following formats to refer to the dates or times:
| Description | Example |
|---|---|
| Date in YYYY-MM-DD format | 2020-04-01 |
| UTC timestamp in 24 hour format | 2020-04-01T12:30:30Z |
| Non-UTC timestamp in 24 hour format | 2020-04-01T12:30:30-05:00 |
| Non-UTC timestamp with miliseconds in 24 hour format | 2020-04-01T12:30:30.00-05:00 |
Working with enumerations
While the OData spec indicates that enumerations should be referenced with their schema, e.g. CoreLogic.DataStandard.RESO.DD.Enums.StandardStatus'Closed', Trestle allows you to shortcut that by simply writing 'Closed'. For multi-select enumerations, you can use the has operator, e.g.:
- Check for one value:
Appliances has 'Dishwasher' - Check for two values:
Appliances has 'Dishwasher,RangeHood'
$select
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.select("ListingKey,StandardStatus")
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
You can limit the fields you want with $select. For example if you want just the ListingKey and the StandardStatus, you would use $select=ListingKey,StandardStatus. One caveat is that if you only select an expanded value (e.g. $select=Rooms), you will get all of the fields back in addition to the expanded value.
$count
GET /trestle/odata/Property?$count=true HTTP/1.1
Host: api-prod.corelogic.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni...3wQJVBA
var client = new RestClient("https://api-prod.corelogic.com/trestle/odata/Property?$count=true");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni...3wQJVBA");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.count(true)
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
The resulting JSON will include
@odata.count
{
"@odata.context": "https://api-prod.corelogic.com/trestle/odata/$metadata#Property/CoreLogic.DataStandard.RESO.DD.Property",
"@odata.nextLink": "https://api-prod.corelogic.com/trestle/odata/Property?$count=true&$skip=10",
"@odata.count": 980150,
"value": [
]
}
Using $count=true will return the total number of results that match your query. Because of the limits on how many records you get per query, the total count will often be more than the number of records returned by the query. To accoutn for this, you can use $top and $skip or the replication endpoint to iterate through the records until you download the total number of records.
$top
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.top(1000)
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
Use $top to specify the number of records you want returned. For most queries, the maximum you can return is 1,000. For queries that only select key fields, you can return up to 300,000. If you need to retrieve more than 1,000,000 records, you will need to use the replication endpoint.
$skip
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.skip(1000)
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
Use $skip to skip over a number of records. This is most useful in combination with $top to iterate over the results of a query that return more than 1,000 records.
$expand
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.expand("CustomProperty")
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
Expand can take options as well
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
Map<QueryOption, Object> expandOptions = new HashMap<QueryOption, Object>();
expandOptions.put(QueryOption.SELECT, "MediaURL");
expandOptions.put(QueryOption.TOP, 1);
expandOptions.put(QueryOption.ORDERBY, "Order");
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.expandWithOptions("Media", expandOptions)
.build();
You can use $expand to pull in data from related resources. In its simplest form, it looks like $expand=Rooms, which would pull in all of the associated rooms data. You can pull in data for more than one resource by listing them out separated by commas, e.g. $expand=Rooms,Units. You can also specify OData query parameters, e.g. $expand=Media($select=MediaURL;$top=1;$orderby=Order) to get just the MediaURL associated with the first photo for the listing.
$apply
Trestle has begun to implement the OData Extension for Data Aggregation. As of now, Trestle provides support for the following pieces of the extension:
$apply=groupby() (partial)
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.applyCustomQuery("$apply", "groupby((PostalCity))")
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
You can use $apply=groupby() to get a list of unique values for a field or set of fields. For example, if you wanted to get a list of PostalCity values available to you, you would use $apply=groupby((PostalCity)). Note that there are two sets of parentheses. You are passing a set of field names as the first parameter to the function (note: Trestle does not currently support the second parameter).
Trestle OData Extensions
Replication=True
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.addCustomQueryOption("Replication", "true")
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
{
"@odata.context": "https://api-prod.corelogic.com/trestle/odata/$metadata#Property/CoreLogic.DataStandard.RESO.DD.Property",
"@odata.nextLink": "https://api-prod.corelogic.com/trestle/odata/Property/replication?id=123-a119257f-5dc0-4a47-bbb3-e526efd22656",
"value": [ ... ]
}
If you are replicating data from Trestle, then you are likely already using the standard pattern of specifying a $top and a $skip to page over the results. If, however, you are attempting to replicate a resource that has more than one million (1,000,000) records in it, then you will need to use Trestle’s replication endpoint.
Using the replication endpoint is a two-step process. The first step is to indicate to Trestle that you want to use the replication endpoint. You do this by adding replication=true as a query parameter, e.g. /trestle/odata/Property?$select=ListingKey,StandardStatus,ListPrice&replication=true.
The second step is to repeatedly call the returned @odata.nextLink until you have fully replicated the data set. After the first request where you pass replication=true, you will get links that look like /trestle/odata/Property/replication?id=123-a119257f-5dc0-4a47-bbb3-e526efd22656.
Beyond the maximum number of records retrievable, there are two main differences between replicating with the replication end point and with $top/$skip. The first is that you cannot skip records, so if your replication process fails part way through, you will have to restart it. The second is that you will need to use the returned @odata.nextLink instead of computing the next URL yourself.
As you can see in the provided JSON example, the @odata.nextLink looks different from the normal @odata.nextLinks. It now contains a new replication link containing a unique id for the replication request.
PrettyEnums=True
String service_uri = "https://api-prod.corelogic.com/trestle/odata/";
ODataClient oDataClient = ODataClientFactory.getClient();
URI uri = oDataClient.newURIBuilder(service_uri)
.appendEntitySetSegment("Property")
.addCustomQueryOption("PrettyEnums", "true")
.build();
ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);
<Member Name="ActiveUnderContract" Value="1">
<Annotation Term="RESO.OData.Metadata.StandardName" String="Active Under Contract" />
</Member>
The OData specification defines enumeration values to consist only of letters, numbers, and the underscore (_) character. Trestle complies with these specifications, so the data you get from Trestle will look like ActiveUnderContract instead of Active Under Contract.
The metadata does provide annotations for the enumerations as seen in the XML sample. While using the annotations in the metadata is The Right Way to get the human-friendly versions of the enumerated values, Trestle provides a simpler way: passing PrettyEnums=true in your query.
With PrettyEnums=true, Trestle will do the translations for you, so that instead of ActiveUnderContract, you will get Active Under Contract. In this way, Trestle simplifies the process of pulling data and displaying it to your end users.