Accessibility
Brian Szoszorek

Brian Szoszorek

BrianSzoszorek.com

Created:
19 March 2007
User Level:
Intermediate
Products:
Coldfusion

Harnessing the power of Verity, ColdFusion event gateways, and Flex 2 – Part 2: Creating a rich document search interface

In Part 1: Setting up the collection and event gateway, I showed you how to keep your Verity document collections updated automatically using the DirectoryWatcher event gateway. When a document change occurs, you use the DirectoryWatcher to detect this change and update the Verity collection. The only part missing is an intuitive interface for searching these collections.

In Part 2 you will see a simple example of how Flex 2 can enable a richer search interface to interact with ColdFusion and Verity.

This article assumes you are familiar with setting up a basic Flex project and exchanging data between Flex 2 and ColdFusion. To find out more, read Kyle Quevillon's excellent article on Moving data from ColdFusion CFCs to Flex 2 applications.

Requirements

To make the most of this article, you need the following software and files:

ColdFusion MX 7.0.2 Enterprise

Flex Builder 2

Sample files:

Prerequisite knowledge

  • You must create a Verity collection as described in the first part of this tutorial, Part 1: Setting up the collection and event gateway.
  • Basic understanding of ColdFusion
  • Basic knowledge of ColdFusion components (CFCs)
  • Basic knowledge of Flex Builder 2

About the Application

Figure 1 shows the state of the application after two searches have taken place. The first search was on the keywords "ColdFusion" and the second search was on the keywords "Flex 2." Each search creates a new tab with the criteria as the label. Inside each tab is a datagrid displaying the results. This enables the user to tab back and forth between the results. In a standard HTML page, this would not be very easy, and the user would have to use the Back button to return to previous searches if they wanted to go back to a previous result.

The state of the search application after two searches.

Figure 1. The state of the search application after two searches.

Installing the Application

This article assumes you have already read part 1 and have a Verity collection called "AdobeArticles" created.

  1. Download verityflex2.zip and verityflex2_articles_cfc.zip from the Requirements section.
  2. Create a folder called DocumentSearch at C:\DocumentSearch. Extract the contents of verityflex2.zip, and place the src directory to the DocumentSearch folder.
  3. Extract the contents of adobe_articles_cfc.zip, and place veritySearch.cfc and the contents of the adobeArticles folder from the ZIP to c:\inetpub\wwwroot\adobeArticles\ (your web root may vary, but this is the folder you created in part 1 of this tutorial series). The veritySearch.cfc is the ColdFusion component, and the other files are the sample articles your application will search.
  4. Index the adobeArticles collection in the ColdFusion administrator. Add a few of the file extensions such as .pdf and .doc so that Verity indexes the new files you added to the adobeArticles folder in step 3. For the Directory setting, browse to c:\[your web root]\adobeArticles; and for the return URL, enter http://[your web root]/adobeArticles. See that you have successfully created a collection, and close ColdFusion administrator.
  5. Open Flex Builder 2.
  6. Select File > New > Flex Project.
  7. In the Create New Project dialog box, select the ColdFusion Flash Remoting Service option and click Next.
  8. Set the root folder to the root of your ColdFusion server and click Next.
  9. Name the project DocumentSearch. Specify the location of the project to C:\DocumentSearch.  Click Next.
  10. In the Source Path tab, for the Main Source Folder field, enter src. This specifies to Flex Builder where the source files are located for your project.
  11. Set the Main application file to main.mxml
  12. Set the Output Folder to the root of your ColdFusion server.
  13. Set the Output folder URL to the URL of the root of your ColdFusion server.
  14. Click Finish.
  15. Open searchBar.mxml in the views directory within your DocumentSearch project. Find the RemoteObject tag in line 55 and change the source attribute to adobeArticles.VeritySearch (this is the name of the CFC with dot notation to denote its location in the adobeArticles folder). Take note that if you change the location of the CFC, you'll need to update it in the RemoteObject's source attribute value. Close searchBar.mxml.
  16. Select Run > Run main or click the Run icon (arrow in a green circle). This will compile the application into a SWF file along with an HTML page within a browser.
  17. In the SWF file, type "Flex 2" and click Search in the search interface. Now type "J2EE" to see the second set of search results in a new tab in the interface. To see the "suggestion" feature, enter the typo: "Fex" for a search query. The application will ask if you meant "Flex."
  18. Read on to understand more about how this application was built.

