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>
>
>
>
>
>