# Client Application Developer’s Guide

# Advanced Topics

This chapter describes miscellaneous features that are related to client programming with Oracle Data Service Integrator. It includes the following topics:

- Data Service control Source Upgrade
- Accessing Metadata Using Catalog Services
- Filtering, Sorting, and Fine-tuning Query Results
- Using Inverse Functions to Improve Query Performance

### Data Service control Source Upgrade

You need to upgrade ALDSP 2.x controls to Oracle Data Service Integrator 10gR3 using source upgrade. After the upgrade, a version of DSP_Control.jar is placed in the WEB-INF/lib folder. The DSP_Control.jar file contains control runtime classes.

While performing the upgrade, ensure that the domain to which the Web-App is to be deployed includes the following files:

- `wldso.jar` file under `<ALDSP_HOME>/lib/`
- BEA V2 XmlBeans, `<ALDSP>/lib/sdo.jar` in its server classpath or Web-App library path

Source upgrade also upgrades the schemas used by Data Service control.

## Accessing Metadata Using Catalog Services

Oracle Data Service Integrator maintains metadata about data services, application, functions, and schemas through Catalog Services, which is a system catalog-type data service. Catalog services provide a convenient way for client-application developers to programmatically obtain information about Oracle Data Service Integrator applications, data services, schemas, functions, and relationships.

Catalog Services are also data services; you can view them using the Oracle Data Service Integrator Administration Console, the Oracle Data Service Integrator Palette, and Data Service controls.

Some advantages of using Catalog Services are as follows:

- Client application developers can use the Catalog Services in the same way as they use any other data service in Oracle Data Service Integrator.
- Application developers can create dynamic applications based on the metadata underlying the data service applications that have been deployed.
- For enterprise, third-party, and other developers, Catalog Services leverage the development of dynamic, metadata driven, query-by-form (QBF) applications.
- Catalog Services enable interoperability with other metadata repositories.

This section provides details about installing and using Catalog Services to access metadata for any Oracle Data Service Integrator application. It includes the following topics:

- Installing Catalog Services
- Using Catalog Services

### Installing Catalog Services

You can install Catalog Services as a project for an Oracle Data Service Integrator application or as a JAR file that is added to the Library folder in Workshop for WebLogic. The Catalog Services project (_catalogservices) contains data services that provide information about the application, folders, data services, functions, schemas, and relationships available with the application.

DataServiceRef and SchemaRef are additional data services that consist of functions that retrieve the paths to the data services and schemas available with the Oracle Data Service Integrator application. For more information about the data services and functions available with Catalog Services, refer to Using Catalog Services on page 8-4.

To install Catalog Services as a project:

1. Right-click the Oracle Data Service Integrator application in Workshop for WebLogic.
2. Select the Install Catalog Services (Expanded) option if you want to use the catalog services for development, as shown in Figure 8-1. If you need catalog services only during runtime then select Install Catalog Services (Jar) option.
Figure 8-1 Installing Catalog Services

### Using Catalog Services

After installing Catalog Services, the catalog services project, _catalogservices, is created for the Oracle Data Service Integrator application. All the data services associated with catalog services are available under this project. You can invoke the data service functions to access metadata. The client Mediator API is used to invoke the Catalog Service methods.

The data services available under _catalogservices include:

- Application (application.ds)
- DataServiceRef (DataServiceRef.ds)
- Folder (folder.ds)
- Function (Function.ds)
- Relationship (Relationship.ds)
- Schema (Schema.ds)
- The following table provides the declaration and description for the functions available in Schema.dsSchemaRef (SchemaRef.ds)

#### Application (application.ds)

The following table provides the declaration and description for the `getApplication()`function in Application.ds.

Table 8-2 Functions in Application.ds
Function Declaration | Description
--- | ---
`getApplication`() as schema-element(t1:Application) external; | This function returns the name of the Oracle Data Service Integrator application. It does not take any parameters.

#### DataService (DataService.ds)

Table 8-3 provides declaration and description information for the functions available in DataService.ds.

Table 8-3 Functions in DataService.ds
Function Declaration | Description | Sample Input
--- | --- | ---
`getDataServiceRef``($arg as element(md:DataService)) as element(md:DataServiceRef) {$arg/md:DataServiceRef}` | This function returns the path of the data service associated with the function. For this function, you need to specify the following: Path of the data service Path of the schema for the data service Function ID of the function for which you need the data service reference | `<urn:DataService kind="javaFunction" xmlns:acc="``ld:RTLAppDataServices/CustomerDB/Customer``" xmlns:urn="urn:metadata.ld.bea.com">` `<urn:DataServiceRef> <id>``ld:RTLAppDataServices/CustomerDB/Customer.ds``</id>` `</urn:DataServiceRef>` `<returnType name="``CUSTOMER``" kind="``read``" quantifier="``*``" schemaId="``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``"/>` `<!--Zero or more repetitions:-->` `<key>` `<!--1 or more repetitions:--> <path>``ld:RTLAppDataServices/CustomerDB/Customer.ds``</path>` `</key>` `<!--Zero or more repetitions:-->` `<urn:FunctionId name="``CUSTOMER``" arity="``0``"/>` `</urn:DataService>`
`getDataService``($x1 as element(t1:DataServiceRef)) as schema-element(t1:DataService)? external` | This function returns the attributes of the specified data service such as the schema path, functions, and relational data source. Specify the path of the data service to retrieve the required result. | `<DataServiceRef xmlns="urn:metadata.ld.bea.com">` `<id xmlns="">``ld:DataServices/CustomerDB/CUSTOMER.ds``</id>` `</DataServiceRef>`

#### DataServiceRef (DataServiceRef.ds)

The following table provides the declaration and description for the functions available in DataServiceRef.ds.

