SAP Gateway $filter on $expand Entity - odata

I've seen two post about the URL convention, but my question is specific to the SAP's Gateway implementation for OData web services assuming. When trying to use $filter in combination with $expand we get the error message:
Left hand expression of memberaccess operation has wrong cardinality
Assuming I have two simple entities:
Foo
* Key
- Value
Bar
* Key
* Id
- Value
Foo has a 1:n association to Bar. The following URL works as intended.
/sap/opu/odata/sap/ZTEST_SRV/Foo?$expand=Bar
As does
/sap/opu/odata/sap/ZTEST_SRV/Foo?$filter=Key gt 10&$expand=Bar
When trying to using $filter on entity Bar property Id we get the error message.
/sap/opu/odata/sap/ZTEST_SRV/Foo?$filter=Key gt 10 and Bar/Id gt 2&$expand=Bar
Is it possible to use a $filter in this way with SAP? Related articles below.
ODATA / SAP Gateway: About Query with $filter and $expand simultaneously
Filter on Expanded entities in OData

I am also facing similar problems, there is this sap note:
https://apps.support.sap.com/sap/support/knowledge/en/3008698
Copy of the note details:
BEGIN OF NOTE
Symptom
After adding a filter
$filter = To_Employees/Name eq 'Hugo' to an Odata service, there is an error:
"Left hand expression of memberaccess operator has wrong cardinality (to many not allowed)"
Environment
SAP_GWFND 750
Reproducing the Issue
Access the odata service: /sap/opu/odata/sap/API_XXX_SRV/Orgnization$select=Orgnization,to_Employees/Name&$expand=to_Employees&$filter=to_Employees/Name eq 'Hugo'
There is an error: "Left hand expression of memberaccess operator has wrong cardinality (to many not allowed)"
Cause
"Left hand expression of memberaccess operator has wrong cardinality (to many not allowed)" indicates that "to_Employees"is a to-many navigation, and this is not supported in combination with a filter expression (e.g.
$filter = To_Employees/Name eq 'Hugo',
when "To_Employees" points to more than one employee ).
Therefore, the whole request is invalid.
Resolution
Remove the filter, do not combine to many results with filter eq
END OF NOTE
I don't like the solution presented :)
The workaround solution I have implemented was to create an entity which has cardinality 1:1 just for filtering.
In your example, I assume you need the cardinaliy of the relationship from Foo to Bar to be 1:n in order to be able to receive multiple Bar records for each Foo found. If the relationship Foo to Bar is 1:1, you can simply change the cardinality and you are good to go. If you need cardinality 1:n, you may create an entity like BarFilter which is related to Foo with 1:1 cardinality. Then you would be able to execute this request without errors and can receive the filters to make the corresponding query on your backend system. The request would be something like:
New simple entity:
BarFilter
Key
Id
Value
Foo has a 1:1 association to new entity BarFilter.
Request:
/sap/opu/odata/sap/ZTEST_SRV/Foo?$filter=Key gt 10 and BarFilter/Id gt 2&$expand=Bar
I hope this is clear and that it helps you.
I am very interested in this topic so if you find something interesting, please share it.
Have a nice weekend.
Cheers

Related

Get Microsoft Graph messages by flagStatus using OData $filter