How it Works

When the user types search criteria and clicks Search, the application calls veritySearch.cfc.

Searching the Verity Collection and Handling the Result

The following listing shows the searchCollection function code (you can also see this code in the sample files included with this tutorial).

<cfcomponent>
    <cffunction access="remote" name="searchCollection" returntype="struct">
        <cfargument name="criteria" type="string" required="true">
        <cfargument name="maxrows" type="numeric" required="true">
            <cfsearch 
                collection="adobearticles"
                name="vResults" 
                status="vStatus" 
                criteria="#arguments.criteria#"
                suggestions="5" maxrows="#arguments.maxrows#">
                    <cfset result = structnew()>
                    <cfset result.records = vResults>
                    <cfset result.status = vStatus>
            <cfreturn result>
    </cffunction>
</cfcomponent>

This function takes two arguments:

  • criteria: Specifies the keywords the user has entered in the Flex-based interface.
  • maxrows: Specifies the maximum number of rows the user wants returned.

The cfsearch tag is used to search an indexed collection. The following attributes control the parameters of the search.

  • collection: Specifies the name of the collection to search.
  • name: Specifies the name of the search query.
  • status: Specifies the name of the structure variable into which ColdFusion places search information, including alternative criteria suggestions.
  • criteria: Specifies the search criteria to use against the collection.
  • suggestions: Specifies whether Verity returns spelling suggestions for possibly misspelled words. Additionally, you can specify one of the following values:

    • always: Verity always returns spelling suggestions.
    • never: Verity never returns spelling suggestions.
    • Positive integer: Verity returns spelling suggestions if the number of documents retrieved by the search is less than or equal to the number specified.

    Note: Your application will experience a small performance penalty for retrieving suggestion data.

<cfset result = structnew()>
<cfset result.records = vResults>
<cfset result.status = vStatus>

This is a structure that holds both the result records and the search status information. ColdFusion passes this structure to Flex.

The SearchBar.mxml component

SearchBar.mxml is a self-contained component that calls veritySearch.cfc.

<mx:Button id="searchBTN" label="Search" width="25%" click="doSearch(event)"/>

When the user clicks the button, the the application executes the doSearch function.

private function doSearch(e:MouseEvent):void{
    veritySearchRO.searchCollection(searchTXT.text, resultLimit.value);    
}

The doSearch function calls the RemoteObject tag and passes in two arguments.

  • searchTXT.text is the search string the user has typed.
  • resultLimit.value is the value of the numeric stepper.
<mx:RemoteObject 
    id="veritySearchRO"
    destination="ColdFusion"  
    source="adobeArticles.VeritySearch"
    showBusyCursor="true" 
    fault="handleFault(event)"
    result="handleResult(event)"/>

The attributes of the RemoteObject tag are:

  • id: The RemoteObject tag has an ID attribute of VeritySearchRO, by which it is referenced.
  • source: This is set to adobeArticles.VeritySearch (the name of the CFC, with dot notation to denote its location). Since you placed the CFC in a folder under your webroot called components for example, then the path is "adobeArticles.veritySearch."
  • fault: If an error occurs, the handleFault() function is executed.
  • result: When a result is returned from the CFC, the handleResult() function is invoked.
private function handleFault(e:FaultEvent):void{
    Alert.show("Cannot search documents at this time.","Search Error");
}

If an error occurs and this function is executed, the function simply shows an alert box with a message.

private function handleResult(e:ResultEvent):void{
    var sri:SearchResultItem = createSearchItem(e);
    var se:SearchEvent = new SearchEvent("SearchEvent",sri);
    this.dispatchEvent(se);

    if(e.result.STATUS.SUGGESTEDQUERY == undefined){
        suggestionLBL.text = "No Suggestions";
        suggestionBTN.visible = false;
    }
    else{
        suggestionLBL.text = "Did you mean "+'"'+e.result.STATUS.SUGGESTEDQUERY+'"'+"?";
        suggestionBTN.visible = true;
        suggestionHint = e.result.STATUS.SUGGESTEDQUERY;
    }
}