Table 8-4 Functions in DataService
Function Declaration | Description | Sample Input
--- | --- | ---
`getDataServiceRefsByFolder``($x1 as xsd:string, $x2 as xsd:boolean) as schema-element(t1:DataServiceRef)* external` | This function returns the data services that exist within a folder in the project. You need to specify the path of the project folder and set the boolean value to true for this function. | String parameter = `ld:RTLAppDataServices/CustomerDB/` Boolean =`true`
`getDataServiceRefs``() as schema-element(t1:DataServiceRef)* external` | This function returns the path to all the data services in the project. It does not require any parameters. | No input required.
`getDependents``($x1 as element(t1:DataServiceRef), $x2 as xsd:boolean) as schema-element(t1:DataServicRef)* external` | This function returns the path of the data services on which the specified data service depends. For this function, you need to specify the path of the data service whose dependents you need to determine. For example, if you need to find out the dependents for CUSTOMER.ds then specify the path of the data service as: `ld:DataServices/CustomerDB/CUSTOMER.ds` | `<urn:DataServiceRefdat xmlns:urn="urn:metadata.ld.bea.com"> <id>``ld:DataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>`
`getDependencies``($x1 as element(t1:DataServiceRef), $x2 as xsd:boolean) as schema-element(t1:DataServiceRef)* external` | This function returns the dependencies for the specified data service. For this function, you need to specify the path of the data service whose dependencies you need to determine. | `<urn:DataServiceRef xmlns:urn="urn:metadata.ld.bea.com"> <id>``ld:DataServices/Demo/CustomerProfile.ds``</id>` `</urn:DataServiceRef>`
`getFunctions``($x1 as element(t1:DataServiceRef)) as schema-element(t1:Function)* external` | This function returns the list of data service functions and their attributes such as function kind, arity, and schema path. For this function, specify the path of the data service as input. | `<DataServiceRef xmlns="urn:metadata.ld.bea.com">` `<id xmlns="">``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</DataServiceRef>`
`getRelationships``($x1 as element(t1:DataServiceRef)) as schema-elemen43t(t1:Relationship)* external` | This function retrieves the path of data services which have any relationship with the specified data service. You need to specify the path of the data service, such as `ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds` | `DataServiceRef xmlns="urn:metadata.ld.bea.com">` `<id xmlns="">``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds</id>``` `</DataServiceRef>`
`getSchemaRefs``($arg as element(t1:DataServiceRef), $transitive as xs:boolean) as element(t1:SchemaRef)* external` | For this function, enter the path of the data service and set the boolean value to true for retrieving the list of associated schemas. This function also lists the paths of schemas for data services, which have a relationship with the specified data service. | `<urn:DataServiceRef xmlns:urn="urn:metadata.ld.bea.com"> <id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>` `Enter true as the boolean parameter`.
`getDataService``($x1 as element(t1:DataServiceRef)) as schema-element(t1:DataService)? external` | This function returns the attributes of the specified data service such as the schema path, functions, and relational data source. Specify the path of the data service to retrieve the required result. | `<urn:DataServiceRef xmlns:urn="urn:metadata.ld.bea.com">` `<id xmlns="">``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>`

#### Folder (folder.ds)

The following table provides the declaration and description for the functions available in Folder.ds.

Table 8-5 Functions in Folder.ds
Function Declaration | Description | Sample Input
--- | --- | ---
`getFolder``() as schema-element(t1:Folder)* external` | This function provides a list of paths of folders and data services that exist within the Oracle Data Service Integrator project. It does not require any parameters. | No input required.
`getFolder``($x1 as xsd:string, $x2 as xsd:boolean) as schema-element(t1:Folder)* external` | This folder returns the paths of all the data services that exists within a specified folder. You need to specify two parameters for this function, which include: Path of the folder such as `ld:RTLAppDataServices/CustomerDB` Boolean value (usually set to true) | Parameter 1 (string) = `ld:RTLAppDataServices/CustomerDB` Parameter 2 (boolean) = `true`
`getDataServiceRefs``($x1 as element(t1:Folder)) as schema-element(t1:DataServiceRef)* external` | This function also provides the paths of the data services that exist within a folder. To retrieve this information, specify the path of the folder as input. | `<Folder xmlns="urn:metadata.ld.bea.com">` `<id xmlns="">``ld:RTLAppDataServices/CustomerDB``</id>` `</Folder>`

#### Function (Function.ds)

The following table provides the declaration and description for the functions in Function.ds.

Table 8-6 Functions in Function.ds
Function Declaration | Description | Sample Input
--- | --- | ---
`getFunctionById``($x1 as element(t1:FunctionId)) as schema-element(t1:Function) external4` | This function returns the path of the data service and schema along with function arity, function kind and return type information about the specified function. For this function, specify the function ID and arity as input. | `<FunctionId name="` `cus:CUSTOMER``"` `arity="0"``xmlns:cus="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``" xmlns="urn:metadata.ld.bea.com"/>`
`getDataService``($arg as element(md:Function)) as element(md:DataService` | This function returns the function arity and physical data source information for the specified function. For this function, you need to specify the function ID, path of the data service and schema. | `<?xml version="1.0" encoding="UTF-8" ?>` `<urn:Function kind="``read``" xmlns:acc="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``"` `xmlns:urn="urn:metadata.ld.bea.com">` `<urn:FunctionId arity="``0``" name="``acc:getAll``"></urn:FunctionId>` `<returnType kind="``element``" name="``urn:Account``" quantifier="``1``" schemaId="``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``">` `</returnType>` `<urn:DataServiceRef> <id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id> </urn:DataServiceRef>` `</urn:Function>`
`getRelationship``($arg as element(md:Function)) as element(md:Relationship) external` getFunction()relationship function | This function returns the relationship target and path of the data services with which the navigation function has a relationship. Specify the function ID, path of the data service and schema as input.
Note: | This function is applicable to navigation functions only.
| `<?xml version="1.0" encoding="UTF-8" ?>` `<urn:Function kind="``navigate``" xmlns:acc="``ld:RTLAppDataServices/CustomerDB/Customer``" xmlns:urn="urn:metadata.ld.bea.com">` `<urn:FunctionId arity="``1``" name="acc:``getDISCOUNT``">` `</urn:FunctionId>` `<returnType kind="``element``" name="urn:``getDISCOUNT``" quantifier="``1``" schemaId="``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``">` `</returnType>` `<parameter name="``arg``">` `<type kind="``navigate``" name="urn:``DISCOUNT``" quantifier="``*``" schemaId="``ld:RTLAppDataServices/CustomerDB/CUSTOMER.xsd``">` `</type>` `</parameter>` `<urn:DataServiceRef>` `<id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>` `<roleName>``DISCOUNT``</roleName>` `</urn:Function>`
`getSchemaRefs``($x1 as element(t1:Function), $x2 as xsd:boolean) as schema-element(t1:SchemaRef)* external` DataServicesRef_ | For this function, specify the function ID and path of the data service to retrieve the path of the schemas associated with the data service. | `<urn:Function kind="``navigate``" xmlns:acc="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``" xmlns:urn="urn:metadata.ld.bea.com">` `<urn:FunctionId name="``acc:getDISCOUNT``" arity="``1``"/>` `<returnType name="``DISCOUNT``" kind="``element``" quantifier="``*``" schemaId="``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``"/>` `<!--Zero or more repetitions:-->` `<parameter name="``arg``">` `<type name="``DISCOUNT``" kind="``element``" quantifier="``*``" schemaId="``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``"/>` `</parameter>` `<urn:DataServiceRef>` `<id>``ld:RTLAppDataServices/CustomerDB/Customer.ds``</id>` `</urn:DataServiceRef>` `<!--Optional:-->` `<roleName>``DISCOUNT``</roleName>` `</urn:Function>`

#### Relationship (Relationship.ds)

The following table provides the declaration and description for the functions available in Relationship.ds.

Note: | The functions in Relationship.ds can be used to access metadata only for navigation functions.

