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