When a result is returned from the RemoteObject call, the handleResult() function is executed. This function does several things, but its essential purpose is to dispatch a search event that contains information about the search that just took place.

First, you put the search results into an object called SearchResultItem. This object holds the criteria, the records returned, and the extra search information verity provides. Next, test your code to see if the variable STATUS.SUGGESTEDQUERY is defined. If it is, you know that a search suggestion was returned. Finally, you show a "Did you mean [suggested term]" and a button to execute another search using the suggested term.

Now let's say you want to dispatch an event out of this component. Because you want to send the searchResultItem object along with the event, you use a custom event called SearchEvent.

var se:SearchEvent = new SearchEvent("SearchEvent",sri);
this.dispatchEvent(se);

Create an instance of a SearchEvent and put the SearchResultItem you just created inside the event and dispatch it. In short, this component handles executing a search, packages the results of the search into a custom event object, and dispatches the event. The root application listens for the search event dispatched from the SearchBar component.

private function handleSearchEvent(e:SearchEvent):void{
searchHolder.addSearchItem(e.sResultItem);
}

This function takes the SearchResultItem object and inserts it into an ArrayCollection. The SearchResults component is bound to this ArrayCollection, which is the data provider to the repeater for the tab navigator.

<mx:TabNavigator width="100%" height="100%">
    <mx:Repeater id="resultRepeater" dataProvider="{dp.sHolder}">
        <mx:VBox label="{resultRepeater.currentItem.searchCriteria}"> 
                <mx:Label text="Searched {resultRepeater.currentItem.resultStatus.SEARCHED} documents and found {resultRepeater.currentItem.resultStatus.FOUND} matches"/>
            <mx:DataGrid x="0" y="0" width="100%" height="100%" dataProvider="{resultRepeater.currentItem.resultSet}" dragEnabled="true">
                <mx:columns>
                    <mx:DataGridColumn headerText="Matching Documents" itemRenderer="Renderers.ResultRenderer"/>
                </mx:columns>
            </mx:DataGrid> 
        </mx:VBox>
    </mx:Repeater>
</mx:TabNavigator>

In short, the Repeater component is bound to the ArrayCollection that contains each search item. The Repeater loops over each item in the ArrayCollection, creating a new navigator tab and data grid to hold the results.

Bookmarking Documents

While this example is searching on a limited number of PDF files, a corporate intranet could have thousands of files. Having a way to bookmark a document that you have found for future reference is a nice feature. To accomplish this, the project uses shared objects. Shared objects are the Flex equivalent of cookies, but can do much more. Shared objects, unlike cookies, can store complex data structures.

Drag-and-Drop

Drag-and-drop is a common user interface feature in desktop applications. Flex 2 makes implementing drag-and-drop functionality very easy. To enable the user to drag a record into the Favorites data grid is very simple.

Open SearchResults.mxml and notice the DataGrid tag has an attribute dragEnabled="true". This enables the data grid to act as a drag initiator so the user can drag items out of the component.

Open Favorites.mxml and look at the DataGrid tag. Here you see dropEnabled="true" as one of the attributes. This enables the data grid to act as a drop target so the user can drop items into the component.

Where to go from here

This is a simple example of creating a rich search interface to integrate with Verity. Flex 2 opens up endless possibilities for this application. For example, you could add the following features:

  • E-mail documents
  • Search within result set
  • Close button for each search result tab
  • Display total documents in collection
  • Display search execution time
  • Upload document, and because of the DirectoryWatcher event gateway, the document would automatically be added into the collection
  • Add transitions to the interface
  • Select collections to search

As you can see, the list of features to expand this application are endless, and with Flex 2 you can expose these features in a rich one-screen interface.

About the author

Brian Szoszorek has been developing ColdFusion applications since 2000. He is a certified ColdFusion developer and the chief technologist of New Point Media. He has worked and provided consulting services to companies such as New Era Cap Company, XL Capital, Spire Systems, and Adobe Systems. Brian is a strong evangelist for how ColdFusion, Flex, Adobe AIR, and the Flash Platform as they provide higher productivity and richer experiences compared to competing technologies.