OData V4 Contains and any - odata

We have an OData V4 endpoint with the following structure. We are using
Entity Framework 6.1, OData V4 and Web Api 2.2
http://api.com/odata/Companies
and if we want to get the address we simply expand like this
http://api.com/odata/Companies?$expand=Addresses
If I filter by city it works nice
http://api.com/odata/Companies?$filter=Addresses/any(a:a/City eq 'New York')
But we cannot apply Contains to the filter.
What is the correct syntax to achieve Contains with the City property?

One part of the correct syntax is to have the letters of built-in query option "contains()" all in lower case, in case the reason that you failed to apply it to the filter is because the casing isn't right.
You can see the following query to the TripPin sample service as an example of the whole syntax:
http://services.odata.org/v4/TripPinServiceRW/People?$filter=Trips/any(a:contains(a/Name,'US'))&$expand=Trips

Related

How to get proper results for these queries?

Need to execute and return 1st order detail alone for each order. Below doesn't work
https://services.odata.org/Experimental/Northwind/Northwind.svc/Customers?$expand=Orders($expand=Order_Details;$top=1)
Need to filter records based on order id. Below doesn't work and throws "Term 'Orders($expand=Order_Details)$filter=OrderID eq '10643'' is not valid in a $select or $expand expression"
https://services.odata.org/Experimental/Northwind/Northwind.svc/Customers?$expand=Orders($expand=Order_Details)$filter=OrderID eq '10643'
Invalid but returned results
https://services.odata.org/Experimental/Northwind/Northwind.svc/Regions?expand=Order_Details
https://services.odata.org/Experimental/Northwind/Northwind.svc/Regions?expand=Territories
Not Returning Childrens
https://services.odata.org/Experimental/Northwind/Northwind.svc/Products?&expand=Suppliers
https://services.odata.org/Experimental/Northwind/Northwind.svc/Regions?&expand=Territories
https://services.odata.org/Experimental/Northwind is no longer 'Best Practise'
It's a bold statement, but this question proves that many advanced query features have not been fully or properly implemented in the published service.
While many developers may use it to practise OData query concepts, given that the OData implementation is largely up to the developers and the version of the packages they use, it is probably a of more commercial value if you query against a live or a dev implementation of the actual service that you want to query.
The following is an analysis of OPs queries and how to achieve the desired response according the the OData-V4 specification and verified against a deployed API that utilises the following NuGet packages:
Microsoft.AspNet.OData v7.3.0
Microsoft.OData.Core v7.6.2
The actual API that I used for testing is proprietary and cannot be published here.
According to the spec, you have to move the $top specifier to the Order_Details expansion.
https://services.odata.org/Experimental/Northwind/Northwind.svc/Customers?
$expand=Orders($expand=Order_Details($top=1))
However:
https://services.odata.org/Experimental/Northwind does not correctly implement $top query option within expansion as defined in 5.1.3 System Query Option $expand
Query options can be applied to an expanded navigation property by appending a semicolon-separated list of query options, enclosed in parentheses, to the navigation property name. Allowed system query options are $filter, $select, $orderby, $skip, $top, $count, $search, and $expand.
$top is however supported in the ODataLib v7+ (.Net) implementation of OData v4. So the syntax is correct, but you should check with your API developers for their opinion if your queries with this syntax do not work.
NOTE: When using $top you should also use $orderbyto ensure that the query results are reliable and reproducable:
https://services.odata.org/Experimental/Northwind/Northwind.svc/Customers?
$expand=Orders($expand=Order_Details($orderby=ProductID;$top=1))
To apply multiple query options within an expansion, you must separate then with a semi-colon: ;. However that alone will not prevent other customer records from being returned, so you must also add a root level filter as well, which is complicated by the fact that Orders is a collection. We can use the any function to only return customers that have an order with the specified Id:
Also note that OrderID is numeric, so do not wrap comparison values in quotes
https://services.odata.org/Experimental/Northwind/Northwind.svc/Customers?
$expand=Orders($expand=Order_Details;$filter=OrderID eq 10643)
&$filter=Orders/any(o:o/OrderID eq 10643)
this can be further simplified using parameter alias:
https://services.odata.org/Experimental/Northwind/Northwind.svc/Customers?
$expand=Orders($expand=Order_Details;$filter=OrderID eq #orderId)
&$filter=Orders/any(o:o/OrderID eq #orderId)
&#orderId='10643'
However:
https://services.odata.org/Experimental/Northwind does not correctly implement parameter aliases so you cannot verify the alias syntax against this service.
Also note
that experimental service is not correctly applying the filter to either the root elements or the navigation collection, the syntax shown here does however work against the .Net ODataLib implementations of OData v4.
The reason that your $expand is not working is that you have left off the $ from the parameter name. The OData query interpreter only identifies query options are parameters that start with $.
Eitherway, according to the https://services.odata.org/Experimental/Northwind/Northwind.svc/$metadata#Regions, there is no Order_Details navigation property to $expand on:
<EntitySet Name="Regions" EntityType="NorthwindModel.Region">
<NavigationPropertyBinding Path="Territories" Target="Territories" />
</EntitySet>
So when you try again with the correct syntax:
https://services.odata.org/Experimental/Northwind/Northwind.svc/Regions?$expand=Order_Details
you get the expected message:
Could not find a property named 'OrderID' on type 'NorthwindModel.Region'
The second attempt will work if you put the correct $ in there for the $expand query option:
https://services.odata.org/Experimental/Northwind/Northwind.svc/Regions?$expand=Territories
The OData query parser only looks for the expected query options with the $ prefix, this allows your API logic to still process other non-OData parameters as you see fit to do so. The other parameters therefor are still HTTP Url compliant parameters, the implementation at odata.org doesn't know what to do with them and they are simply ignored.
This is just another variation on the same issue with 3, the $ is missing. (I suspect that this URL was meant to be in 3: https://services.odata.org/Experimental/Northwind/Northwind.svc/Products?$expand=Suppliers)
So while https://services.odata.org/Experimental/Northwind is not 100% reliable, neither are the .Net ODataLib, SAP or MS Dynamics implementations. The spec is evolving and there are many query techniques that are not fully implemented in probably any providers at this stage.
Simply be mindful of this fact and when you run into issues using an API, the first point of contact should be the developers or the community that are supporting that particular API, it will be up to the developers what techniques and packages they use and at the end of the day to what extent they support the protocol as it is specified.

Does OData 2.0 support "contains" in the filter

I can see in the Basic Tutorial section of the odata website that you can perfom a contains search using a filter
GET serviceRoot/Airports?$filter=contains(Location/Address, 'San Francisco')
The API I am using refused this when I tried to use it. The API I am accessing is OData 2.0.
So I wanted to know if OData 2.0 supports the contains filter but couldn't find a list anywhere documenting the filters you could use e.g. startsWith, endsWith, contains
My research led me to the only answer I could find which was to use substringof instead of contains. This is the end of the url call from the actual API I am working on. This results in searching for Items filtered by Description containing "69":
/logistics/Items?$filter=substringof('69',%20Description)%20eq%20true

How to expand multiple properties on OData

Consider I have this OData expression:
http://services.odata.org/northwind/northwind.svc/Categories?
$expand=Products/Category
It will correctly expand the Products.Category.
Now I want to expand another property too. For example 'Products.Supplier`.
I've tried duplicating the $expand usage:
http://services.odata.org/northwind/northwind.svc/Categories?
$expand=Products/Category
&$expand=Products/Supplier
but it failed returning this error:
Query parameter '$expand' is specified, but it should be specified exactly once.
According to OData ABNF, expand syntax should be:
expand = '$expand' EQ expandItem *( COMMA expandItem )
Which amounts to:
$expand=expandItem1,expandItem2,expandItem3,...
So please try:
http://services.odata.org/northwind/northwind.svc/Categories?$expand=Products/Category,Products/Supplier
For more information, see:
http://www.odata.org/documentation/odata-version-2-0/uri-conventions/#ExpandSystemQueryOption
You can also try this syntax for expanding multiple levels:
$expand=Products($expand=Category),...
This works well with MS OData implementation in WebAPI.
For our SAP consultants out there:
We're able to use multi-levels in oData as well.
You need to pass it this way:
<host>/API_BUSINESS_PARTNER/Identification?$expand=to_Parent,to_Parent/to_Role
The example above provides you the ability to fetch the expands of another expand. In this case to_parent -> to_Role. "to_Role" is only available at the parent level.

odata v4 search example needed

I am trying to learn the OData version 4 protocol and am using the Northwind database to run queries against.
OData 4 introduced the free text search with $search but the queries I've tried all fail.
A couple things I tried (with many variants):
http://services.odata.org/V4/Northwind/Northwind.svc/Customers?$search=%28City%20eq%20Berlin%29
http://services.odata.org/V4/Northwind/Northwind.svc/Customers?$search=City%20eq%20Berlin
http://services.odata.org/V4/Northwind/Northwind.svc/Customers?$search=Berlin
Error message I get is: The query parameter '$search' begins with a system-reserved '$' character but is not recognized.
The official docs don't say much here, and just reference another source for the exact format. However, the format is very cryptic to me...
From the docs, the general idea is http://host/service/Products?$search=blue OR green which seems in line with my examples. So not sure what I'm doing wrong here.
Has anyone successfully used this before and could give me an example? Thanks!
You got an error message from http://services.odata.org/V4/Northwind/Northwind.svc because this service has not been updated to support $search. ODL began to support $search in version 6.1.0. please check 6.1.0 release notes
From the spec, "The $search system query option restricts the result to include only those entities matching the specified search expression. The definition of what it means to match is dependent upon the implementation." Since the match rule is dependent on the service implementation, the service can determine which property or even properties combinations to match the search expression.
This service http://odatae2etest.azurewebsites.net/demo/DefaultService/ has simply implemented $search, and this service choose to have the first string type property to match the search expression.
So for this service, http://odatae2etest.azurewebsites.net/demo/DefaultService/ProductDetails?$search=snack is actually meant to return ProductDetails whose description contains 'snack'.
Otherwise, $search supports AND, OR, NOT operations.

How can I retrieve an entity by key without using filter with JayData

JayData has a method on EntitySet called find(keyValue, cb). However, this method translates to the following OData query:
http://localhost/api/MyEntitySet?$filter=(Id eq 1)&$top=2
Why doesn't the OData provider query by key instead? Like so:
http://localhost/api/MyEntitySet(1)
I find the second query to be more natural in this case, also easier to debug. Is there a way to force JayData to use the second query?
Sorry, we don't support this now. You can add it to our backlog (http://jaydata.org/backlogs) or open an issue on github.
Now it seems to be supported, but not documented yet. I have tested wit the Version 1.3.6. The reuest was translated into
http://localhost/api/MyEntitySet(1)
The only documentation i found was here (Search for "New find() for OData provider"):
http://jaydata.org/blog/release-notes

Resources