Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Some of the following examples on this page may often refer to FIQL due to the fact FIQL has been supported for a long time, but the same examples will work with OData $filter expressions. For example, replace the "_s=name==CXF" query with "$filter=name eq CXF".

Please use a "search.query.parameter.name" contextual property to indicate to the runtime that a $filter query option need to be checked for the query expression and a "search.parser" property to point to the instance of org.apache.cxf.jaxrs.ext.search.odata.ODataParser, as shown in this test, see the startServers function.

When to use advanced queries.

Consider a typical query expression such as "a=avalue&c=cvalue". This can mean either "find all resources with 'a' and 'c' properties equal to 'avalue' and 'cvalue'" or "find all resources with 'a' or 'c' properties equal to 'avalue' and 'cvalue'". It is application specific on whether it is "and" or "or" as far as the combination of multiple query properties is concerned.

It is also to capture conditional expressions with the custom language, example, "find all resource with 'a' property less than 123" when a number of properties is large or the entities which can be searched are created dynamically.

Use FIQL or OData for capturing simple or medium complexity queries, typically in cases where a set of properties that a user can specify is well-known. Example, a book store resource will let users search books given a number of useful properties(those of Book and/or Library a given book is available in, etc).

Furthermore, consider using FIQL/OData and SearchConditionVisitor for the purpose of generalizing the search code, when the number of properties and entities is large, dynamic, etc.

Dependencies and Configuration

The following dependency is required starting from CXF 2.6.0And here is also an XML Spring configuration example (using SearchBean in this specific case):

Code Block
xml
xml
 <cxf:bus>
  <cxf:properties><dependency>
     <entry key="search.query.parameter.name" value="$filter" />
<groupId>org.apache.cxf</groupId>
     <entry key="search.parser"> <artifactId>cxf-rt-rs-extension-search</artifactId>
      <bean class="org.apache.cxf.jaxrs.ext.search.odata.ODataParser"<version>2.6.0</version>
   </dependency>

   <!-- If working with OData -->
   <!--
      <constructor-arg value="#{ T(org.apache.cxf.jaxrs.ext.search.SearchBean) }" />
      </bean>
    </entry>
  </cxf:properties>
</cxf:bus>
 

 

Also note that Apache Olingo offers its own visitor model which can be used to work with JPA2, etc.

When to use advanced queries.

Consider a typical query expression such as "a=avalue&c=cvalue". This can mean either "find all resources with 'a' and 'c' properties equal to 'avalue' and 'cvalue'" or "find all resources with 'a' or 'c' properties equal to 'avalue' and 'cvalue'". It is application specific on whether it is "and" or "or" as far as the combination of multiple query properties is concerned.

It is also to capture conditional expressions with the custom language, example, "find all resource with 'a' property less than 123" when a number of properties is large or the entities which can be searched are created dynamically.

Use FIQL or OData for capturing simple or medium complexity queries, typically in cases where a set of properties that a user can specify is well-known. Example, a book store resource will let users search books given a number of useful properties(those of Book and/or Library a given book is available in, etc).

Furthermore, consider using FIQL/OData and SearchConditionVisitor for the purpose of generalizing the search code, when the number of properties and entities is large, dynamic, etc.

Dependencies and Configuration

The following dependency is required starting from CXF 2.6.0:

...

   <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-extension-search</artifactId>
      <version>2.6.0</version>
   </dependency>

   <!-- If working with OData -->
   <!--
       <dependency>
            <groupId>org.apache.olingo</groupId>
            <artifactId>olingo-odata2-core-incubating</artifactId>
            <version>1.1.0</version> 
        </dependency>
   -->
 
 <dependency>
            <groupId>org.apache.olingo</groupId>
            <artifactId>olingo-odata2-core-incubating</artifactId>
            <version>1.1.0</version> 
        </dependency>
   -->
 

Additionally, starting from CXF 2.6.0, SearchContextProvider needs to be registered as jaxrs:provider.