Table 8-7 Functions in Relationship.ds
Function Declaration | Description | Sample Input
--- | --- | ---
`getFunctions``($arg as element(md:Relationship)) as element(md:Function)` | This function returns the attributes of the function that you specify as input. You need to specify the following parameters for this function: String parameter = Path of the data service Function ID Values for minOccurs and maxOccurs | `<urn:Relationship xmlns:acc="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``" xmlns:urn="urn:metadata.ld.bea.com">` `<!--1 to 2 repetitions:-->` `<relationshipTarget roleName="``DISCOUNT``" minOccurs="``1``" maxOccurs="``1``" description="">` `<urn:DataServiceRef>` `<id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>` `<!--Zero or more repetitions:-->` `<urn:FunctionId name="``acc:getDISCOUNT``" arity="``1``"/>` `</relationshipTarget>` `</urn:Relationship>`
`getDataServiceRefs``($x1 as element(t1:Relationship)) as schema-element(t1:DataServiceRef)` | You need to specify the following parameters for this function: String parameter = Path of the data service Function ID Values for minOccurs and maxOccurs | `<urn:Relationship xmlns:acc="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``" xmlns:urn="urn:metadata.ld.bea.com">` `<!--1 to 2 repetitions:-->` `<relationshipTarget roleName="``DISCOUNT``" minOccurs="``1``" maxOccurs="``1``" description="">` `<urn:DataServiceRef>` `<id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>` `<!--Zero or more repetitions:-->` `<urn:FunctionId name="``acc:getDISCOUNT``" arity="``1``"/>` `</relationshipTarget>` `</urn:Relationship>`
`getDataServices``($arg as element(md:Relationship)) as element(md:DataService)` | This function returns the attributes, such as relational datasource and function arity, of the navigation function of the data service. For this function, you need to specify the following parameters: String parameter = Path of the data service String parameter = Path of the schema Values for maxOccurs and minOccurs FunctionID | `<?xml version="1.0" encoding="UTF-8" ?>` `<urn:Relationship xmlns:acc="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``" xmlns:urn="urn:metadata.ld.bea.com">` `<relationshipTarget description="" maxOccurs="``1``" minOccurs="``1``" roleName="``DISCOUNT``">` `<urn:DataServiceRef> <id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>` `<urn:FunctionId arity="``1``" name="acc:``getDISCOUNT``">` `</urn:FunctionId>` `</relationshipTarget>` `<relationshipTarget description="" maxOccurs="``1``" minOccurs="``1``" roleName="``DISCOUNT``" xmlns:acc="``ld:RTLAppDataServices/CustomerDB/CUSTOMER``">` `<urn:DataServiceRef>` `<id>``ld:RTLAppDataServices/CustomerDB/CUSTOMER.ds``</id>` `</urn:DataServiceRef>` `<urn:FunctionId arity="``1``" name="``acc:getDISCOUNT``">` `</urn:FunctionId>` `</relationshipTarget>` `</urn:Relationship>`

#### Schema (Schema.ds)

The following table provides the declaration and description for the functions available in Schema.ds

Table 8-8 Functions in Schema.ds
Function Declaration | Description | Sample Input
--- | --- | ---
`getSchema``($x1 as element(t1:SchemaRef)) as schema-element(t1:Schema)* external` | This functions returns the schema attributes of the schema associated with the data service. You need to specify the path of the schema which you need to access as string parameter. For example: `ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd` | `<urn:SchemaRef xmlns:urn="urn:metadata.ld.bea.com">` `<id>``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``</id>` `</urn:SchemaRef>`
`getSchemaRef``($x1 as element(t1:Schema)) as schema-element(t1:SchemaRef)` | This function returns the path of the schema of the data service. Specify the schema path to get the reference to the schema. For example: `ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER_TABLE.xsd` | `<urn:Schema xmlns:urn="urn:metadata.ld.bea.com">` `<urn:SchemaRef>` `<id>``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER_TABLE.xsd``</id>` `</urn:SchemaRef>` `</urn:Schema>`

SchemaRef (SchemaRef.ds)

The following table provides the declaration and description for the functions available in SchemaRef.ds.

Table 8-9 Functions in SchemaRef.ds
Function Declaration | Description | Sample Input
--- | --- | ---
`getDependencies``($x1 as element(t1:SchemaRef), $x2 as xs:boolean) as schema-element(t1:SchemaRef)* external` | This function returns the dependencies of the specified data service. You need to specify the path of the schema for the data service as a string parameter. For example: `ld:DataServices/Demo/schemas/CustomerProfile.xsd` | `<urn:SchemaRef xmlns:urn="urn:metadata.ld.bea.com">` `<id xmlns="">``ld:DataServices/Demo/schemas/CustomerProfile.xsd``</id>` `</urn:SchemaRef>` The second parameter is boolean and the value can be either `true` or `false`.
`getSchema``($x1 as element(md:SchemaRef)) as schema-element(md:Schema)* external` | This functions returns the schemas associated with the data service. You need to specify the path of the schema for the data service as a string parameter. For example: `ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd` | `<urn:SchemaRef xmlns:urn="urn:metadata.ld.bea.com">` `<id>``ld:RTLAppDataServices/CustomerDB/schemas/CUSTOMER.xsd``</id>` `</urn:SchemaRef>`

## Filtering, Sorting, and Fine-tuning Query Results

The Filter API enables client applications to apply filtering conditions to the information returned by data service functions. In a sense, filtering allows client applications to extend a data service interface by allowing the application to specify more about how data objects are to be instantiated and returned by functions.

The Filter API alleviates data service designers from having to anticipate every possible data view that clients may require and to implement a data service function for each view. Instead, designers can choose to specify a broader, more generic interface for accessing a business entity and allow client applications to control views as desired through filters.

Using the API, you can specify that only objects that meet a particular condition in the function return set be returned to the client. A filter is therefore similar to a WHERE clause in an XQuery or SQL statement—it applies conditions to a possible result set. You can apply multiple filter conditions using `AND` and `OR` operators.

The effects of a filter can vary, depending on the desired results. Consider, for example, the CUSTOMERS data object shown in Figure 8-10. The data object contains multiple complex elements (CUSTOMER and ORDERS) and several simple elements, including ORDER_AMOUNT.

Figure 8-10 Nested Value Filtering

You can apply a filter to any element in this hierarchy. For example, you could apply a filter to return all CUSTOMER objects but filter ORDERS than have an ORDER_AMOUNT greater than 1000. Similarly, you could apply a filter to return only the CUSTOMER objects that have at least one large order.

You can also use a filter to specify the order criteria (ascending or descending) in which results should be returned from the data service. Finally, you can use a filter to set the maximum number of results to be returned.

Note: | Filter evaluation occurs at the server, so objects that are filtered are not passed over the network. Often, objects that are filtered out are not even retrieved from the underlying data sources.

### Introducing the Filter API

You specify filters using the `FilterXQuery` object, which includes the following methods enabling you to add a filter, create a filter to apply later, specify the sort order, and set a limit on the number of results returned:

- addFilter()
- createFilter()
- addOrderBy()
- setLimit()

#### addFilter()

The `addFilter()` method enables you to create a filter and add it to the list of filters. The `addFilter()` method has several signatures with different parameters, including the following:

    public void addFilter(java.lang.String appliesTo,
                          java.lang.String field,
                          java.lang.String operator,
                          java.lang.String value,
                          java.lang.Boolean everyChild)

This version of the method takes the following arguments:

- `appliesTo` specifies the node (the XPath relative to the document element) that the filtering affects. That is, if a node specified by the `field` argument does not meet the condition, `appliesTo` nodes are filtered out.
- `field` is the node against which the filtering condition is tested.
- `operator` and `value` together comprise the condition statement. The `operator` parameter specifies the type of comparison to be made against the specified `value`. See Table 8-11, Filter Operators, on page 8-20 for information about available operators.
- `everyChild` is an optional parameter. It is set to false by default. Specifying true for this parameter indicates that only those child elements that meet the filter criteria will be returned. For example, by specifying an operator of GREATER_THAN (or ">") and a value of 1000, only records for customers where all orders are over 1000 will be returned. A customer that has an order amount less than 1000 will not be returned, although other order amounts might be greater than 1000.

#### createFilter()

The `createFilter()` method enables you to create a filter that you can later apply to any of the objects in the hierarchy. The `createFilter()` method has several signatures with different parameters, including the following:

    public void createFilter(java.lang.String field,
                             java.lang.String operator,
                             java.lang.String value,
                             java.lang.Boolean everyChild)

This version of the method takes the following arguments:

- `field` is the node against which the filtering condition is tested (specified as the XPath relative to the document element).
- `operator` and `value` together comprise the condition statement. The `operator` parameter specifies the type of comparison to be made against the specified `value`. See Table 8-11, Filter Operators, on page 8-20 for information about available operators.
- `everyChild` is an optional parameter. It is set to false by default. Specifying true for this parameter indicates that only those child elements that meet the filter criteria will be returned.

#### addOrderBy()

The `addOrderBy()` method enables you to add a sort criteria (either ascending or descending) to the specified object. The `addOrderBy()` method has the following signature:

    public void addOrderBy(java.lang.String appliesTo,
                           java.lang.String field,
                           java.lang.String sort)

The method takes the following arguments:

- `appliesTo` specifies the node returned by the filter (specified as the XPath relative to the document element).
- `field` specifies the node to which the ordering is applied, relative (not the full path) to the `appliesTo` node.
- `sort` is the sort criteria (either ascending or descending)

#### setLimit()

The `setLimit()` method enables you to specify the maximum number of entries to return of the specified object. The `setLimit()` method has the following signature:

    public void setLimit(java.lang.String appliesTo,
                         java.lang.String max)

The method takes the following arguments:

- `appliesTo` specifies the node (the XPath relative to the document element) to which the filter is applied.
- `max` is the maximim number of entries to return (an int value specified as a string, for example, “10”).

#### Exploring the Filter Operators

Table 8-11 describes the operators that you can apply to filter conditions.

Table 8-11 Filter Operators
Operator | Usage Note or Example
--- | ---
LESS_THAN | Can also use "<". For example: `myFilter.addFilter("CUST/CUST_ORDER/ORDER", "CUST/CUST_ORDER/ORDER/ORDER_AMOUNT", "<", "1000");` is identical to `myFilter.addFilter("CUST/CUST_ORDER/ORDER", "CUST/CUST_ORDER/ORDER/ORDER_AMOUNT", FilterXQuery.LESS_THAN, "1000");`
GREATER_THAN | Can also use ">".
LESS_THAN_EQUAL | Can also use "<=".
GREATER_THAN_EQUAL | Can also use ">=".
EQUAL | Can also use "=".
NOT_EQUAL | Can also use "!=".
MATCHES | Tests for string equality.
BEA_SQL_LIKE | Tests whether a string contains a specified pattern in a manner similar to the SQL LIKE clause.
AND | Compound operator that can apply to more than one filter.
OR | Compound operator that can apply to more than one filter.
NOT | Compound operator that can apply to more than one filter.

Note: | Filter API Javadoc, and other Oracle Data Service Integrator APIs are available on e-docs.

### Using Filters

Filtering capabilities are available to Mediator and Oracle Data Service Integrator Control client applications. To use filters in a mediator client application, import the appropriate package and use the supplied interfaces for creating and applying filter conditions.

Data service control clients get the interface automatically. When a function is added to a control, a corresponding "WithFilter" function is added as well.

The filter package is named as follows:

    	com.bea.ld.filter.FilterXQuery;

To use a filter, perform the following steps:

1. Create an `FilterXQuery` object, such as:

    FilterXQuery myFilter = new FilterXQuery();

2. Add a condition to the filter object using the `addFilter()` method.

The following example shows how to add a filter to have orders with an order amount greater than 1000 returned (note that the optional `everyChild` parameter is not specified, so order amounts below 1000 will also be returned):

         myFilter.addFilter("CUSTOMERS/CUSTOMER/ORDER",
                            "CUSTOMERS/CUSTOMER/ORDER/ORDER_AMOUNT",
                            ">",
                             "1000");

3. Use the Mediator API call `setFilter()` to add the filter to a RequestConfig object, and pass the RequestConfig as an argument to the data service operation invocation. For example,

    RequestConfig config = new RequestConfig();
    config.setFilter(myFilter);
    CUSTOMERDAS custDAS = CUSTOMER.getInstance(ctx, "RTLApp");
    custDS.myOperation(config);

4. Invoke the data service function.

For more information on invoking data service functions, see Chapter 3, “Invoking Data Services from Java Clients.”

### Filtering Examples

In general, with nested XML data, a condition such as “CUSTOMER/ORDER/ORDER_AMOUNT > 1000” can affect what objects are returned in several ways. For example, it can cause all CUSTOMER objects to be returned, but filter ORDERS that have an amount less than 1000.

Alternatively, it can cause only CUSTOMER objects to be returned that have at least one large order, but containing all ORDERs (small and large) for each such CUSTOMER.

The following examples show how filters can be applied in several different ways:

- Returns all CUSTOMER objects but only their large ORDER objects:

    FilterXQuery myFilter = new FilterXQuery();
    Filter f1 = myFilter.createFilter(
                         "CUSTOMERS/CUSTOMER/ORDER/ORDER_AMOUNT",
                         FilterXQuery.GREATER_THAN,"1000");
    myFilter.addFilter("CUSTOMERS/CUSTOMER/ORDER", f1);

- Returns only CUSTOMER objects that have at least one large order but view all ORDER objects for such CUSTOMER objects:

    FilterXQuery myFilter = new FilterXQuery();
    myFilter.addFilter("CUSTOMERS/CUSTOMER",
                       "CUSTOMERS/CUSTOMER/ORDER/ORDER_AMOUNT",
                        FilterXQuery.GREATER_THAN,"1000");