I'm writing Microsoft Graph OData query to get mail messages based on message flagStatus in order to reuse working query in my C# code. How correct query should look like?
Following example is tried in Graph Explorer's sample account where all messages have object flag with field flagStatus = "notFlagged".
https://graph.microsoft.com/v1.0/me/messages?$filter=flag/flagStatus eq 'notFlagged'
It is expected that all messages should be returned. But as a result empty array was given back.
If in query I change eq to ne to be
https://graph.microsoft.com/v1.0/me/messages?$filter=flag/flagStatus ne 'notFlagged'
then all messages are returned. But by me it is expected that here should be empty array in this case.
Considering that maybe flag object isn't ready for filtering, I also tried to $expand it
https://graph.microsoft.com/v1.0/me/messages?$expand=flag&$filter=flag/flagStatus eq 'notFlagged'
but got error message
Property 'flag' on type 'microsoft.graph.message' is not a navigation property or complex property. Only navigation properties can be expanded.
So probably $expand for flag filtering is not needed. But is it possible to filter by message's flag at all?
Also if consider situation that flag object could be null and also flagStatus inside could be null (at least in C# class Microsoft.Graph.FollowupFlag property FlagStatus is nullable), then how query should look like to meet full requrirement below?
Get messages whose
flag is null
or flag/flagStatus is null
or flag/flagStatus is 'notFlagged'
I had the same problem, probably is nullable as suggested. There's only three possible statuses according to the followupFlag documentation, so I worked around it with a not equal and condition for the two other statuses, complete and flagged.
https://graph.microsoft.com/v1.0/me/messages?$filter=flag/flagStatus ne 'flagged' and flag/flagStatus ne 'complete'

Adding relationship to existing nodes with Cypher doesn't work

I am working on Panama dataset using Neo4J graph database 1.1.5 web version. I identified Ion Sturza, former Prime Minister of Moldova on the database and want to make a map of his related network. I used following code to query using Cypher (creating a variable 'IonSturza'):
MATCH (IonSturza {name: "Ion Sturza"}) RETURN IonSturza
I identified that the entity 'CONSTANTIN LUTSENKO' linked differently to entities like 'Quade..' and 'Kinbo...' with a name in small letters as in this picture. I hence want to map a relationship 'SAME_COMPANY_AS' between the capslock and the uncapped version. I tried the following code based on this answer by #StefanArmbruster:
MATCH (a:Officer {name :"Constantin Lutsenko"}),(b:Officer{name :
"CONSTANTIN LUTSENKO"})
where (a:Officer{name :"Constantin Lutsenko"})-[:SHAREHOLDER_OF]->
(b:Entity{id:'284429'})
CREATE (a)-[:SAME_COMPANY_AS]->(b)
Instead of indexing, I used the 'where' statement to specify the uncapped version which is linked only to the entity bearing id '284429'.
My code however shows the cartesian product error message:
This query builds a cartesian product between disconnected patterns.If a part of a query contains multiple disconnected patterns, this will build a cartesian product between all those parts. This may produce a large amount of data and slow down query processing. While occasionally intended, it may often be possible to reformulate the query that avoids the use of this cross product, perhaps by adding a relationship between the different parts or by using OPTIONAL MATCH (identifier is: (b))<<
Also when I execute, there are no changes, no rows!! What am I missing here? Can someone please help me with inserting this relationship between the nodes. Thanks in advance!
The cartesian product warning will appear whenever you're matching on two or more disconnected patterns. In this case, however, it's fine, because you're looking up both of them by what is likely a unique name, s your result should be one node each.
If each separate part of that pattern returned multiple nodes, then you would have (rows of a) x (rows of b), a cartesian product between the two result sets.
So in this particular case, don't mind the warning.
As for why you're not seeing changes, note that you're reusing variables for different parts of the graph: you're using variable b for both the uppercase version of the officer, and for the :Entity in your WHERE. There is no node that matches to both.
Instead, use different variables for each, and include the :Entity in your match. Also, once you match to nodes and bind them to variables, you can reuse the variable names later in your query without having to repeat its labels or properties.
Try this:
MATCH (a:Officer {name :"Constantin Lutsenko"})-[:SHAREHOLDER_OF]->
(:Entity{id:'284429'}),(b:Officer{name : "CONSTANTIN LUTSENKO"})
CREATE (a)-[:SAME_COMPANY_AS]->(b)
Though I'm not quite sure of what you're trying to do...is an :Officer a company? That relationship type doesn't quite seem right.
I tried the answer by #InverseFalcon and thanks to it, by modifying the property identifier from 'id' to 'name' and using the property for both 'a' and 'b', 4 relationships were created by the following code:
MATCH (a:Officer {name :"Constantin Lutsenko"})-[:SHAREHOLDER_OF]->
(:Entity{name:'KINBOROUGH PORTFOLIO LTD.'}),(b:Officer{name : "CONSTANTIN
LUTSENKO"})-[:SHAREHOLDER_OF]->(:Entity{name:'Chandler Group Holdings Ltd'})
CREATE (a)-[:SAME_NAME_AS]->(b)
Thank you so much #InverseFalcon!

Dynamics CRM OData query filtering on expanded attributes only works if no results come out?

I have a requirement to fetch Price List Item records which adhere to the following requirements:
Filter by a specific PriceList
Filter by a specific currency
Filter by the Name of the related Product containing a given string
I got the first two points working no problem, but it feels like expanding doesn't cope well with filtering. I started from a "straight" query on Product entity:
.../ProductSet?$filter=substringof('sometext', Name)
Equivalent SQL (targeting the corresponding CRM filtered views for clarity):
SELECT * FROM FilteredProduct WHERE ProductNumber LIKE '%sometext%'
The above query works, I can tweak it and have no issues. However, if I attempt to move on to ProductPriceLevel (thus expanding the relationship with Product, which is product_price_levels) I end up with this:
.../ProductPriceLevelSet?$expand=product_price_levels&$filter=substringof('sometext', product_price_levels/Name)
Equivalent SQL (again, targeting the relevant filtered views):
SELECT * FROM FilteredProductPriceLevel PPL JOIN FilteredProduct P
ON PPL.ProductId = P.ProductId WHERE P.ProductNumber LIKE '%sometext%'
Which has two different outcomes I see:
If the $filter has no matches, it works fine and returns an empty result set
If the $filter matches something, I get an error
code: -2147220970
message: The result selector of the 'Join' operation must return an anonymous type of two properties.
AFAIK that's what happens when you hit a limitation of LINQ-to-CRM regarding using .Where() on multiple entities at once... doesn't seem relevant!
What's wrong with my query ?
NOTE: The CRM 2013 I'm using is On-Premise, without any update rollup / service pack.
ALSO NOTE: The equivalent SQL, as can be expected, works perfectly
I don't think CRM OData supports adding a filter on a joined entity. Try reversing the entity you're actually starting with, and add a path to the referencing entity:
ProductSet()?$filter=substringof('sometext',ProductNumber)&$expand=product_price_levels&$select=product_price_levels/*
P.S. If you have linqPad, this is the query I used to generate this:
from p in ProductSet
where p.ProductNumber.Contains("sometext")
select new { p.product_price_levels }

Can an OData query specify a filter that references a nested entity?

Here is the setup: I've got a "Student" who has a related entity "Course" (1 to many). Each "Course" has a related entity "Period" that contains all of the time and date details of the course. I want to get back all of the students who have had a course in 2011.
I've tried this:
~/Student()?$expand=Course/Period&$filter=Course/Period/Year eq 2011
but it results in an error: No property 'Period' exists in type 'System.Data.Objects.DataClasses.EntityCollection`1[[Course]]
Clearly, Period is being treated as a property rather than an Entity, but I'm confused, because the following query does return the results I expect, and it uses almost the same syntax:
~/Student()?$expand=Course/Period&$select=Course/Period/Year
So am I doing something wrong with the $filter syntax, or is this not possible?
TIA for any insight.
The filter would work if the navigation property would be a singleton, but since it's a collection (1 to many) the filter won't work. Mainly because it's unclear what would that mean. Do you want students which have all their courses in 2011 or just some... and so on.
In the latest CTP (http://blogs.msdn.com/b/astoriateam/archive/2011/06/30/announcing-wcf-data-services-june-2011-ctp-for-net4-amp-sl4.aspx) There's a support for any and all operators which should allow you to do what you want. For more details see this blog post: http://www.odata.org/blog/even-more-any-and-all.
Yes it should be possible. You need to use something like this.
~/Student()?$expand=Course/Period&$filter=Course/any(d:d/Period/Year eq 2011)
Course/any() looks to see if anything in the collection matches the expression in ().
d: specifies an iterator for the collection which is then referenced in the d/Period/Year.
Reference can be found in OData Documentation In Section 5.1.1.10.1 (Search for "/any").
Note: You can also do /all to ensure that all courses match some criteria.

Filter an OData query based on a link count?

Given a structure where two types are exposed in an OData system with a master/detail relationship:
Order
- OrderDetails
How would you filter a query of orders based upon the count of associated OrderDetails? In my head, it's something along the lines of
/Orders$filter=count(OrderDetails) eq 0
But, of course there's no count function. So, how would you produce a list of orders which had no OrderDetails?
Very similar question to this one: Collection Exists Criteria in WCF Data Services
Currently the OData protocol doesn't support any operator/query to do that. The best solution is to expose a service operation which exposes this kind of operation from the server directly.

Resources