Working with the queries

SearchContext needs be injected into an application code and used to retrieve a SearchCondition representing the current FIQL/OData query. This SearchCondition can be used in a number of ways for finding the matching data.

In this section we assume that the data to be matched are already available in memory. The follow-up section on converting the queries will show how the queries can be converted to some other query language typed or text expression.

So, suppose a list or map of Book instances is available. Here is one possible approach:

Code Block
java
java
@Path("books")
public class Books {

private Map<Long, Book> books;
@Context
private SearchContext context;

 @GET
 public List<Book> getBook() {

   SearchCondition<Book> sc = searchContext.getCondition(Book.class);
   // SearchCondition#isMet method can also be used to build a list of matching beans

   // iterate over all the values in the books map and return a collection of matching beans
   List<Book> found = sc.findAll(books.values());
   return found;
 }
}

Note that a searchContext.getCondition(Book.class) call may return an arbitrary complex SearchCondition, it can be a simple primitive
expression or a more complex, composite one.

Capturing the queries

For the query expression to be captured, a bean like Book.class is instantiated and has all the search properties injected into it. A complex composite expression will be 'injected' into a number of Book instances - something that may have to be optimized.

Note that by default, a bean such as Book class needs to have a matching property per every property name found in the FIQL expression, for example, given a 'name==b;id==123' expression, the Book class would need to have 'name' and 'id' properties available. The reason for this strict mode being enabled by default is that ignoring a property which can not be captured may lead to a false or unexpected match, for example, if Book 'name' property has been renamed to 'title' then ignoring the 'name' property will lead to a wider match. Thus, if the property does not exist, org.apache.cxf.jaxrs.ext.search.PropertyNotFoundException will be thrown; capturing it can let returning an empty response or retry with the more lax mode, see the next paragraph.

When a more lax parsing of FIQL expressions is expected, for example, where the primitive expressions are joined by "OR", using SearchBean (see one of the next subsections) or setting a contextual property "search.lax.property.match" will help. The former option is better when you need to know the list of all the properties which have been used in the expression, even those which will not be possible to use for the actual search; the latter option will simply have the unrecognized properties ignored.

Note that a "search.decode.values" property can be used to have the 'reserved' characters such as FIQL ',' or ';' characters passed as percent-encoded characters as part of the search property values.

Mapping of query properties to bean properties

As noted above, when a 'typed' bean such as Book.class is used to capture the expressions, a property found in the query expression that can not be mapped to a specific Book property will lead to an exception being reported or it can be optionally ignored. In the reality, there is a number of reasons why the direct match between properties found in query expressions and in capturing beans may not be ideal:

  • Capturing beans may evolve independently of the actual queries; for example, a working query such as "name==b" will break if a Book 'name' gets renamed to 'title' which will make it difficult to have the queries bookmarked.
  • Direct match will simply not work for cases where an actual bean property does not belong to the capturing bean itself but to one of its child properties; for example, a JPA2 Book entity may have an OwnerInfo bean with Name bean property which does contain a primitive 'name' property.

The preferred approach, when working with typed beans, is to register a bean properties map, using a "search.bean.property.map" contextual property or directly with SearchContext. For example, given

Code Block
java
java
public class Book {

    private int id;
    private OwnerInfo ownerinfo;
    //setters and getters omitted for brewity
}

@Embeddable
public class OwnerInfo {

    private Address address;
    private Name name;
    //setters and getters omitted for brewity
}

@Embeddable
public class Name {

    private String name;
    //setters and getters omitted for brewity
}

and the following map:

Code Block
xml
xml
<map>
 <!-- 'oname' is alias for the actual nested bean property -->
 <entry key="oname" value="ownerinfo.name.name"/>
</map>

will let users type and bookmark queries (and without seeing them producing unexpected results) like this one:

Code Block
java
java
//Find all the books owned by Fred with id greater than 100
/books?_s=id=gt=100;oname=Fred