- Returns only CUSTOMER objects that have at least one large order and return only large ORDER objects:

    FilterXQuery myFilter = new FilterXQuery();
    myFilter.addFilter("CUSTOMERS/CUSTOMER",
                       "CUSTOMERS/CUSTOMER/ORDER/ORDER_AMOUNT",
                        FilterXQuery.GREATER_THAN,"1000");
    myFilter.addFilter("CUSTOMERS/CUSTOMER/ORDER",
                       "CUSTOMERS/CUSTOMER/ORDER/ORDER_AMOUNT",
                        FilterXQuery.GREATER_THAN,"1000");

- Returns only CUSTOMER objects for which every ORDER_AMOUNT is greater than 1000:

    	FilterXQuery myFilter = new FilterXQuery();
    	myFilter.addFilter("CUSTOMERS/CUSTOMER",
    	                   "CUSTOMERS/CUSTOMER/ORDER/ORDER_AMOUNT",
    	                    FilterXQuery.GREATER_THAN,"1000",true);

Note that the `everyChild` flag is set to true; by default this parameter is false.

#### Specifying a Compound Filter

You can create a filter with two conditions using logical AND and OR operators. Listing 8-1 uses the AND operator to apply a combination of filters to a result set, given a data service instance `customerDS`.

Listing 8-1 Example of Combining Filters by Using Logical Operators

    FilterXQuery myFilter = new FilterXQuery();
    Filter f1 = myFilter.createFilter("CUSTOMER_PROFILE/ADDRESS/ISDEFAULT",
    		 			FilterXQuery.NOT_EQUAL,"0");
    Filter f2 = myFilter.createFilter("CUSTOMER/ADDRESS/STATUS",
    					FilterXQuery.EQUAL,
    					"\"ACTIVE\"");
    Filter f3 = myFilter.createFilter(f1,f2, FilterXQuery.AND);

#### Ordering and Truncating Data Service Results

You can specify the order criteria (ascending or descending) in which results should be returned from the data service. The `addOrderBy()` method accepts a property name as the criterion upon which the ascending or descending decision is based.

Listing 8-2 provides an example of creating a filter to return customer profiles in ascending order, based on the date each person became a customer.

Listing 8-2 Example of Applying an Ordering Filter

    FilterXQuery myFilter = new FilterXQuery();
    myFilter.addOrderBy("CUSTOMER_PROFILE",
    			"CustomerSince" ,FilterXQuery.ASCENDING);

Similarly, you can set the maximum number of results to be returned using the `setLimit()` method. Listing 8-3 shows how to use the `setLimit()` method to limit the number of active addresses in the result set to 10.

Listing 8-3 Example of Applying a Filter that Truncates (Limits) Results

    FilterXQuery myFilter = new FilterXQuery();
    Filter f2 = myFilter.createFilter("CUSTOMER_PROFILE/ADDRESS",
                                       FilterXQuery.EQUAL,"\"INACTIVE\"");
    myFilter.addFilter("CUSTOMER_PROFILE", f2);
    myFilter.setLimit("CUSTOMER_PROFILE", "10");

### Using Ad Hoc Queries to Fine-tune Results from the Client

An ad hoc query is an XQuery function that is not defined as part of a data service, but is instead defined in the context of a client application. Ad hoc queries are typically used in client applications to invoke data service functions and refine the results in some way. You can use an ad hoc query to execute any valid XQuery expression against a data service. The expression can target the actual data sources that underlie the data service, or can use the functions and procedures hosted by the data service.

To execute an XQuery expression, use the PreparedExpression interface, available in the Mediator API. Similar to JDBC PreparedStatement interface, the PreparedExpression interface takes the XQuery expression as a string in its constructor, along with the JNDI server context and application name. After constructing the prepared expression object in this way, you can call the executeQuery( ) method on it. If the ad hoc query invokes data service functions or procedures, the data service’s namespace must be declared by the query string before you can reference the methods in your ad hoc query.

Listing 8-4 shows a complete example; the code returns the results of a data service function named getCustomers( ), which is in the namespace:

    ld:DataServices/RTLServices/Customer

Listing 8-4 Invoking Data Service Functions using an Ad Hoc Query

    import com.bea.dsp.das.DataAccessServiceFactory;
    import com.bea.dsp.das.PreparedExpression;

    String queryStr =
          "declare namespace ns0=\"ld:DataServices/RTLServices/Customer\";" +
          "<Results>" +
          "  { for $customer_profile in ns0:getCustomer()" +
          "     return $customer_profile }" +
          "</Results>";
    PreparedExpression adHocQuery =
          DataServiceFactory.prepareExpression(context,"RTLApp",queryStr );
    DASResult<Object> result = adHocQuery.executeQuery();

Note that the return type of the `executeQuery( )` method is `DASResult<Object>`. The kinds of Objects that can be returned from this `DASResult` are the same as for data service operations invoked using the dynamic mediator API. Simple schema types, such as `xs:int` and `xs:decimal` are returned as Java Objects (`java.lang.Integer`, `java.math.BigDecimal`) according to the mapping described in Table 2-5, “XML Schema to Java Data Type Mapping,” on page 2-6. Complex types are returned as SDO `DataObjects`.

A single ad-hoc query may return multiple Objects, corresponding to the sequence of items in the result of the XQuery expression. Each of these items are returned as a single Object from calls to `result.next()`.

Because ad-hoc queries are defined inside the client code itself, the Mediator API cannot know the return type of the query. That is why the return value of `executeQuery( )` is `DASResult<Object>` rather than a more specific type such as `DASResult<Customer>`, even if the query only returns Customer DataObjects.

However, if the query does return DataObjects whose schema is defined in a static mediator client JAR, as described in Chapter 3, “Invoking Data Services from Java Clients,” and that static mediator client JAR is on the client’s CLASSPATH, it is possible to cast the Objects from the `DASResult` to the corresponding typed `DataObject`, just as it is with the dynamic mediator API. For instance,

    DASResult<Object> result = adHocQuery.executeQuery();
    Customer cust = (Customer) cust.next();

Note that if the results of the ad-hoc query are not actually Customer DataObjects, the above code throws a ClassCastException when attempting to cast the result of `cust.next()`.

Security policies defined for a data service apply to the data service calls in an ad hoc query as well. If an ad hoc query uses secured resources, the appropriate credentials must be passed when creating the JNDI initial context. (For more information, see “Obtaining the WebLogic JNDI Context for Oracle Data Service Integrator” on page 3-47.)

As with the PreparedStatement interface of JDBC, the PreparedExpression interface supports dynamically binding variables in ad hoc query expressions. PreparedExpression provides several methods (bindType( ) methods; see Table 8-12), for binding values of various data types.

