OData Reference
Check out our Postman Collection to see OData in action
$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 |
OpenHouse dates and times are exposed in the timezone of the MLS
Note, for positive offsets, make sure to encode the +
sign as %2B
e.g. 2020-04-01T12:30:30%2B05: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).
The groupby function will only return the first 10,000 unique values.
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
.
After the first request, subsequent replication requests will be of the form /trestle/odata/<resource>/replication?id=<guid>
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.nextLink
s. It now contains a new replication link containing a unique id for the replication request.
Returned replication links expire after 5 minutes of inactivity
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.