Note, a property name such as "ownerinfo.name.name" uses '.' to let the parser navigate to the actual Name bean which has a 'name' property. This can be optimized in cases where the owner bean is known to have either a constructor or static valueOf() method accepting the 'name' property, for example, given

Code Block
java
java
public class Name {

    private String name;
    public Name() {
    } 
    public Name(String name) {
        this.name = name;
    }

Additionally, starting from CXF 2.6.0, SearchContextProvider needs to be registered as jaxrs:provider.

Working with the queries

SearchContext needs be injected into an application code and used to retrieve a SearchCondition representing the current FIQL/OData query. This SearchCondition can be used in a number of ways for finding the matching data.

In this section we assume that the data to be matched are already available in memory. The follow-up section on converting the queries will show how the queries can be converted to some other query language typed or text expression.

So, suppose a list or map of Book instances is available. Here is one possible approach:

...

@Path("books")
public class Books {

private Map<Long, Book> books;
@Context
private SearchContext context;

 @GET
 public List<Book> getBook() {

   SearchCondition<Book> sc = searchContext.getCondition(Book.class);
   // SearchCondition#isMet method can also be used to build a list of matching beans

   // iterate over all the values in the books map and return a collection of matching beans
   List<Book> found = sc.findAll(books.values());
   return found;
 }
}

Note that a searchContext.getCondition(Book.class) call may return an arbitrary complex SearchCondition, it can be a simple primitive
expression or a more complex, composite one.

Capturing the queries

For the query expression to be captured, a bean like Book.class is instantiated and has all the search properties injected into it. A complex composite expression will be 'injected' into a number of Book instances - something that may have to be optimized.

Note that by default, a bean such as Book class needs to have a matching property per every property name found in the FIQL expression, for example, given a 'name==b;id==123' expression, the Book class would need to have 'name' and 'id' properties available. The reason for this strict mode being enabled by default is that ignoring a property which can not be captured may lead to a false or unexpected match, for example, if Book 'name' property has been renamed to 'title' then ignoring the 'name' property will lead to a wider match. Thus, if the property does not exist, org.apache.cxf.jaxrs.ext.search.PropertyNotFoundException will be thrown; capturing it can let returning an empty response or retry with the more lax mode, see the next paragraph.

When a more lax parsing of FIQL expressions is expected, for example, where the primitive expressions are joined by "OR", using SearchBean (see one of the next subsections) or setting a contextual property "search.lax.property.match" will help. The former option is better when you need to know the list of all the properties which have been used in the expression, even those which will not be possible to use for the actual search; the latter option will simply have the unrecognized properties ignored.

Note that a "search.decode.values" property can be used to have the 'reserved' characters such as FIQL ',' or ';' characters passed as percent-encoded characters as part of the search property values.

Mapping of query properties to bean properties

As noted above, when a 'typed' bean such as Book.class is used to capture the expressions, a property found in the query expression that can not be mapped to a specific Book property will lead to an exception being reported or it can be optionally ignored. In the reality, there is a number of reasons why the direct match between properties found in query expressions and in capturing beans may not be ideal:

  • Capturing beans may evolve independently of the actual queries; for example, a working query such as "name==b" will break if a Book 'name' gets renamed to 'title' which will make it difficult to have the queries bookmarked.
  • Direct match will simply not work for cases where an actual bean property does not belong to the capturing bean itself but to one of its child properties; for example, a JPA2 Book entity may have an OwnerInfo bean with Name bean property which does contain a primitive 'name' property.

The preferred approach, when working with typed beans, is to register a bean properties map, using a "search.bean.property.map" contextual property or directly with SearchContext. For example, given

Code Block
javajava
public class Book {

    private int id;
    private OwnerInfo ownerinfo;
    //setters and getters omitted for brewity
}

@Embeddable
public class OwnerInfo {

    private Address address;
    private Name name;
    //setters and getters omitted for brewity
}

@Embeddable
public class Name {