Table 8-12 PreparedExpression Methods for Bind Variables
To bind data type of... | Use bind method...
--- | ---
`Binary` | `bindBinary(javax.xml.namespace.QName qname, byte[] abyte0)`
`Boolean` | `bindBoolean(javax.xml.namespace.QName qname, boolean flag)`
`Byte` | `bindByte(javax.xml.namespace.QName qname, byte byte0)`
`Date` | `bindDate(javax.xml.namespace.QName qname, java.sql.Date date)`
`Calendar` | `bindDateTime(javax.xml.namespace.QName qname, java.util.Calendar calendar)`
`DateTime` | `bindDateTime(javax.xml.namespace.QName qname, java.util.Date date)`
`DateTime` | `bindDateTime(javax.xml.namespace.QName qname, java.sql.Timestamp timestamp)`
`BigDecimal` | `bindDecimal(javax.xml.namespace.QName qname, java.math.BigDecimal bigdecimal)`
`double` | `bindDouble(javax.xml.namespace.QName qname, double d)`
`Element` | `bindElement(javax.xml.namespace.QName qname, org.w3c.dom.Element element)`
`Object` | `bindElement(javax.xml.namespace.QName qname, java.lang.String s)`
`float` | `bindFloat(javax.xml.namespace.QName qname, float f)`
`int` | `bindInt(javax.xml.namespace.QName qname, int i)`
`long` | `bindLong(javax.xml.namespace.QName qname, long l)`
`Object` | `bindObject(javax.xml.namespace.QName qname, java.lang.Object obj)`
`short` | `bindShort(javax.xml.namespace.QName qname, short word0)`
`String` | `bindString(javax.xml.namespace.QName qname, java.lang.String s)`
`Time` | `bindTime(javax.xml.namespace.QName qname, java.sql.Time time)`
`URI` | `bindURI(javax.xml.namespace.QName qname, java.net.URI uri)`

To use the bindType methods, pass the variable name as an XML qualified name (QName) along with its value; for example:

    adHocQuery.bindInt(new QName("i"),94133);

Listing 8-5 shows an example of using a `bindInt()` method in the context of an ad hoc query. Note that all variables to be bound must be explicitly declared as `external` variables in the ad-hoc query, as shown in the example.

Listing 8-5 Binding a Variable to a QName (Qualified Name) for use in an Ad Hoc Query

    PreparedExpression adHocQuery = DataServiceFactory.preparedExpression(
                     context, "RTLApp",
                    "declare variable $i as xs:int external;
                     <result><zip>{fn:data($i)}</zip></result>");
    adHocQuery.bindInt(new QName("i"),94133);
    DASResult<Object> result = adHocQuery.executeQuery();

Note: | For more information on QNames, see:

    http://www.w3.org/TR/xmlschema-2/#QName

Listing 8-6 shows a complete ad hoc query example, using the PreparedExpression interface and QNames to pass values in bind methods.

Listing 8-6 Sample Ad Hoc Query

    import com.bea.dsp.das.PreparedExpression;
    import com.bea.dsp.das.DataAccessServiceFactory;
    import commonj.sdo.DataObject;
    import javax.naming.InitialContext;
    import javax.xml.namespace.QName;
    import weblogic.jndi.Environment;

    public class AdHocQuery
    {
       public static InitialContext getInitialContext() throws NamingException {
        Environment env = new Environment();
        env.setProviderUrl("t3://localhost:7001");
        env.setInitialContextFactory("weblogic.jndi.WLInitialContextFactory");
        env.setSecurityPrincipal("weblogic");
        env.setSecurityCredentials("weblogic");
        return new InitialContext(env.getInitialContext().getEnvironment());
       }

       public static void main (String args[]) {
         System.out.println("========== Ad Hoc Client ==========");
         try {
          StringBuffer xquery = new StringBuffer();
          xquery.append("declare variable $p_firstname as xs:string external; \n");
          xquery.append("declare variable $p_lastname as xs:string external;  \n");

          xquery.append(
           "declare namespace ns1=\"ld:DataServices/MyQueries/XQueries\"; \n");
          xquery.append(
           "declare namespace ns0=\"ld:DataServices/CustomerDB/CUSTOMER\"; \n\n");

          xquery.append("<ns1:RESULTS>                                    \n");
          xquery.append("{                                                \n");
          xquery.append("    for $customer in ns0:CUSTOMER()              \n");
          xquery.append("    where ($customer/FIRST_NAME eq $p_firstname  \n");
          xquery.append("       and $customer/LAST_NAME eq $p_lastname)   \n");
          xquery.append("    return                                       \n");
          xquery.append("      $customer                                  \n");
          xquery.append(" }                                               \n");
          xquery.append("</ns1:RESULTS>                                   \n");

             PreparedExpression pe = DataAccessServiceFactory.prepareExpression(
                            getInitialContext(), "RTLApp",  xquery.toString());
             pe.bindString(new QName("p_firstname"), "Jack");
             pe.bindString(new QName("p_lastname"),  "Black");
             DASResult<Object> results = pe.executeQuery();

           } catch (Exception e) {
               e.printStackTrace();
           }
    }

## Using Inverse Functions to Improve Query Performance

When designing and implementing data services, one of the principal goals is to provide a set of abstractions that enable client applications to see and manipulate integrated enterprise data in a clean, unified, meaningful, canonical form. Doing so invariably requires transforming data, which can include restructuring and unifying the schemas and the instance-level data formats of the disparate data sources.

In such cases, names may be reformatted, addresses normalized, and differences in units reconciled, among other operations, to provide application developers (the consumers of data services) with a natural and easily manipulable view of the underlying data. Such transformations, while highly useful to the end consumers of the data, can lead to performance challenges when retrieving underlying data.

When the resulting data is queried, it is crucial for performance that much of the query processing (especially for selections and joins) be pushable to the underlying sources, particularly to relational data sources. This requires updates to the transformed view of the data to be translatable back into appropriate source updates. Unfortunately, if data transformations are written in a general-purpose programming language, such as Java, both of these requirements can be difficult. This is because, unlike user-written XQuery functions, such general-purpose functions are opaque to the Oracle Data Service Integrator query and update processors.

### The Inverse Function Solution

To solve this issue, Oracle Data Service Integrator enables data service developers to register inverse functions with the system, enabling you to define and use general user-defined data transformations without sacrificing query pushdown and updatability. Using this information, Oracle Data Service Integrator is able to perform a reverse transformation of the data when analyzing query predicates or attempting to decompose updates into underlying data source updates.

This means that you can use inverse functions to retain the benefits of high-performance data processing for your logical data without giving up application-oriented convenience data functions. In addition, inverse functions enable automated updates without the need to create Java update overrides.

Note: | Using inverse functions effectively and correctly requires careful design. In particular, you must ensure that the functions are true inverses of one another, otherwise Oracle Data Service Integrator may perform undesired operations on your data. While inverse functions are an intuitive and useful idea, be aware that the details require careful attention.

#### Understanding Invertible Functions

The thing to keep in mind when creating inverse functions is that the functions you create need to be truly invertible.

For example, in the following case date is converted to a string value:

    public static String dateToString(Calendar cal) {
       SimpleDateFormat formatter;
       formatter = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a");
       return formatter.format(cal.getTime()) ;
    }

However, notice that the millisecond value is not in the return string value. You get data back but you have lost an element of precision. By default, all values projected are used for optimistic lock checking, so a loss of precision can lead to a mismatch with the database's original value and thus an update failure.

Instead the above code should have retained millisecond values in its return string value, thus ensuring that the data you return is exactly the same as the original value.

### How Inverse Functions Can Improve Performance

Here are some additional scenarios where inverse functions can improve performance, especially when large amounts of data are involved:

- Type mismatches. A UK employees database stores date of hire as an integer number; the U.S. employees database stores hire dates in a datetime format. You can convert the integer values to datetime, but then searching on hire date would require fetching every record in the database and sorting at the middleware layer. In this situation, you could use inverse functions to take advantage of each database's hire date index.
- Data Normalization. In order to avoid confusion of UK and U.S. employees, a data service function prepends a country code to the employee IDs of both groups. Again, sorting based on these values will be time consuming since the processing cannot be achieved on the backend without modifying the underlying data. Inverse functions can be used to remove the prepended country code when pushing the operation down to the underlying database.
- Data Transformation. A logical data service has a fullname operation that concatenates firstname and lastname elements. Any attempt to sort by fullname would be penalized by the required retrieval of information on all customers, followed by local processing of the returned results.
- Data Conversion. There are many cases where values need to be converted to their inverse based on established formulas. For example there could be a requirement that the application retrieve customers by date as an xs:dataTime rather than as a numeric. In this way users could supply date information in a variety of formats. Consider an example where the data architect creates the following XQuery function:

    declare function tns:getEmpWithFixedHireDate() as element(ns0:usemp)*{
       for $e in ns1:USEMPLOYEES()
          return
             <emp>
                <eid>{fn:data($e1/ID)}</eid>
                <name>{mkName($e1/LNAME, $e1/FNAME)}</name>
                <hiredate>{int2date($e1/HIRED)}</hiredate>
                <salary>)fn:data($e1/SAL)}</salary>
             </emp>
       }

