rss
twitter
    Made it to the hotel. Where is everyone?

ColdFusion 10 - ORM Search

One of my favorite new features in ColdFusion 10 is ORM Search. ORM Search is pretty much what it sounds like, its a way to be able to quickly and easily search data stored in your ORM objects.

NOTE: All the code samples found in this post are from the sample code which you can get here. The code utilizes the 'cfbookclub' database and datasource that are installed when you install the demos

Before we can search our objects, we first need to add some values to the this.ormSettings in Application.cfc. Here is the Application.cfc we will use for this example.

NOTE:<cfsript> tags are added to code examples solely to trigger the code coloring.

view plain print about
1<cfscript>
2component
3{
4
5    this.name = "ORM_Searchtest";
6    this.mappings["/cfc"] = getDirectoryFromPath(getCurrentTemplatePath()) & "/cfc";
7    this.ormEnabled = "true";
8    this.ormSettings.datasource = "cfbookclub";
9    this.ormsettings.cfclocation = "/cfc";
10    this.ormSettings.logsql = true;
11    this.ormsettings.saveMapping = true;
12    
13    this.ormsettings.searchenabled = "true";
14    this.ormSettings.search.autoindex = "true";
15    this.ormSettings.search.language = "English";
16    this.ormsettings.search.indexDir = getDirectoryFromPath(getCurrentTemplatePath()) & "/ormindex";
17    
18    function onRequestStart(){
19        if( structKeyExists( url, "init" ) ){
20            ORMReload();
21        }
22    }
23}
24
</cfscript>

The new settings we will be using are :

  • this.ormsettings.searchenabled This turns on ORM search for you application.
  • this.ormsettings.autoindex This tells ColdFusion to update the ORM index when items are added, updated or deleted
  • this.ormSettings.search.language This tells ColdFusion what language to use for the index.
  • this.ormsettings.search.indexDir This tells ColdFusion were to store the search files. For each ORM entity you make searchable, a folder will be created in the folder referenced by this.ormsettings.search.indexDir with the name of the entity. This can be set at the server level in the ColdFusion Administrator. NOTE:It is a good idea to NOT have this directory under your web root.

Now that we have our application configured for ORM search, lets go set up our ORM entities. The code below represents our Book object (and the only object in this example)

view plain print about
1<cfscript>
2component persistent="true" table="BOOKS" schema="APP" output="false" indexable="true" autoindex="true"
3{
4    /* properties */
5    
6    property name="bookID" column="BOOKID" type="numeric" ormtype="int" fieldtype="id";
7    property name="title" column="TITLE" type="string" ormtype="string" indexable="true" ;
8    property name="description" column="BOOKDESCRIPTION" type="string" ormtype="clob" indexable="true" indexboost="5.0";
9    property name="image" column="BOOKIMAGE" type="string" ormtype="string";
10    property name="thumbnail" column="THUMBNAILIMAGE" type="string" ormtype="string";
11    property name="isSpotlight" column="ISSPOTLIGHT" type="string" ormtype="string";
12    property name="genre" column="GENRE" type="string" ormtype="string";
13        
14}
15
</cfscript>

You may have noticed a few new attribute on our component. These are:

  • indexable This tells ColdFusion that this object has properties that can be indexed.
  • autoindex This tells ColdFusion that the indexable properties should be indexed whenever the entity is saved or deleted. This should not be required when autoIndex is enabled in Application.cfc

Next we will look at a couple of new attributes on our object's properties.

  • indexable This tells ColdFusion that this property should be indexed.
  • indexBoost This tells allows you to prioritize the search results. Results form this property will will appear above others in the query results.
There are other settings at the component and property level, but for this basic example we are going to stick with just these.

Now that we have set up our application and object(s) to use ORM search, we need to index the data. Even though the object is set to 'autoIndex', we still need to index the data initially. This is done with the new ORMIndex() function.

view plain print about
1<cfscript>
2ORMIndex(); //This will index ALL your indexable objects
3
ORMIndex( "entityName" ); //This will index all the entities of type 'entityName'
4
ORMIndex( "entitName list") //This is a comma delimited list of entities to index
5
ORMIndex( entityObject ) //This is a specific enitity instance to index
6
</cfscript>

Once you have your objects indexed, you can now use the new ORMSearch() function to search your objects

view plain print about
1<cfscript>
2ORMSearch( "search query", "EntityName", fields, optionMap );
3
</cfscript>

  • search query Is the search term(s) you wish to search for.
  • EntityName The name of the entity you wish to search. Provided they all share the same properties listed in fields, you can supply a comma delimited list of Entities to search.
  • fields is an array of properties you want to search. If multiple entities are specified in the second argument, they must each have properties that match the values in this array
  • optionMap A structure that allows you to specify extra options.
    • sort Sorts based on the field name specified
    • offset Specifies the position from which to retrieve the objects - you would use this for pagination
    • maxResults The maximum number of results to be retrieved

Here is the search code from our example.

view plain print about
1<cfset results = ORMSearch( url.search, "Book", ["title", "description"]) />

Here we are using the value from url.search to search the 'title' and 'description' properties of the 'Book' entity.

Please note that ORMSearch() returns the following structure

  • DATA An array of structures which contains the follwoing:
    • entity The matching entity instance
    • score The 'score' for the match. By default, the aray is sorted by the 'score'
  • maxTotalCount The total number of matching objects.
NOTE: ORMSearch() returns ORM objects and NOT a query like <cfserach>

You can download the sample code here. The sample code includes the following files:

  • Application.cfc I think we all know what this is for
  • test.cfm This file is responsible for doing the initial ORMINdex().
  • test2.cfm This file contains code to perform searches.
  • test3.cfmThis file allows you to change the description of a Book.
  • cfc/Book.cfc The Book ORM object

After indexing the data and trying a few searches, you should use 'test3.cfm' to change the description of a book (the code only allows you to change the description of the first book in the database). Once you have changed the description, go back to test2.cfm to try searching for a term in the new description.

4 comments

(Comment Moderation is enabled. Your comment will not appear until approved.)
Kerr said...
Thanks for posting a quick example on how to get ORMSearch rolling. This feature has a lot of potential, so I started playing around with it tonight. My use case is searching forum posts. After indexing, I was dismayed to find that ORM is fetching entities with individual EntityLoadByPK calls. This could become a performance issue when a lot of search results are returned. I know one could paginate with offSet and maxResults, but still this approach doesn't sound like it will scale well.
T O said...
Could this be used as an alternative to setting up Solr collections? Also, does ORM Search accommodate wildcard searches where the wildcard begins or ends the search string? For example, bowl* to find bowler, bowling, bowled. Or, *ing to find words ending in ing.
Scott Stroz said...
This could be an alternative to Solr collections, assuming that you only have data from your ORM objects to index.

When searching, your search terms must follow the same rules as it does for cfsearch.