    private String name;
    //setters and getters omitted for brewity
}

the mapping between "oname" and "ownerinfo.name" will work too.

You can also have many to one mappings, for exampleand the following map:

Code Block
xml
xml
<map>
 <!-- 'oname' and 'owner' isare aliasaliases for the actual nested 'ownerinfo.name.name' bean property -->
 <entry key="oname" value="ownerinfo.name.name"/>
</map>

will let users type and bookmark queries (and without seeing them producing unexpected results) like this one:

...

//Find all the books owned by Fred with id greater than 100
/books?_s=id=gt=100;oname=Fred

...

 <entry key="owner" value="ownerinfo.name.name"

...

public class Name {

    private String name;
    public Name() {
    } 
    public Name(String name) {
        this.name = name;
    }
    //setters and getters omitted for brewity
}

the mapping between "oname" and "ownerinfo.name" will work too.

You can also have many to one mappings, for example

...

<map>
 <!-- 'oname' and 'owner' are aliases for the 'ownerinfo.name.name' bean property -->
 <entry key="oname" value="ownerinfo.name.name"/>
 <entry key="owner" value="ownerinfo.name.name"/>
</map>
/>
</map>

Parser properties

The parser properties are the ones which tell the parser how to treat the conversion of Date values and the unrecognized search property names.

As explained above, "search.lax.property.match" can be used to tell the parser that it should ignore the search property names which have no corresponding bean properties.

"search.date.format" and "search.timezone.support" tell the parser how to convert the date values, see "Using dates in queries" section.

More properties may be supported in the future.

All of these properties can be set as endpoint contextual properties or directly with SearchContext.

Mapping of query properties to column/field names

When converting FIQL queries to SQL or other untyped query language expressions, as well as when using Lucene converter, it can be useful to be able to map between an actual query parameter and the column or field name. All FIQL converters shipped with CXF have constructors accepting a map for mapping the queries to columns/fields. See the next "SearchBean" section for one example.

Note this property is not the same as the one described in the "Mapping of query properties to bean properties" section. The latter (the one described in the previous section) is required for getting FIQL queries captured into typed, domain specific beans like Book, and it can be sufficient for JPA2 which also has annotations like @Column.

SearchBean

org.apache.cxf.jaxrs.ext.search.SearchBean is a utility bean class which can simplify analyzing the captured FIQL expressions and converting them to the other language expressions, in cases where having to update the bean class such as Book.class with all the properties that may need to be supported is not practical or the properties need to be managed manually. For example:

Code Block
java
java
// ?_s="level=gt=10"
SearchCondition<SearchBean> sc = searchContext.getCondition(SearchBean.class);

Map\<, String\> fieldMap = new HashMap\<String, String\>();
fieldMap.put("level", "LEVEL_COLUMN");

SQLPrinterVisitor<SearchBean> visitor = new SQLPrinterVisitor<SearchBean>(fieldMap, "table", "LEVEL_COLUMN");
sc.accept(visitor);
assertEquals("SELECT LEVEL_COLUMN FROM table 
   

Parser properties

The parser properties are the ones which tell the parser how to treat the conversion of Date values and the unrecognized search property names.

As explained above, "search.lax.property.match" can be used to tell the parser that it should ignore the search property names which have no corresponding bean properties.

"search.date.format" and "search.timezone.support" tell the parser how to convert the date values, see "Using dates in queries" section.

More properties may be supported in the future.

All of these properties can be set as endpoint contextual properties or directly with SearchContext.

Mapping of query properties to column/field names

When converting FIQL queries to SQL or other untyped query language expressions, as well as when using Lucene converter, it can be useful to be able to map between an actual query parameter and the column or field name. All FIQL converters shipped with CXF have constructors accepting a map for mapping the queries to columns/fields. See the next "SearchBean" section for one example.

Note this property is not the same as the one described in the "Mapping of query properties to bean properties" section. The latter (the one described in the previous section) is required for getting FIQL queries captured into typed, domain specific beans like Book, and it can be sufficient for JPA2 which also has annotations like @Column.

SearchBean

org.apache.cxf.jaxrs.ext.search.SearchBean is a utility bean class which can simplify analyzing the captured FIQL expressions and converting them to the other language expressions, in cases where having to update the bean class such as Book.class with all the properties that may need to be supported is not practical or the properties need to be managed manually. For example:

Code Block
javajava
// ?_s="level=gt=10"
SearchCondition<SearchBean> sc = searchContext.getCondition(SearchBean.class);

Map\<, String\> fieldMap = new HashMap\<String, String\>();
fieldMap.put("level", "LEVEL_COLUMN");

SQLPrinterVisitor<SearchBean> visitor = new SQLPrinterVisitor<SearchBean>(fieldMap, "table", "LEVEL_COLUMN");
sc.accept(visitor);
assertEquals("SELECT LEVEL_COLUMN FROM table 
              WHERE LEVEL_COLUMN > '10'",
              visitor.getResult());

...

Code Block
java
java
public class Chapter {
   private int id;
   private Book book;

   @OneToOne(mappedBy="book")
   public Book getBook() {}
}

@Path("search")
public class BooksResource {
   @Context
   private SearchContext context;

   //GET /chapters(bookId=gt=300,id=lt=5)
   @GET
   @Path("chapters({search})") 
   public List<Chapter> findSelectedChapters(@PathParam("search") String chapterExpression) {
       
       SearchCondition<Chapter> chapterCondition = context.getCondition(chapterExpression, Chapter.class);
   
       JPATypedQuery<Chapter> visitor = new JPATypedQueryVisitor<Chapter>(entityManager, Chapter.class);
       chapterCondition.visit(visitor);
       TypedQuery<Chapter> typedQuery = visitor.getQuery();
       return typedQuery.getResultList();
   }

}

Note this code assumes that "bookId" is mapped to "Book.id" property with the help of the contextual "search.bean.property.map" property as explained earlier.

Validation

First option is to have a bean capturing specific property values do a domain specific validation. For example, a Book.class may have its setName(String name) method validating the name value.
Another option is to inject a custom validator into a visitor which is used to build the untyped or typed query.

Finally, avoid letting users to use properties whose values which can not be well validated in the application code. Using a typed capturing bean like Book.class offers a perfect option to limit a number of supported properties to the ones known to be related to Books.

Bean Validation 1.1 can also be used.

Building the queries

FIQL

CXF 2.4.0 introduces SearchConditionBuilder which makes it simpler to build FIQL queries. SearchConditionBuilder is an abstract class that returns a FIQL builder by default:

)") 
   public List<Chapter> findSelectedChapters(@PathParam("search") String chapterExpression) {
       
       SearchCondition<Chapter> chapterCondition = context.getCondition(chapterExpression, Chapter.class);
   
       JPATypedQuery<Chapter> visitor = new JPATypedQueryVisitor<Chapter>(entityManager, Chapter.class);
       chapterCondition.visit(visitor);
       TypedQuery<Chapter> typedQuery = visitor.getQuery();
       return typedQuery.getResultList();
   }

}