Given such a function, issuing a filter query on hiredate, on top of this function, results in inefficient execution since every record from the back-end must be retrieved and then processed in the middle tier.

You can use inverse functions in these and other situations to improve performance, especially when processing sizable amounts of data.

#### A Closer Look

Consider the case of a logical data service that has a fullname operation that concatenates firstname and lastname elements. It is clear that performance would be adversely affected when running the fullname operation against large data sets.

The ideal would be to have a function or functions which decomposed fullname into its indexed components, passes the components to the underlying database, gets the results and reconstitutes the returned results to match the requirements of the fullname operation. In fact, that is the basis of inverse functions.

Of course there are no XQuery functions to magically deconstruct a concatenated string. Instead you need to define, as part of your data service development process, custom functions that inverse engineer fullname.

In many cases complimentary inverse functions are needed. For example, `fahrenheitToCentigrade()` and `centigradeToFahenheit()` would be inverses of each other. Complimentary inverse functions are also needed to support fullname.

In addition to creating inverse functions, you also need to identify inverse functions when defining the data service.

### Examining the Inverse Functions Sample

You need to complete the following actions to use inverse functions:

- Create the underlying Java functions
- Create physical data services based on the functions
- Add comparison logic to the data service
- Configure the inverse functions
- Create the data service

#### Creating the Underlying Java Functions

The inverse functions sample includes logic to perform transformations between:

- first/last names and full names
- department names and numbers
- employee IDs and names

The string manipulation logic to manipulate first and last names needed by the inverse function is in the following Java file in the JavaFunctionLib project:

    JavaFunctionLib/JavaFuncs/NameLib.java

This file defines three string manipulation functions.

    package JavaFuncs;
    public class NameLib {
       public static String fullname(String fn, String ln) {
          return (fn == null || ln == null) ? null : (fn + " " + ln);
       }

       public static String firstname(String name) {
          try {
             int sepidx = name.indexOf(' ');
             if (sepidx < 0) return null;
                return name.substring(0, sepidx);
          }
          catch (Exception e) { return null; }
       }

       public static String lastname(String name) {
          try {
             int sepidx = name.indexOf(' ');
             if (sepidx < 0) return null;
                return name.substring(sepidx+1, name.length());
             }
          catch (Exception e) { return null; }
       }

       public static void main(String[] args) {
          String first = "John";
          String last  = "Doe";
          String full  = "John Doe";
          System.out.println(fullname(first, last));
          System.out.println(firstname(full));
          System.out.println(lastname(full));

          System.out.println(firstname(first));
          System.out.println(lastname(first));
       }
    }

Notice that the function fullname() simply concatenates the first and last names. In contrast, the `firstnname()` and `lastname()` functions deconstruct the resulting full name using the required space in the full name string as the marker identifying the separation between first and last names. Or, put another way, the `fullname()` function is the invertible of `firstname()` and `lastname()`.

Similar functions are available in the `DeptLib` and `EmpIdLib` packages supporting transformations between department names and numbers, and employee IDs and names respectively.

#### Creating the Physical Data Services Based on the Functions

After you have compiled the Java functions, you can create a physical data service from the resulting class file. In the sample, physical data services were created using the `NameLib.class`, `DeptLib.class`, and `EmpIdLib`.class files.

Note: | See Create a Physical Data Service from a Java Function for step-by-step instructions for creating a physical data service from a class file.

In the sample, the resulting operations corresponding to the string manipulation logic reside in the `NameFunc.ds` data service, as illustrated by the following:

Figure 8-13 NameFunc.ds Data Service

##### Adding Comparison Logic to the Data Service

As is often the case, some additional programming logic is needed. In the case of the sample, a function, `fullnameEQ()`, compares names and returns a Boolean value indicating whether the names are identical.

    declare function f1:fullnameEQ($full1 as xs:string?, $full2 as       xs:string?) as xs:boolean? {
       (f1:firstname($full1)eq f1:firstname($full2)) and    (f1:lastname($full1) eq f1:lastname($full2))
    };

Note: | You can define additional functions for specific conditions, such as "is greater-than" or "is-less-than." Later, when configuring the inverse functions, you can create associations for these conditionals enabling the the XQuery engine to substitute the custom logic for a simple conditional.

#### Configuring the Inverse Functions

You need to configure the inverse functions to perform the reverse transformation of the data.

In this particular case, this means that you need to identify an inverse function for each parameter in the `fullname()` function.

Note: | Inverse functions can only be defined when the input and output function parameters are atomic types.

To association the parameters of a function with inverse functions:

1. Double-click on the data service in Project Explorer. For example, double-click NameFuncs.ds in the sample.
2. Right-click on the function with which you want to associate inverse functions, and choose Configure Inverse Function. In the sample, right-click the fullname operation and choose Configure Inverse Function.
Figure 8-14 Selecting Configure Inverse Function

A dialog appears enabling you to select the inverse functions for each parameter.

3. Choose the corresponding inverse functions for each parameter using the drop-down lists, and click Next.
Figure 8-15 Selecting Configure Inverse Function

4. Specify the equivalent transforms.

#### Associating Custom Conditional Logic with Functions

After you have associated inverse functions with the correct parameters, you may want to associate custom conditional logic with the functions. You do this by substituting a custom function for such generic conditions as eq (is equal to) and gt (is greater than). The following table lists conditional operations available for such transformations.

Table 8-1 Conditional Operations
Conditional Operation | Definition
--- | ---
gt | string-greater-than
ne | string-not-equal
lt | string-less-than
ge | string-greater-than-or-equal-to
eq | string-equal
le | string-less-than-or-equal-to

Associating a particular conditional (such as "is greater-than") with a transformational function allows the XQuery engine to substitute the custom logic for a simple conditional. As is always the case with Oracle Data Service Integrator, the original basis of the function does not matter. It could be created in your data service, or externally in a Java or other routine. In this example the transformational function, fullnameEQ, is in the Java-based physical data service.

Figure 8-16 Defining the Equivalent Transforms

#### Creating the Data Service

The final step is to build the data service that contains the operations to create, read, update, and delete the data. In the sample dataspace, this data service, `Employee.ds`, includes operations such as `createEmployee`, `getAll`, `updateEmployee`, and `deleteEmployee`. The data service also includes operations such as `getByDeptName`, `getByEmpName`, and `getByEmpNo`.

The following shows the overview of the `Employee.ds` data service.

Figure 8-17 Employee.ds Data Service

The data service uses XML types associated with the `Employee.xsd` schema. This schema could have been created through the XQuery Editor, through the Oracle Data Service Integrator schema editor, or through a third-party editing tool.

The `getAll()` operation returns all employee records, as shown in the following listing:

    declare function ns1:getAll() as element(ns1:Employee)* {
       for $EMP2 in emp2:EMP2()
       return
          <tns:Employee>
             <EmpNo>{emp1:empnum($EMP2/EmpId)}</EmpNo>
             <MgrName?>{fn:data($EMP2/MgrName)}</MgrName>
             <Dept?>{dep:deptname($EMP2/Dept)}</Dept>
          </tns:Employee>
    };

Examining the query plan for the `getAll()` operation, as shown in the following, you can see that predicates are being pushed despite data transformations, because of the use of inverse functions.

Figure 8-18 Query Plan for the getAll Operation

The case is the same for the other read methods `getByDeptName()`, `getByEmpName()`, and `getByEmpNo()`. Examining the corresponding query plans, you can see that predicates are being pushed regardless of the specific transformations because of the corresponding inverse functions.

Note: | The `getByEmpName()` operation illustrates a typical case where the transformation involves performing a concatenation and the inverse function reverses the operation. In this case, N values are merged into 1 or vice versa. The `getByDeptName()` and `getByEmpNo()` operations are both 1:1 examples, transforming between numeric and string values.

### How To Set Up the Inverse Functions Sample

This section describes how to import and configure the Oracle Data Service Integrator inverse functions sample dataspace project.

#### Requirements

You can install and work with the inverse function sample on any system with Oracle Data Service Integrator 10gR3 (Oracle WebLogic Server 10gR3) installed.

The inverse function sample is available as a ZIP file from:

    http://edocs.bea.com/aldsp/docs30/code/InverseFunctions.zip

It is recommended that the ZIP file be extracted into an Oracle Data Service Integrator directory such as:

    <ALDSP_HOME>/user_projects/workspaces/default/InverseFunctionSample

#### Importing the Dataspace Project

The inverse functions sample consists of two projects:

- The inverse functions sample dataspace
- A Java project that defines the functions used for transforming the data

To import the dataspace project:

1. Launch Workshop for WebLogic.
2. Right-click in the Project Explorer and choose Import > Import.
Figure 8-19 Import Dialog

3. Select General > Existing Projects into Workspace, and click Next.
Figure 8-20 Import Menu

4. Click the Select archive file button, and click Browse.
5. Navigate to the InverseFunctions.zip file, select the file, and click Open.
Figure 8-21 Importing the Projects

6. Click Finish. Workshop for WebLogic imports two projects: `InverseFnHowTo` and `JavaFunctionLib`.
Figure 8-22 Inverse Function Projects

#### Assigning a Targeted Runtime

Before examining the inverse functions sample, you need to start an Oracle Data Service Integrator-enabled server and assigned a targeted runtime server to the project.

To assign a targeted runtime:

1. Start an Oracle Data Service Integrator-enabled server.
2. In the Project Explorer, right-click the `InverseFnHowTo` project and choose Properties.
Figure 8-23 Choosing Properties

3. Click Targeted Properties. The list of available servers appears.
Figure 8-24 Selecting the Targeted Runtime

4. Select a server and click OK. Workshop for WebLogic assigns a runtime server to the project.

### Exploring the Inverse Functions Sample

The inverse functions sample consists of two projects.

Table 8-2 Inverse Function Sample Projects
Project | Description
--- | ---
InverseFnHowTo | The inverse functions sample dataspace, including the top-level data service, a relational physical data service, physical data services corresponding to Java transformation functions, and utility data services.
JavaFunctionLib | A Java project that defines the functions used for transforming the da

#### Exploring the Projects

This section describes the principal entities within the two projects that comprise the inverse functions sample.

##### Exploring the InverseFnHowTo Dataspace Project

The following table describes the data services defined in the `InverseFnHowTo` dataspace project:

Table 8-3 Data Services in the InverseFnHowTo Dataspace
Data Service | Description
--- | ---
Employee | The top-level data service for the project. Examining the query plans for the read methods in the data service shows that predicates are pushed despite data transformations because of inverse functions.
EMP2 | The physical data service that accesses the data in the underlying relational database.
DeptFunc EmpIdFuncs NameFuncs | The physical data services corresponding to the Java transformation functions in the JavaFuncLib project.
EMP2Util | Contains functions useful for manipulating and fixing the sample data through the test view.

##### Exploring the JavaFunctionLib Project

The following table describes the data services defined in the `JavaFunctionLib` project:

Table 8-4 Data Services in the JavaFunctionLib Project
Class | Method | Description
--- | --- | ---
DeptLib | deptname() | Converts a department number to a department name.
deptno() | Converts a department name to a department number.
EmpIdLib | empid() | Converts an employee number to a string of the following format: EMPid_number.
empnum() | Converts a string of format EMPid_number to an employee id.
NameLib | firstname() | Extracts the first name from a string (containing a person's full name).
lastname() | Extracts the last name from a string (containing a person's full name).
fullname() | Concatenates the first and last name to form a full name.