Hi Hanif,

This will be possible with OData 4.0, in OData 2.0 complex properties are 
single-valued. 

A typical work-around with OData 2.0 is to use a separate entity type and a 
navigation property with cardinality * instead of the list-valued complex 
property. In many use cases the items in the list actually can be uniquely 
identified, and are in fact stored in a separate table. So this "work-around" 
actually is making the underlying data model visible again.

What is your use case?

Regards!
--Ralf

-----Original Message-----
From: Rajabali, Hanif [mailto:[email protected]] 
Sent: Tuesday, 11. February 2014 17:39
To: Klevenz, Stephan
Cc: Xu, Yao Feng; [email protected]
Subject: How to resolve 1 Entity type that has a property which is a list of a 
complex type (cadinality 0.. *)

Hi Stephan and the great Olingo community!   I have a situation where I'm 
trying to model an oData service from a webservice that returns back a lot of 
data in the form of nested complex types (a very complex xsd) .    I basically 
have 1 entity type that represents the main webservice response object.    And 
that entity type has several complex/simple type properties that I've mapped to 
match.    When ready to write the odata response, I can map them to a 
List<Map<String,Object>> as per what odata expects by using reflection etc 
etc..... the problem I'm having is if I have a List of complex type as part of 
my entity type.    Is there a way for me to create a complex property and 
define it as a list or collection that has cardinality 0..*?   I don't have any 
navigation properties as I only have the 1 entity.   So once I get it correctly 
defined, my metadata should show that particular field/property as a collection 
am I correct?     
Any help with some code examples/snippets would be greatly appreciated!

Thank you!
Hanif


On 16.01.14 22:44, "Rajabali, Hanif" <[email protected]> wrote:

>Hello Sven and the olingo community!   Needed your help again please :)
> As it stands, we are using the olingo library to generate odata
>services.   We are sort of using the odata services as a proxy to invoke
>other services so its not really being used to perform CRUD operation on
>a backend database per se, but more so to simply relay the request(s) to
>underlying service(s).    For example, we have an Account.svc which
>simply gets accounts from another disparate rpc service.   We have
>Product.svc which gets products from another webservice.     I realize
>this is not really what odata is meant to be used for but using odata to
>communicate with a ui layer (sap ui5) is a strict requirement for us.
>So now, since we have multiple services, we have not defined a
>singleprocessor as in the car/manufacturer example.   Instead, we have
>defined separate processors which extend from OdataSingleProcessor i.e)
>AccountProcessor, ProductProcessor and have defined a delegator (also
>extends OdataSingleprocessor) which through reflection, and our own
>defined annotations, determines,  which processor to use.    We have also
>defined our own annotations to represent, simple/complex types which get
>mapped at runtime through the edmprovider instead of hard coding entity
>field simpl/complex types as shown in the sample app. (Unless there is
>something out of the box we can use??)
>
>We now need to implement FunctionImport.   The car/manufacturer example
>defines it in the edmprovider but doesn't provide a concrete
>implementation for us to use as an example.   We would like to create
>such functionimports to be able to in turn call services that don't
>really tie into an EntityType.   We also don't use JPA... can you
>illustrate how we might perform something like:
>http://myhost/MyService.svc/MyFunctionImport(parameter1....parameterN)?
>
>Thank you so much!
>Hanif
>
>-----Original Message-----
>From: Kobler-Morris, Sven [mailto:[email protected]]
>Sent: Thursday, December 19, 2013 5:00 AM
>To: [email protected]
>Subject: FW: How to implement a filter or query using Olingo API
>
>Hello Hanif,
>
>using  the filter is the better option, because that is what the $filter
>system query parameter  is made for (similar to the "where " clause
>in SQL statements). A ODATA client can request only entities which
>pass the filter expression.
>
>To avoid a manual parsing of the $filter parameter the odata library
>does the parsing and creates a filter tree which can be obtained from the
>UriInfo object  supplied as input parameter "uriInfo" on the "readEntity"
>method. The filter tree can be used directly or with help of the Visitor
>pattern. 
>
>Generally  you can use the filter tree to create a query string which is
>understood by your data layer and pass this query string to the data
>layer (potentially insecure) or you can filter a entity set manually in
>your layer.
>
>I recommend to implement the interface "ExpressionVisitor" to either
>create the query string for the data layer or to the manual filtering.
>
>An potential solution for manual filtering would be to implement
>and ExpressionVisitor  and use this visitor on each entity set/data
>record 
>you want to filter.
>
>E.g.
>...
>public class AccountFilter implements ExpressionVisitor
>...
>use the methods:
>visitMember -> to read and validate the propertyName
>                                (from your sample only accountName is
>allowed)
>               and return the column name
>visitLiteral -> to read the accountName value (e.g. "Account1")
>               and return it
>visitBinary -> if the parameter is "eq"  return
>               whether property accountName on  the current
>               entityset/datarecord is equal to the literal or not
>...
>As sample a rough flow would be:
>- read the entityset ( all records)
>- get the filter tree from uriInfo
>- for each record
>-     if ( filtertree->accept(yourAccountFilter) != true) {
>       delete record from entityset    
>      }
>- next record
>- serialize the remaining records
>
>The call sequence for $filter=AccountName eq 'Account1') would be
>1. visitMember
>2. visitLiteral 
>3. visitBinary (left operant is "AccountName" right opterand is
>"Account1")
>
>Consider also how your service should behave in case of more complex
>filters
>Like:
>$filter='Account1' eq  AccountName
>$filter='Account1' eq  AccountName or AccountName eq 'Acount2'
>
>However this is not the fastest solution. It is always faster to
>transform the 
>filtertree into a query which can be used directly in your datalayer, but
>always
>consider security checks before using this to avoid script injection
>etc...
>
>regards,
>Sven
>
>----
>
>On 17.12.13 22:55, "Hanif Rajabali" <[email protected]> wrote:
>
>>Hello all, sorry if this is too rudimentary of a question but I have
>>setup an oData service with the help of the great tutorials posted on the
>>olingo site.    The tutorials cover the Resource path but I'd like to now
>>perform a system or custom query.   Below I've included my basic
>>$metadata document which has 2 simple entities: Account/Producer
>>(EntitySets: Accounts/Producers).    As it stands I can perform an
>>operation like /Accounts(1) which is taken care of in my implementation
>>of readEntity() in my ODataSingleProcesser shown below.    Now I'd like
>>to get say an account by 'AccountName'.   So would it be system $filter
>>like: /Accounts?$filter=AccountName eq 'Account1'  or would it be a
>>custom query like: /Accounts?AccountName='Account1'
>>
>> 
>>
>>And, where would I implement it?   Within readEntity?  If so, could you
>>give me some guidance on the implementation?    I saw the sample of the
>>visitor pattern to transform an odata query into a jdbc query but my
>>odata service is connecting to another disparate service:
>>this.accountService so I just need to basically parse the query and then
>>perform the corresponding service call based on the query name/value
>>pair.    Should I leverage what was shown in the visitor example?  If so,
>>how does that 'plugin' to my OdataSingleProcessor implementation?
>>
>> 
>>
>>public class WPODataSingleProcessor extends ODataSingleProcessor {
>>
>>.....
>>
>>public ODataResponse readEntity(GetEntityUriInfo uriInfo, String
>>contentType) throws ODataException {
>>
>>                              logger.info("custom query options: " +
>>uriInfo.getCustomQueryOptions());
>>
>>                              if (uriInfo.getNavigationSegments().size()
>>== 0) {
>>
>>                                             EdmEntitySet entitySet =
>>uriInfo.getStartEntitySet();
>>
>>                                             WPEntitySet startEntitySet =
>>WPEntitySet.fromString(entitySet.getName());
>>
>>                 
>>
>>                                             int id;
>>
>>                                             switch(startEntitySet) {
>>
>>                                             case ACCOUNTS:
>>
>>                                                            id =
>>getKeyValue(uriInfo.getKeyPredicates().get(0));
>>
>>                                                            Map<String,
>>Object> account = this.accountService.getAccount(id);
>>
>> 
>>
>>                                                            if (account
>>!= null) {
>>
>>                 
>> URI serviceRoot = getContext().getPathInfo().getServiceRoot();
>>
>>                 
>> ODataEntityProviderPropertiesBuilder propertiesBuilder =
>>EntityProviderWriteProperties.serviceRoot(serviceRoot);
>>
>> 
>>
>>                 
>> return EntityProvider.writeEntry(contentType, entitySet, account,
>>propertiesBuilder.build());
>>
>>                                                            }
>>
>>                 
>>
>>                                             case PRODUCERS:
>>
>>                                                            id =
>>getKeyValue(uriInfo.getKeyPredicates().get(0));
>>
>>                                                            Map<String,
>>Object> producer = this.producerService.getProducer(id);
>>
>> 
>>
>>                                                            if (producer
>>!= null) {
>>
>>                 
>> URI serviceRoot = getContext().getPathInfo().getServiceRoot();
>>
>>                 
>> ODataEntityProviderPropertiesBuilder propertiesBuilder =
>>EntityProviderWriteProperties.serviceRoot(serviceRoot);
>>
>> 
>>
>>                 
>> return EntityProvider.writeEntry(contentType, entitySet, producer,
>>propertiesBuilder.build());
>>
>>                                                            }
>>                 
>>
>>                                             }
>>
>> 
>>
>>                                             throw new
>>ODataNotFoundException(ODataNotFoundException.ENTITY);
>>
>> 
>>
>>                              }
>>
>> 
>>
>>                              throw new ODataNotImplementedException();
>>
>>               }
>>
>> 
>>
>>...
>>
>> 
>>
>>}
>>
>> 
>>
>> 
>>
>>-------------------
>>
>>My $metadata:
>>
>>-------------------
>>
>><edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx";
>>Version="1.0">
>>
>><edmx:DataServices
>>xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
>>m:DataServiceVersion="1.0">
>>
>><Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm";
>>Namespace="com.sap.workplace.uiservice.odata">
>>
>><EntityType Name="Account">
>>
>><Key>
>>
>><PropertyRef Name="Id"/>
>>
>></Key>
>>
>><Property Name="Id" Type="Edm.Int32" Nullable="false"/>
>>
>><Property Name="AccountId" Type="Edm.String" Nullable="false"/>
>>
>><Property Name="AccountType" Type="Edm.String" Nullable="false"/>
>>
>><Property Name="AccountName" Type="Edm.String" Nullable="false"/>
>>
>><Property Name="AccountNumber" Type="Edm.String" Nullable="false"/>
>>
>><Property Name="Address"
>>Type="com.sap.workplace.uiservice.odata.Address"/>
>>
>></EntityType>
>>
>><EntityType Name="Producer">
>>
>><Key>
>>
>><PropertyRef Name="Id"/>
>>
>></Key>
>>
>><Property Name="Id" Type="Edm.Int32" Nullable="false"/>
>>
>><Property Name="ProducerName" Type="Edm.String" Nullable="false"/>
>>
>><Property Name="ProducerId" Type="Edm.String" Nullable="false"/>
>>
>><Property Name="FirstName" Type="Edm.String" Nullable="false"
>>MaxLength="100"/>
>>
>><Property Name="LastName" Type="Edm.String" Nullable="false"
>>MaxLength="100"/>
>>
>><Property Name="Address"
>>Type="com.sap.workplace.uiservice.odata.Address"/>
>>
>></EntityType>
>>
>><ComplexType Name="Address">
>>
>><Property Name="Address1" Type="Edm.String" Nullable="true"/>
>>
>><Property Name="Address2" Type="Edm.String" Nullable="true"/>
>>
>><Property Name="City" Type="Edm.String" Nullable="true"/>
>>
>><Property Name="State" Type="Edm.String" Nullable="true"/>
>>
>><Property Name="Country" Type="Edm.String" Nullable="true"/>
>>
>><Property Name="Zip" Type="Edm.String" Nullable="true"/>
>>
>></ComplexType>
>>
>><EntityContainer Name="SAPWorkplaceEntityContainer"
>>m:IsDefaultEntityContainer="true">
>>
>><EntitySet Name="Accounts"
>>EntityType="com.sap.workplace.uiservice.odata.Account"/>
>>
>><EntitySet Name="Producers"
>>EntityType="com.sap.workplace.uiservice.odata.Producer"/>
>>
>></EntityContainer>
>>
>></Schema>
>>
>></edmx:DataServices>
>>
>></edmx:Edmx>
>>
>> 
>>
>>
>>
>

Reply via email to