Note this code assumes that "bookId" is mapped to "Book.id" property with the help of the contextual "search.bean.property.map" property as explained earlier.

Validation

First option is to have a bean capturing specific property values do a domain specific validation. For example, a Book.class may have its setName(String name) method validating the name value.
Another option is to inject a custom validator into a visitor which is used to build the untyped or typed query.

Finally, avoid letting users to use properties whose values which can not be well validated in the application code. Using a typed capturing bean like Book.class offers a perfect option to limit a number of supported properties to the ones known to be related to Books.

Bean Validation 1.1 can also be used.

Building the queries

FIQL

CXF 2.4.0 introduces SearchConditionBuilder which makes it simpler to build FIQL queries. SearchConditionBuilder is an abstract class that returns a FIQL builder by default:

Code Block
java
java
SearchConditionBuilder b = SearchConditionBuilder.instance();
String fiqlQuery = b.is("id").greaterThan(123).query();

WebClient wc = WebClient.create("http://books.com/search");
wc.query("_s", fiqlQuery);
// find all the books with id greater than 123 
Collection books = wc.getCollection(Book.class);

Here is an example of building more complex queries:

Code Block
java
java
// OR condition
String ret = b.is("foo").greaterThan(20).or().is("foo").lessThan(10).query();
assertEquals("foo=gt=20,foo=lt=10", ret);

