We continue to listen to customers and improve the Graph Directory Service development experience.  Today we'd like to announce support for queries with disjunctive/OR filter clauses.  The previous version of the service allowed filter clauses to be joined solely by the AND logical operator, i.e. such that all filter clauses must evaluate to true for the directory objects to match.  With the support of both AND and OR logical operators, more comprehensive filtered searches can be applied to the directory.

Disjunctive/OR Filter Clause Support

The logical OR operator allows for a series of filter clauses to evaluate to true if at least one of the clauses evaluates to true.  A simple example of such a filter is as follows:
 
    GET /contoso.com/users?api-version=2013-11-08&$filter=startswith(displayName,'james') or startswith(givenName,'james')
 
This request will return all User objects that either have a displayName or givenName value starting with "james".  Note the api-version argument in the above example is 2013-11-08, but the logical OR operator is usable across all service versions, e.g. 2013-04-05.

Paging

*Update: 3/13/2014* Paging is now supported for filtered searches.  The service will return the first page of results, along with a "odata.nextLink" property value, indicating that more results are available, and also representing the link relative to the service root that can be followed to get the next page of results. An example is as follows:
 
    odata.nextLink=directoryObjects/$/Microsoft.WindowsAzure.ActiveDirectory.User?$filter=userPrincipalName ge 'jon'&$skiptoken=X'4453...'

One key constraint to point out is that a given next-page request cannot be replayed, i.e. it can only be requested once.  This means that if there's a network error that occurs between your client and the directory service on such a request, you may have to restart the filtered search.

Additional Examples

A common usage scenario for user interfaces is to offer a simple search using a single text field. Such an interface might make the following request, given an input of "mary":
 
    GET /contoso.com/users?api-version=2013-11-08&$filter=startswith(displayName,'mary') or startswith(givenName,'mary') or startswith(surname,'mary') or startswith(mail,'mary') or startswith(userPrincipalName,'mary')
 
Note that there are many properties that might contain the text the user is searching for.  Another similar case might process the input text "John Riddell" by searching for Contact objects either matching the displayName, or when the first token matches the givenName and the second token matches the surname:
 
    GET /contoso.com/contacts?api-version=2013-11-08&$filter=startswith(displayName,'John Riddell') or (startswith(givenName,'John') and startswith(surname,'Riddell'))
 
To find an enabled user that has an email address or user principal name matching "jonlawr@contoso.com":
 
    GET /contoso.com/users?api-version=2013-11-08&$filter=accountEnabled eq true and (userPrincipalName eq 'jonlawr@contoso.com' or mail eq 'jonlawr@contoso.com')
 
To find a user having a SMTP proxy address or userPrincipalName matching "william@contoso.com":
 
    GET /contoso.com/users?api-version=2013-11-08&$filter=userPrincipalName eq 'william@contoso.com' or proxyAddresses/any(x:startswith(x,'smtp:william@contoso.com'))
 
Note the "any" function syntax, which must be used when searching against a multi-valued property such as proxyAddresses.  As another example, if you have a number of userPrincipalName values and you would like to find any matching users:
 
    GET /contoso.com/users?api-version=2013-11-08&$filter=userPrincipalName eq 'mary@contoso.com' or userPrincipalName eq 'jonlawr@fabrikam.com' or userPrincipalName eq 'james@contoso.com'
 
The above examples do not include all object types, but such queries can be applied to other object types, with their respective properties.

Filter Expression Constraints

There are a few constraints that must be adhered to when performing a filtered search.  These are as follows:

  • When the search expression contains only AND-joined filter clauses, the maximum allowed number of clauses is 25.
  • When the search expression contains at least one set of OR-joined filter clauses, the maximum allowed number of clauses is 10.
  • The maximum allowed expression height is 3, where an example of a tree of height 3 is ((A and B) or (C and D)). 

These constraints are imposed to enable us to better maintain efficient response guarantees.  If any one of these constraints are violated, an HTTP 400 BadRequest error response will be returned, along with an appropriate error message.  We are actively working on improving the developer experience, so please let us know if any of these constraints impede your ability to obtain the desired search results. 

Filterable Properties

The following sub-sections represent the list of filter properties for each object type that are possible to be used in a $filter query argument as of May 2014. These are irrespective of the service version, meaning that if the type and property are present in a given service version, it should be filterable via $filter.

Application

  • appId
  • availableToOtherTenants
  • identifierUris

Contact

  • city
  • country
  • department
  • dirSyncEnabled
  • displayName
  • givenName
  • jobTitle
  • lastDirSyncTime
  • mail
  • mailNickname
  • proxyAddresses
  • state
  • surname

Device

  • accountEnabled
  • alternativeSecurityIds
  • deviceId
  • devicePhysicalIds
  • dirSyncEnabled
  • displayName
  • lastDirSyncTime

Group

  • dirSyncEnabled
  • displayName
  • lastDirSyncTime
  • mail
  • mailNickname
  • proxyAddresses
  • securityEnabled

ServicePrincipal

  • accountEnabled
  • appId
  • displayName
  • publisherName
  • servicePrincipalNames
  • tags 

User

  • accountEnabled
  • city
  • country
  • department
  • dirSyncEnabled
  • displayName
  • givenName
  • immutableId
  • jobTitle
  • lastDirSyncTime
  • mail
  • mailNickname
  • otherMails
  • proxyAddresses
  • state
  • surname
  • usageLocation
  • userPrincipalName
  • userType

 

As always, we'd love to hear any feedback from the community!
 
Thanks,
 
Robert
 
Azure Active Directory Team
Microsoft Corporation