An Advanced Custom Search Form For ADF
Monday, 26. February 2007, 13:17:51
I recently read a question on the JDeveloper forum about an advanced search. This intrigued me, and so I set about implementing this.
Required: I used the HR schema for my sample, so you need to have a connection to HR from JDeveloper to run it. The advanced search itself, however, is not tied to any particular schema and has been made to be as generic as possible.
Firsly, let me show what an example search form will look like using this method:
The first drop down box has been populated with field labels, based on a managed property that you define as a list of field names. The second drop down box has two options: Is and Is Not, and the third drop down box has four different operators: Like, Equal To, Greater Than and Less Than. To perform a search on one field you would select the field label from the first list, select whether you want the field to match or not match the parameters from the second list, select the relevant operator from the third list and enter a value in the input box. Once the search button is clicked, the associated table will update to show the results.
The other two buttons are there to add and remove a search criteria. If you click the add criteria button, another row will be added with a drop down box before it containing two items: And and Or. This allows you to select how the search method will treat the two criteria (whether the records must match both, or just one).
Here's an example of the search form with two criteria and the results shown in the table below:

Most of the work of this search form is done by a backing bean named SearchCriteriaModelAdapter. There are just a few things to set up in the faces-config.xml and on the page itself, to make it work. Before I get into the code that lies behind all this, I'll show you what set up is actually needed.
In the faces-config.xml file, you need to add a session-scoped managed bean that has the class SearchCriteriaModelAdpater. You then need to add two managed properties: searchIteratorName that contains the name of the iterator binding (in the case of my sample EmployeesSearchIterator) and searchAttrNames that contains a list of attribute names (e.g. FirstName and LastName). That's it for the bean. On the page you need to do a bit more.
Above is a code sample showing what is needed to set up the search form. The managed bean is called searchCriteria in this example. There is a forEach component that is mapped to the model property of the bean. This property is a list of criteria, each of which contain values defining the search attribute name, the operator in use, the attribute value, whether it should be an and conjuction or an or conjunction and whether it should match the condition or not. Each of these properties are mapped to the values of four selectOneChoice components and an inputText component. The items in the attribute and operator selectOneChoice components are mapped to another two attributes from the bean (attrList and operatorList respectively). The first of these is constructed from the list of attribute names defined in the faces-config.xml, while the other is hard-coded in the backing bean class (this could be extended to include other operators). The three commandButton components each have a corresponding action listener method in the backing bean.
Now I'll talk about the code in the backing bean class. The SearchCriteriaModelAdapter class has two attributes that are set in the faces-config (searchIteratorName and searchAttrNames) and three others accessible from EL (model, attrList and operatorList). The operatorList is the most simple to explain - it is a list containing a few SelectItems corresponding to the different operators available. This maps directly to the selectItems component on the page. The attrList property is constructed from the list of attribute names defined in the searchAttrNames property. The code below shows how the label for each attribute is found and then added to a SelectItem instance.
The getViewObject method uses the searchIteratorName property to find the DCBindingIterator and from that, the ViewObject. ADFUtils is a class that is distributed with the SRDemo sample and it is used here un-altered. Once this method is called once, the attrList is set up for the whole session.
The model is the main property of this class and the code below shows how it is constructed.
At the start it is simply populated with a "blank" criteria. The SearchCriteriaItem class is a simple bean with five properties for storing the attribute name, the operator, the attribute value, whether to use an and conjuction or and or conjunction, and whether to use a not conjunction. A blank criteria in this case defines default values for all attributes other than the attribute value, which really is blank. This means that the top item in each of the drop down boxes will be selected, which is probably what the user wants. You could change this so that you can define which attribute name or which operator to use as the defaults. You could even define a default value to go in the input field.
The methods to add and remove a criteria row are quite simple too.
The removeCriteria method ensures that if the user removes the last criteria, there will always be a blank criteria put in its place.
The updateView method is called when the search button is click and it is the main method in the class.
Using all the values from each of the SearchCriteriaItems in the model, this method constructs a ViewCriteria object and applies it to the view of the specified iterator. The clearCache method is called so that the view and its iterator are refreshed.
Since each SearchCriteriaItem object created has default values given to it, you can make the search form as simple or as complex as you want. For example, you could simply not include drop downs for And/Or or Is/Is Not, since the default values of these are probably what most users will want (it's the default behaviour of a normal search form).

That's all there is to it. I have created a sample application which has two pages - one with the full advanced search form and the other with the simpler form described above. You can download this below.
CustomSearch.zip
Required: I used the HR schema for my sample, so you need to have a connection to HR from JDeveloper to run it. The advanced search itself, however, is not tied to any particular schema and has been made to be as generic as possible.
Firsly, let me show what an example search form will look like using this method:
The first drop down box has been populated with field labels, based on a managed property that you define as a list of field names. The second drop down box has two options: Is and Is Not, and the third drop down box has four different operators: Like, Equal To, Greater Than and Less Than. To perform a search on one field you would select the field label from the first list, select whether you want the field to match or not match the parameters from the second list, select the relevant operator from the third list and enter a value in the input box. Once the search button is clicked, the associated table will update to show the results.
The other two buttons are there to add and remove a search criteria. If you click the add criteria button, another row will be added with a drop down box before it containing two items: And and Or. This allows you to select how the search method will treat the two criteria (whether the records must match both, or just one).
Here's an example of the search form with two criteria and the results shown in the table below:

Most of the work of this search form is done by a backing bean named SearchCriteriaModelAdapter. There are just a few things to set up in the faces-config.xml and on the page itself, to make it work. Before I get into the code that lies behind all this, I'll show you what set up is actually needed.
In the faces-config.xml file, you need to add a session-scoped managed bean that has the class SearchCriteriaModelAdpater. You then need to add two managed properties: searchIteratorName that contains the name of the iterator binding (in the case of my sample EmployeesSearchIterator) and searchAttrNames that contains a list of attribute names (e.g. FirstName and LastName). That's it for the bean. On the page you need to do a bit more.
<af:panelForm partialTriggers="Add Remove">
<af:forEach items="#{searchCriteria.model}" var="criteria"
varStatus="vs">
<af:panelHorizontal halign="center">
<af:objectSpacer width="50" height="10"
rendered="#{vs.count == 1}"/>
<af:selectOneChoice value="#{criteria.andConjunction}"
rendered="#{vs.count > 1}">
<af:selectItem label="And" value="true"/>
<af:selectItem label="Or" value="false"/>
</af:selectOneChoice>
<af:selectOneChoice value="#{criteria.attrName}">
<f:selectItems value="#{searchCriteria.attrList}"/>
</af:selectOneChoice>
<af:selectOneChoice value="#{criteria.notConjunction}">
<af:selectItem label="Is" value="false"/>
<af:selectItem label="Is Not" value="true"/>
</af:selectOneChoice>
<af:selectOneChoice value="#{criteria.operator}">
<f:selectItems value="#{searchCriteria.operatorList}"/>
</af:selectOneChoice>
<af:inputText value="#{criteria.attrValue}"/>
</af:panelHorizontal>
</af:forEach>
<f:facet name="footer">
<h:panelGroup>
<af:commandButton text="Add criteria"
actionListener="#{searchCriteria.addCriteria}"
id="Add" partialSubmit="true"/>
<af:commandButton text="Remove criteria"
actionListener="#{searchCriteria.removeCriteria}"
id="Remove" partialSubmit="true"/>
<af:commandButton text="Search"
actionListener="#{searchCriteria.updateView}"
id="Search" partialSubmit="true"/>
</h:panelGroup>
</f:facet>
</af:panelForm>Above is a code sample showing what is needed to set up the search form. The managed bean is called searchCriteria in this example. There is a forEach component that is mapped to the model property of the bean. This property is a list of criteria, each of which contain values defining the search attribute name, the operator in use, the attribute value, whether it should be an and conjuction or an or conjunction and whether it should match the condition or not. Each of these properties are mapped to the values of four selectOneChoice components and an inputText component. The items in the attribute and operator selectOneChoice components are mapped to another two attributes from the bean (attrList and operatorList respectively). The first of these is constructed from the list of attribute names defined in the faces-config.xml, while the other is hard-coded in the backing bean class (this could be extended to include other operators). The three commandButton components each have a corresponding action listener method in the backing bean.
Now I'll talk about the code in the backing bean class. The SearchCriteriaModelAdapter class has two attributes that are set in the faces-config (searchIteratorName and searchAttrNames) and three others accessible from EL (model, attrList and operatorList). The operatorList is the most simple to explain - it is a list containing a few SelectItems corresponding to the different operators available. This maps directly to the selectItems component on the page. The attrList property is constructed from the list of attribute names defined in the searchAttrNames property. The code below shows how the label for each attribute is found and then added to a SelectItem instance.
public List getAttrList()
{
if (attrList == null)
{
attrList = new ArrayList();
ViewObject view = getViewObject();
for (int i = 0; i < searchAttrNames.size(); i++)
{
String searchAttrName = (String) searchAttrNames.get(i);
AttributeDef attrDef = view.findAttributeDef(searchAttrName);
if (attrDef != null)
{
String label =
attrDef.getUIHelper().getLabel(ADFUtils.getDCBindingContainer().getLocaleContext());
SelectItem item = new SelectItem(searchAttrName, label);
attrList.add(item);
}
}
}
return attrList;
}The getViewObject method uses the searchIteratorName property to find the DCBindingIterator and from that, the ViewObject. ADFUtils is a class that is distributed with the SRDemo sample and it is used here un-altered. Once this method is called once, the attrList is set up for the whole session.
The model is the main property of this class and the code below shows how it is constructed.
public List getModel()
{
if (model == null)
{
model = new ArrayList();
model.add(getBlankItem());
}
return model;
}
private SearchCriteriaItem getBlankItem()
{
return new SearchCriteriaItem((String)searchAttrNames.get(0), "like", "", true, false);
}At the start it is simply populated with a "blank" criteria. The SearchCriteriaItem class is a simple bean with five properties for storing the attribute name, the operator, the attribute value, whether to use an and conjuction or and or conjunction, and whether to use a not conjunction. A blank criteria in this case defines default values for all attributes other than the attribute value, which really is blank. This means that the top item in each of the drop down boxes will be selected, which is probably what the user wants. You could change this so that you can define which attribute name or which operator to use as the defaults. You could even define a default value to go in the input field.
The methods to add and remove a criteria row are quite simple too.
public void addCriteria(ActionEvent event)
{
model.add(getBlankItem());
}
public void removeCriteria(ActionEvent event)
{
model.remove(model.size() - 1);
if (model.size() == 0) model.add(getBlankItem());
}The removeCriteria method ensures that if the user removes the last criteria, there will always be a blank criteria put in its place.
The updateView method is called when the search button is click and it is the main method in the class.
public void updateView(ActionEvent event)
{
ViewObject view = getViewObject();
ViewCriteria criteria = view.createViewCriteria();
for (int i = 0; i < model.size(); i++)
{
SearchCriteriaItem item = (SearchCriteriaItem)model.get(i);
if (!item.getAttrValue().equals(""))
{
String attrName = item.getAttrName();
String operator = item.getOperator();
String attrValue = item.getAttrValue();
ViewCriteriaRow row = criteria.createViewCriteriaRow();
row.setAttribute(attrName, operatorMap.get(operator) + " '" + attrValue + "'");
if (item.isAndConjunction())
{
if (item.isNotConjunction()) row.setConjunction(ViewCriteriaRow.VCROW_CONJ_NOT | ViewCriteriaRow.VCROW_CONJ_AND);
else row.setConjunction(ViewCriteriaRow.VCROW_CONJ_AND);
}
else
{
if (item.isNotConjunction()) row.setConjunction(ViewCriteriaRow.VCROW_CONJ_NOT | ViewCriteriaRow.VCROW_CONJ_OR);
}
criteria.addElement(row);
}
}
view.applyViewCriteria(criteria);
view.clearCache();
}Using all the values from each of the SearchCriteriaItems in the model, this method constructs a ViewCriteria object and applies it to the view of the specified iterator. The clearCache method is called so that the view and its iterator are refreshed.
Since each SearchCriteriaItem object created has default values given to it, you can make the search form as simple or as complex as you want. For example, you could simply not include drop downs for And/Or or Is/Is Not, since the default values of these are probably what most users will want (it's the default behaviour of a normal search form).

That's all there is to it. I have created a sample application which has two pages - one with the full advanced search form and the other with the simpler form described above. You can download this below.
CustomSearch.zip
By Tiftif, # 12. March 2007, 16:37:50
Thanks again for this simple and very useful method.
I've adapted it to some needs i had and encountered a little refreshment problem. The page didn't refresh correctly after click on search. However i create the action and its invokeaction, probably i missed something when adapting your files to my needs. I've just add a partial trigger on the table using the ID of the search button, and i've no more problem.
Thanks again!
By Tiftif, # 4. April 2007, 13:18:01
I've a little challenge that could interest you.
I'm using your search function on an editable table displaying the contacts(name, first name,...). Each contact corresponds to one customer (1 customer can have many contacts). In the table i so want to display the customer referenced by each contact. I would like this table stay editable and so display a drop down list of customer to enable changes (I've used Steve Muench example: Drop Down List in Editable Table).
Problem is: contacts have only the ID of its customer, but i would like to use the search function based on its name and not on ID.
Did you have already thinking about similar case? Have you got tips to modify your function in order to obtain wanted behavior?
Thanks again,
TIf
ps: i post the question on the Oracle forums too
By Tiftif, # 5. April 2007, 09:25:06
Thanks for your comments.
If I'm understanding your question correctly, you can acheive the required result without changing anything in the custom search example.
If you open the view object for the contacts, then in the Entity Objects page import the customer entity object and in the Attributes page shuttle across the name attribute from the customer entity (it should automatically shuttle the id too if there is an association between the entities based on the id).
You can now use this attribute in the custom search example.
Hope this helps,
DominionSpy
PS. I have also replied to the forum post.
By dominionspy, # 8. April 2007, 08:12:11
I've followed your advices and complete my view object with including the required fields coming from the table customer. Thank you very much, that works perfectly and that was so simple to perform than i didn't think about.
Thanks again,
Tif
By Tiftif, # 10. April 2007, 08:18:28
it is really a great job and i really enjoyed it.
but i want to know how to change this Advanced "Custom Search Form For ADF" to use it with with ADFBC Swing because i want to use it in a not web application.
thanks
By 7rouz, # 10. May 2007, 12:05:59
I'm afraid I have absolutely no experience with ADF Swing so I wouldn't even know where to start. You might try posting on the OTN JDeveloper forum and nudge Steve Muench or Frank Nimphius to help you out.
Sorry I couldn't be more help,
DominionSpy
By dominionspy, # 11. May 2007, 13:20:54
Have you got any info now on how to do it with ADFBC Swing? Would greatly appreciate some help too.
Thanks a lot,
oz_penguin
By oz_penguin, # 4. June 2007, 06:35:58
I have a little suggestion for your wonderful function:
My users are quite "simple", they don't understand why they should put two "%" around the word or the part of word they are looking for. After trying to explain them so many times, i've changed the function:
in bean "SearchCriteriaModelAdapter", in the function "updateView", i've change this line:
row.setAttribute(attrName, operatorMap.get(operator) + " '" + attrValue + "'");
in row.setAttribute(attrName, operatorMap.get(operator) + " '%" + attrValue + "%'");
To go a little further, my users don't understand difference between "name", "Name" and "NAME" and enter all and more in the database. I've protected the main part of the fields, but for some title i've let them use lower and uppercase to write more readable name. Problem is they can't retrieve them, because they forget which letter was in upper or lower. So on the preceding line again, i've changed it to the following:
row.setAttribute(attrName, operatorMap.get(operator) + " upper('%" + attrValue + "%')");
Then add: row.setUpperColumns(true);
Now they can enter some rubbish and the search still works
Hope that can interest you,
Best regards,
Tif
Btw: I'm trying to create a blog on opera too. I would like to write some tutorials on Jdeveloper then traduce them in french coz there is almost nothing on Jdev in french. I will need some help to correct my examples. I'll really enjoy that you have a look on it when i'had post something interesting. I will recall you when i will have post my first complete tutorial.
By Tiftif, # 5. June 2007, 08:00:23
Thanks for your continued interest in this.
These ideas you have detailed are really good and they fit in quite nicely with my thoughts about how to expand the search adapter.
I have been thinking that it would be useful to be able to specify options for individual columns, such as the data type or allowable values. These options could include whether the column is case insensitive or should use wildcards in the way you have mentioned. This would give greater flexibility to the adapter, as you may not want a numeric field to perform a wildcard search, for example.
----
I shall definitely have a look at your tutorials when you post them, although my French is terrible I would still be interested. Feel free to nick one of my blogs for translation if you want.
PS It's a good job you are translating from English into French and not the other way around
By dominionspy, # 8. June 2007, 10:16:18
I will continue to keep a look on your blog. If you can make your function evolving in the way you described, i assume the SRSearch will be quickly gave up to be replace by yours. I will try to think about it.
Thanks for according me to traduce some of your works. I will probably do it for this custom search function at least. Lot of french developers should be interested.
My first tutorial is on a good way. It could be finished in one week. If i proposed to you to have a look on it, it's because i will post it in english first before traducing. That will enable me to have technical advices that probably french developers couldn't give to me (not enough people use Jdev in France).
Thanks again and Good luck,
Best regards,
Tif
By Tiftif, # 12. June 2007, 09:25:30
I've just realized few days ago that the use of the wildcards and the uppercase doesn't let the search function working on the dates anymore. Also i've looking for an alternative and yet i would appreciate to have your mind about what i found. I'm still working only on the "SearchCriteriaModelAdapter" class.
I have added: - Two attibutes of type SimpleDateFormat (i will accept the users type french or english format: yyyy/MM/dd or dd/MM/yyyy)
- A function "attrValueIsDate" returning true or false
- A conditional treatment depending if attrValue is a date or not
The complete file is here:
http://files.myopera.com/Tiftif/files/SearchCriteriaModelAdapter.java
I know you were looking for some approaching functionalities (detecting the type of the attrName in order to use the correponding component on client context), did you make some progress?
Good luck,
Best regards,
Tristan
PS: I was quite busy, but i'm touching the goal for my tutorial, may be in few days?
By Tiftif, # 26. July 2007, 13:50:56
Thanks for very useful sample . i am trying to use your code and it works great until i have added code to dynamically change the page range as documented in (http://www.orablogs.com/fnimphius/archives/001966.html)
i am getting javax.faces.el.EvaluationException: java.lang.IllegalArgumentException when i click any of the command buttons second time . i was wondering if you can help to add Dynamic table range to your sample code
Thank you for your time
Rao
By raods, # 3. August 2007, 19:29:20
i am able to resolve the issue i was having, please ignore my previous request
Thanks
Rao
By raods, # 4. August 2007, 03:02:57
Hi,
I would like to set Date attribute of ViewCriteriaRow to be less (or greater) than proposed value.
I just tried to do the following:
row.setAttribute("MyDateAttr", " <= to_date('01.01.2007', 'dd.mm.yyyy');
but this produces the following (wrong) WHERE clause:
( (TO_CHAR( Angagement.MY_DATE_ATTR, 'yyyy-mm-dd') >= to_date('01.01.2007', 'dd.mm.yyyy')) ) )
- So, how to to this, in order to find just those db records with MY_DATE_ATTR less or greater than proposed value ?
By anonymous user, # 29. October 2007, 11:39:00
Hi Dominion spy,
Is it possible to use ADF Editable table as the result table and add 'Create' functionality on it ?
Thank you,
xtanto
By anonymous user, # 26. November 2007, 11:43:38
Its good example and also much needed for peoples who are all new to the ADF.
If you don't mine, please post same custom search using Toplink and EJB3.0. I am new to the framework. I am fighting for to develop the screen. Please help me how can i do this using Toplink and EJB3.0.
Thanks & Regards
Vimalan Balan
By vimalan28, # 18. March 2008, 07:35:35
This is a fantastic example!
How would you display a more user-friendly message if the user typed in criteria that would cause a SQL exception like an invalid number? I would like to display a different error message instead of details about the exception.
Thanks!
By anonymous user, # 20. May 2008, 12:44:15
Please help me to resolve my issue. Please view my post in forum.
http://forums.oracle.com/forums/message.jspa?messageID=2665116#2665116
Actually i want to pass the session id as another parameter from backing bean to EJB call.
I am calling the below method in the backing bean
POMasterDetailSessionEJB master = null;
master = Util.getPOMasterSessionBean();
searchResult = (List)master.searchPaymentOrder(poNumFrom.toString(),poNumTo.toString(),
poDateFrom.toString(),poDateTo.toString(),
poAmtFrom.toString(),poAmtTo.toString(),
poBudCodeFrom.toString(),poBudCodeTo.toString(),
poCurCode.toString(), <user login id>);
It will call the search method in the EJB.
When i click the search button, the call from bean to EJB is executed and then again the method in EJB is executed again. (Twice)
First call with session id (Value is there) and next time null value is gng for the second, because of second call it throws no record found.
Thanks & Regards
Vimalan balan
By vimalan28, # 26. July 2008, 10:01:33