// AND condition
String ret = b.is("foo").greaterThan(20).and().is("bar").equalTo("plonk").query();
assertEquals("foo=gt=20;bar==plonk", ret);

// Complex condition
String ret
Code Block
javajava
SearchConditionBuilder b = SearchConditionBuilder.instance();
String fiqlQuery = b.is("idfoo").greaterThanequalTo(123.4).queryor();

WebClient wc = WebClient.create("http://books.com/search");
wc.query("_s", fiqlQuery);
// find all the books with id greater than 123 
Collection books = wc.getCollection(Book.class);
.and(
            b.is("bar").equalTo("asadf*"), 
            b.is("baz").lessThan(20)).query();
assertEquals("foo==123.4,(bar==asadf*;baz=lt=20.0)", ret);

Note, starting from CXF 2.7.1 the following can be used to make connecting multiple primitive expressions simplerHere is an example of building more complex queries:

Code Block
java
java
// OR conditionAND condition, '.and("bar")' is a shortcut for "and().is("bar")", similar shortcut is supported for 'or'
String ret = b.is("foo").greaterThan(20).orand("bar").isequalTo("fooplonk").lessThan(10).query();
assertEquals("foo=gt=20,foo;bar=lt=10plonk", ret);

More updates to the builder API are available on the trunk:

Code Block
java
java


// ANDOR condition
String ret = b.is("foo").greaterThanequalTo(20).andor().is("barfoo").equalTo("plonk"10).query();
assertEquals("foo=gt=20;bar,foo==plonk10", ret);

