Resource search

The Brightpearl API is a RESTful API, and most definitions of REST speak in terms of manipulating resources. Our interpretation of REST is that resources are entities such as WarehousesJournals and Contacts; solid real-world concepts that Brightpearl can be used to manage.

These resources can be manipulated by the core HTTP methods (GET, POST, PUT, DELETE). The 'destructive' methods (PUT, POST, DELETE) are generally not controversial - we think it is reasonable to assume that whenever you want to perform one of these actions, you know in advance which resources you want to manipulate. This is why all destructive methods require you to specify an  ID  for every resource that should be affected.

The non-destructive GET method is more complicated. It is often the case that you do not know the IDs  of the resources you want or that you want a subset of resources that share some data in common (all of your Contacts at a particular company, for example). In these cases it is obvious that some sort of search mechanism is required.

How other APIs perform searches (and why we think it's a bad idea)

The most common mechanism for adding search functionality to a RESTful API is allow resource GET messages to accept parameters like this hypothetical Contact GET URI:

contact?company=Brightpearl

which would return all the Contact resources with the company name of 'Brightpearl'. This looks obvious and elegant, but we think it causes some subtle problems:

Message complexity, size and speed

Even though our JSON is considerably less verbose than an XML equivalent would have been, our messages are generally designed to be logical, readable, unambiguous and self-contained which means that complex resources can be large and have several levels of structure.

A good example of this is our Order resource. It is quite likely that a consumer of the Brightpearl API might want to generate a page or screen which shows sales orders that have not yet been shipped with some simple data like customer name, date of sale and current status.

With a search mechanism that is coupled to the resource GET mechanism, you would have a lot of large, complex Order  resources that would need to be parsed and interrogated before a results page could be displayed.

Consistency

In our experience, API developers who link the resource GET and search mechanisms together have a difficult time in maintaining consistency across those resources. Parameter names, pagination and filters gradually get out of synch and the integrator ends up having to write separate code for each type of search.

We believe developers would prefer a consistent search interface which is applicable to all types of resources.

Cachability

We want, wherever possible, to encourage client-side caching of data retrieved via the Brightpearl API. Using cached data generally means better performance for your application, and less load on the Brightpearl servers.

When the resource GET and search mechanisms are linked, caching is broken is two ways:

  1. Complete resources are being returned with every search, regardless of whether or not the calling application has a cached copy of that result.
  2. Searches almost always provide search terms and pagination instructions as query parameters at the end of a URI. According to the HTTP specifications, well behaved HTTP caches should not cache URIs that include query parameters.

We believe, that wherever possible, a developer should have control over where and how resources are cached.

Searching across Brightpearl resources

Our implementation of resource searching is that every 'searchable' resource has a resource GET message and separate resource-search GET message. For example:

/contact/{id-set}
/contact-search?{parameters}

The body of search message responses are split into two parts:

  1. A meta-data block describing the results (column names, types, results available, results shown etc)
  2. A list of results represented as a two-dimensional array

Each search message follows a common pattern for specifying columns to show, values to filter on, pagination controls, sort controls as well the structure of the results. Once you have integrated with one search message, you will be able to re-use that code for all other search messages.

Resource search column & filter data types

INTEGER A number with no fractional component. Most often used to represent IDs. Can also be used for negative searching via the ¬ operator. For example, to search for ids not 4: id=¬4
IDSET A representation of one or more IDs. For example 1,3,6-10.
STRING Unicode String (Brightpearl API messages are encoded in UTF-8). Columns or filters with the data type STRING use exact matching only.
DATETIME An ISO 8601 date/time. Ranges are represented as a start date/time then an end date/time (e.g. 2007-03-01T13:00:00Z/2008-05-11T15:30:00Z )
BOOLEAN true or false
SEARCH_STRING Unicode String that can be matched against case-insensitive substrings (e.g. companyName=RIGHT would match a contact with the company name Brightpearl)
STRING_SET A representation of one or more String values (e.g. productStatus=LIVE.DISCONTINUED would match a product with LIVE or DISCONTINUED status)
FREE_TEXT_QUERY A type only used on filters which allows a result to be matched if any column with the type SEARCH_STRING contains the supplied sub-string. For examplekeyword=right would match contacts with an email address of brightpearl.com or a company name Brightpearl
PERIOD A period value can be specified as either an ISO 8061 date(e.g 2012-01-09) or date/time(e.g 2007-03-01T13:00:00Z).

 

Date only:

  • A single date, before a particular date , after a particular date or between particular dates
  • Single date e.g 2010-12-16
  • After a particular date (exclusive) i.e return me all the dates after the start date e.g 2010-12-16/
  • Before a particular date (exclusive) i.e. return me all the dates before the end date e.g /2010-12-16
  • Between particular dates (inclusive) i.e. return me all the dates between the start and end dates including the start and end dates e.g. 2010-12-10/2010-12-16.
Date(T)Time:
  • With date/time the comparisons are against the exact time.
  • You can provide the time with or without the seconds, i.e 2012-04-13T09:45 will be considered to be 2012-04-13T09:45:00
  • In order to retrieve results for a specific time we can use : 2012-04-13T09:45:36
  • In order to retrieve results before a specific time(exclusive) we can use : /2012-04-13T09:45:36
  • In order to retrieve results after a specific time(exclusive) we can use : 2012-04-13T09:45:36/
  • In order to retrieve results between two specific times(exclusive) we can use : 2012-04-13T09:45:36/2012-04-13T09:45:40

Most of the resources that can be manipulated using the Brightpearl API have a 'resource search' message as well as a GET message. These messages are used to find those resources that share a particular property (all Contacts at a company) or are in a particular state (all Goods-Out Notes that are ready to ship).

If you have not already done so, it is worth reading our introduction to resource searches, which explains why we have implemented searches in this way, and how it might differ from other APIs you may have used.

The resource search messages behave very differently to the other messages you will encounter while using the Brightpearl API. Our resource messages are designed to be readable and unambiguous, while our resource search messages are designed to be terse, fast and behave in the same way no mater which type of resource you are working with.

As a result, it will take a little longer to understand how resource searches work, but your knowledge and any code you write can be reused for every resource that you need to search against.

All the examples below are taken from the Contact Search and Goods-Out Note Search resource searches.

Key Concepts

Each of these concepts is explored in more detail below, but these are the fundamentals of using a Brightpearl resource search.

Issuing a search

Resource searches are implemented as HTTP GET messages. The path for the resource search will always be the same as the path to the resource GET with an additional -search suffix.

For example, Contact GET has the path /contact, so Contact Search has the path /contact-search

It is pefectly valid to make a request using just this base path. Making a request to /contact-search will return you a single 'page' of results from the set of all the Contacts stored in your Brightpearl account, using default page sizes, columns and sorting.

Meta data

In addition to our online documentation, it is possible to programmatically discover the capabilites of a resource search by issuing a meta-data request. For example /contact-search/meta-data GET will show you the columns and filters available as well as default values for pagination and sorting.

Columns

Each resource search supports a set of fixed set of columns - data that will be included in your results like contact names and email addresses. By default, every result will include a value for each column (although that value may be null) it is possible to change the set and order of the columns returned in the results.

You can constrain your search by specifying one or more column names as query parameters in your resource search URI. For example:

/contact-search?primaryEmail=brightpearl.com&firstName=Ben

will only include results where the contact's email address contains brightpearl.com and their first name contains Ben. The allowed values for columns varying according to the data type for that column

Filters

A filter works in an identical manner to a column, but does not directly map to a column that is included in a result.

Pagination and sorting

Resource searches have a builtin mechanism for breaking large result sets into multiple pages. These are discussed in detail later in this guide.

Response structure

The response from a resource search is broken into three sections: meta-data, results and reference data. An example response from a Contact Search might look like:

{
  "response": {
    "metaData": {
      "resultsAvailable": 1,
      "resultsReturned": 1,
      "firstResult": 1,
      "lastResult": 1,
      "columns": [
        {
          "name": "contactId",
          "sortable": true,
          "filterable": true,
          "reportDataType": "INTEGER",
          "required": false
        },
        {
          "name": "primaryEmail",
          "sortable": true,
          "filterable": true,
          "reportDataType": "SEARCH_STRING",
          "required": false
        },
        {
          "name": "firstName",
          "sortable": true,
          "filterable": true,
          "reportDataType": "SEARCH_STRING",
          "required": false
        },
        {
          "name": "lastName",
          "sortable": true,
          "filterable": true,
          "reportDataType": "SEARCH_STRING",
          "required": false
        }
      ],
      "sorting": [
        {
          "filterable": {
            "name": "lastName",
            "sortable": true,
            "filterable": true,
            "reportDataType": "SEARCH_STRING",
            "required": false
          },
          "direction": "ASC"
        },
        {
          "filterable": {
            "name": "firstName",
            "sortable": true,
            "filterable": true,
            "reportDataType": "SEARCH_STRING",
            "required": false
          },
          "direction": "ASC"
        }
      ]
    },
    "results": [
      [
        4,
        "admin@email.com",
        "Primary",
        "Admin"
      ]
    ]
  },
  "reference": {}
}

Meta Data

As the name suggests, this section contains information describing the results. The first four properties are to do with pagination:

resultsAvailable The total number of results that match the supplied filters/columns. Depending on your pagination parameters, this may be more than the number of results actually returned in this response.
resultsReturned The number of results included in this response.
firstResult A 1 based index indicating where the first result in the response falls in the total set of results. For example if you have a page size of 10 results, firstResulton page one will be 1 and firstResult on page two will be 11
lastResult A 1 based index indicating where the last result in the response falls in the total set of results. For example if you have a page size of 10 results, lastResulton page one will be 10 and lastResult on page two will be 20 (assuming there are at least 20 results).

Columns

The columns property in the metaData object describes the data that is included in the results property of the response. The properties present in each column have the following meanings:

name This is name of the query parameter you would pass into a resource search if you wanted to match against that column.
sortable Indicates that the results can or cannot be sorted by this column (see sorting below)
filterable Indicates that you can or cannot match against this column.
reportDataType The data type for that column .
referenceData If a referenceData property is present on a column, it means that the values for that column in the results, can be used to look up an entry in one or more of the reference data maps provided in the response (see below).
required A small minority of searches specify columns for which you must provide a value to match against.

Sorting

This section explains what sorting has been applied to the results. All searches have a default sort order, so this section is always present. In the above example, the results have been primarily been ordered according to the contact's last name, the by first name in the event of more than one contact having the same last name.

The direction property is either ASC for ascending, or DESC for descending.

Results

The results property on the response contains the actual results of the search. The property is a two-dimensional array (an array of arrays). Each array is of the same length and that length matches the number of columns present in the meta data section at the top of the response.

So results from the contact search might look like:

results: [
          [4,"some.user@brightpearl.com","Some","User"],
          [5,"someone.else@brightpearl.com","Someone","Else"]
]

In this example, our search has returned two results. The order of the data in the result array matches the order of the columns in the meta data section at the top of the response.

Reference

The reference property at the bottom of the response is only present if one or more of the columns in the meta data section has the referenceData property set.

The purpose of the reference section is to avoid duplicating data in the results section. A good example of this is a Goods-Out Note Search . Each Goods-Out Note  is associated with a Warehouse  you may have hundreds of Goods-Out Notes, but only a couple of Warehouses. Instead of repeatedly returning a potentially long warehouse name hundreds of times, only the ID of the Warehouse is present in the result.

The definition of the warehouseId column might look like:

"name": "warehouseId",
"sortable": true,
"filterable": true,
"reportDataType": "INTEGER",
"referenceData": ["warehouseNames"],
"required": false

Which explains that the value of each warehouseId column in the results can be used to find a value from a map called warehouseNames in the reference property.

In this example, the reference section of the results might look like:

"reference": {
    "warehouseNames": {
      "2": "Bristol Primary Shipping",
      "3": "Reading Returns and B-grade"
    }
}

As you can see, the reference property is a hash-table of hash-tables.

Specifying a sort order

When you execute a resource, you may change the default sort order by setting a query parameter sort. The value of this parameter is a comma delimited list of 1-n column names with an optional sort direction. For example:

/goods-out-note-search Default sort order
/goods-out-note-search?sort=warehouseId|ASC Sort by warehouseId in ascending order
/goods-out-note-search?sort=warehouseId,price|DESC Sort by warehouseId initially, then price in descending order.

Pagination

You can control the number of results that are included in the response by using two pagination query paramters:

pageSize This parameter sets the maximum number of results that will be returned in a response. The maximum value for this parameter is 500.
firstResult This is the (one based) index of the first result you would like in your results. For example, if there are 100 potential results and you set this parameter to 11 andpageSize to 10, you will receive results 11-20 inclusive.

Advanced features

Column set and ordering

You may control the set of columns that are included in the results and the order in which they are presented. You may use the columns parameters and provide a comma separated list of column names.

/contact-search?columns=contactId Only the contactId will be included in the results.
/contact-search?columns=contactId,firstName Only contactId and firstName will be returned. The first column in each result and in the meta data will becontactId.
/contact-search?columns=firstName,contactId Only contactId and firstName will be returned. The first column in each result and in the meta data will befirstName.
 
Have more questions? Submit a request

0 Comments

Please sign in to leave a comment.