Support for Disjunctive/OR Filter Clauses in the Graph Directory Service

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 /$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.


*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 /$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 /$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 "":
    GET /$filter=accountEnabled eq true and (userPrincipalName eq '' or mail eq '')
To find a user having a SMTP proxy address or userPrincipalName matching "":
    GET /$filter=userPrincipalName eq '' or proxyAddresses/any(x:startswith(x,''))
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 /$filter=userPrincipalName eq '' or userPrincipalName eq '' or userPrincipalName eq ''
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.


  • appId
  • availableToOtherTenants
  • identifierUris


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


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


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


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


  • 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!
Azure Active Directory Team
Microsoft Corporation

Comments (7)
  1. Thibaut says:

    Hi, is there an updated .Net wrapper dll for that in Nugget (or somewhere else)?


  2. Shawn says:

    Absolutely; Microsoft.Azure.ActiveDirectory.GraphClient .9

    //Example: GET /$filter=startswith(displayName,'mary') or startswith(givenName,'mary') or startswith(surname,'mary') or startswith(mail,'mary') or startswith(userPrincipalName,'mary')

    using GC = Microsoft.Azure.ActiveDirectory.GraphClient;

    GraphSettings graphSettings = new GraphSettings();

    graphSettings.ApiVersion = "2013-11-08";

    GraphConnection graphConnection = new GraphConnection(accessToken, ClientRequestId, graphSettings);

    var searchvalue = "mary";

    var q1 = GC.ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.DisplayName, searchvalue);

    var q2 = GC.ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.GivenName, searchvalue);

    var q3 = GC.ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.Surname, searchvalue);

    var q4 = GC.ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.Mail, searchvalue);

    var q5 = GC.ExpressionHelper.CreateStartsWithExpression(typeof(User), GraphProperty.UserPrincipalName, searchvalue);

    var qbinary = GC.ExpressionHelper.JoinExpressions(q1,



                   GC.ExpressionHelper.JoinExpressions(q4, q5, System.Linq.Expressions.ExpressionType.Or)

                   , System.Linq.Expressions.ExpressionType.Or)

                   , System.Linq.Expressions.ExpressionType.Or)

                   , System.Linq.Expressions.ExpressionType.Or);

               var fg = new FilterGenerator();

               fg.QueryFilter = qbinary;

               PagedResults<User> pagedReslts = graphConnection.List<User>(null, fg);

  3. Nandhini Raman says:

    I get the below error while using linking 2 objects in AddLink method.…/metadata" xmlns:xsd="…/XMLSchema&quot; xmlns:xsi="…/code><message xml:lang="en">Unable to update the specified properties for on-premise mastered Directory Sync objects or objects currently undergoing migration.</message></error>

    but this works fine for azure AD account and error is thrown for federated user account.

    could some one help me to fix this?

    the graph api version i'm using is 2013-11-08

  4. Robert Ward - MSFT says:

    Hi Nandhini, you are getting that error because your request is attempting to update the links of a User that is currently marked as mastered from on-premises.  This means that the User object has been synced from a tenant's on-premises directory to the Azure directory, and its links can only be changed by updating the on-premises directory.

    Hope that helps,


  5. Nandhini Raman says:

    Thanks Robert,

    so you conclude that there is no way to update the link using WAAD graph API for on-premises user?



  6. Vincent-Philippe says:


    I have problem to filter on group "mailEnabled" property.  I see in this blog post that it isn't supported.

    The blog post is a few months old, is that still accurate today (End-of-March 2015)?

    My query is:…/groups$filter=mailEnabled%20eq%20True

    and I receive

    {"odata.error":{"code":"Request_BadRequest","message":{"lang":"en","value":"No property 'True' exists in type 'Microsoft.WindowsAzure.ActiveDirectory.Group' at position 15."}}}

    which sounds a bit odd as an error message…


    Vincent-Philippe Lauzon

  7. orad says:

    Hi, the code snippet posted by Shawn above seems to be obsolete now. Do you have any new pointers how to do filtering with the latest GraphClient?

Comments are closed.

Skip to main content