// Complex conditionSame query, shorter expression
String ret = b.is("foo").equalTo(123.420, 10).orquery().and(
           ;
assertEquals("foo==20,foo==10", ret);

and

Code Block
java
java
// Connecting composite or() and and() expressions will add "()" implicitly:
String ret = b.is("barfoo").equalTo("asadf*"), 
            b.is("baz20, 10).and("bar").lessThan(20)10).query();
assertEquals("(foo==123.4,(bar20,foo==asadf*10);bazbar=lt=20.0)10", ret);

Note, starting from CXF 2.7.1 the following can be used to make connecting multiple primitive expressions simpler:

Code Block
javajava
// AND condition, '.and("bar")' is a shortcut for "and().is("bar")", similar shortcut is supported for 'or'
String ret = b.is("foo").greaterThan(20).and

// wrap() method can be used to wrap explicitly:

String ret = b.is("foo").equalTo(10).and("bar").lessThan(10).wrap().or("bar").equalTogreaterThan("plonk"25).query();
assertEquals("(foo=gt=20;bar=lt==plonk10),bar=gt=25", ret);


 

Using dates in queries

By default, the date values have to have the following format: "yyyy-MM-dd", for example:

Code Block
java
java
?_search=date=le=2010-03-11

A custom date format can be supported. Use "search.date-format" contextual property, example, "search.date-format"="yyyy-MM-dd'T'HH:mm:ss" will let users typeMore updates to the builder API are available on the trunk:

Code Block
java
java
// OR condition
String ret = b.is("foo").equalTo(20).or().is("foo").equalTo(10).query();
assertEquals("foo==20,foo==10", ret);

// Same query, shorter expression
String ret = b.is("foo").equalTo(20, 10).query();
assertEquals("foo==20,foo==10", ret);

and

?_search=time=le=2010-03-11T18:00:00

If needed, "search.timezone.support" can be enabled to get the timezones supported too.

At the moment, for custom date formats be recognized by SearchConditionBuilder, FIQLSearchConditionBuilder has to be created explicitly:

Code Block
java
java
// Connecting composite or() and and() expressions will add "()" implicitly:
String ret = b.is("foo").equalTo(20, 10).and("bar").lessThan(10).query();
assertEquals("(foo==20,foo==10);bar=lt=10", ret);

// wrap() method can be used to wrap explicitly:

String ret = b.is("foo").equalTo(10).and("bar").lessThan(10).wrap().or("bar").greaterThan(25Map<String, String> props = new HashMap<String, String>();
props.put("search.date-format", "yyyy-MM-dd'T'HH:mm:ss");
props.put("search.timezone.support", "false");

Date d = df.parse("2011-03-01 12:34:00");
        
FiqlSearchConditionBuilder bCustom = new FiqlSearchConditionBuilder(props);
        
String ret = bCustom.is("foo").equalTo(d).query();
assertEquals("(foo==20;bar=lt=10),bar=gt=252011-03-01T12:34:00", ret);


OData

 

Please work with Apache Olingo to produce OData queries from the code.

Using dates in queries

By default, the date values have to have the following format: "yyyy-MM-dd", for example:

...

?_search=date=le=2010-03-11

A custom date format can be supported. Use "search.date-format" contextual property, example, "search.date-format"="yyyy-MM-dd'T'HH:mm:ss" will let users type:

...

?_search=time=le=2010-03-11T18:00:00

Alternative query languages

Custom org.apache.cxf.jaxrs.ext.search.SearchConditionParser implementations can be registered as a "search.parser" contextual property starting from CXF 3.0.0-milestone2.

OData

 

Please use a "search.query.parameter.name" contextual property to indicate to the runtime that an OData '$filter' query option needs to be checked for the query expression and a "search.parser" property to point to the instance of org.apache.cxf.jaxrs.ext.search.odata.ODataParser, as shown in this test, see the startServers function.

And here is also an XML Spring configuration example (using SearchBean in this specific case):

Code Block
xml
xml
 <cxf:bus>
  <cxf:properties>
    <entry key="search.query.parameter.name" value="$filter" />
    <entry key="search.parser">
      <bean class="org.apache.cxf.jaxrs.ext.search.odata.ODataParser">

If needed, "search.timezone.support" can be enabled to get the timezones supported too.

At the moment, for custom date formats be recognized by SearchConditionBuilder, FIQLSearchConditionBuilder has to be created explicitly:

Code Block
javajava
Map<String, String> props = new HashMap<String, String>();
props.put("search.date-format", "yyyy-MM-dd'T'HH:mm:ss");
props.put("search.timezone.support", "false");

Date d = df.parse("2011-03-01 12:34:00");
        
FiqlSearchConditionBuilder bCustom<constructor-arg value= new FiqlSearchConditionBuilder(props);
    "#{ T(org.apache.cxf.jaxrs.ext.search.SearchBean) }" />
      </bean>
    </entry>
String ret = bCustom.is("foo").equalTo(d).query();
assertEquals("foo==2011-03-01T12:34:00", ret);

Alternative query languages

 </cxf:properties>
</cxf:bus>
 

 

Also note that Apache Olingo offers its own visitor model which can be used to work with JPA2, etcCustom org.apache.cxf.jaxrs.ext.search.SearchConditionParser implementations can be registered as a "search.parser" contextual property starting from CXF 3.0.0-milestone2.

Content Extraction

Starting from CXF 3.0.2, the content extraction support has been added in order to complement the search capabilites with text extraction from various document formats (PDF, ODF, DOC,TXT,RTF,...). It is based on Apache Tika and is available in two shapes: raw content extraction (TikaContentExtractor) and Lucene document content extraction (TikaLuceneContentExtractor).

...