Jul 22 2010

Extracting a row from a query

Posted by Steve Onnis at 11:59 PM
3 comments
- Categories: Coldfusion | General | Regular Expressions

I had a friend of mine, Dale Fraser ask me if there was a way to return a row from a query recordset as a structure.  There are many query functions in ColdFusion but sadly there is not one that will just give you a single row from a recordset.  We had a look at queryConvertForGrid() but it just gave back a query object.  What we were looking for was a plain structure object.

Now the simplest way to acheive this would be to create a UDF and loop over each column and create a new structure to return, maybe something like this:

<cffunction name="queryRowToStruct" access="private" returntype="struct">
    <cfargument name="query" type="query" required="true" />
    <cfargument name="row" type="numeric" default="1" />
  
    <cfset var local = {} />
    <cfset local.result = {} />
  
    <cfloop index="local.i" list="#query.columnList#">
        <cfset local.result[local.i] = arguments.query[local.i][arguments.row] />
    </cfloop>
 
    <cfreturn local.result />
</cffunction>

Then i got to thinking, what if we can acheive the same result just using ColdFusion functions without having to do a loop.  I thought what if we could somehow use the new implicit structure creation with {} to create a string and use Evaluate() to create our new sctructure.  Well the string part was easy enough using regular expressions but where it fell over was trying to evaluate a string with {} in it.

Then i thought, "Hey! JSON! I can use the JSON functions to decode a JSON string!". So rather than try to evaluate the string and try to create my structure that way i could just create my JSON style string and use DESerialJSON() to create my structure.  On a side note, I really cant understand why they called the function that.  Array functions start with "array", structure functions start with "struct", spreadheet functions start with "spreadsheet", date functions start with "date", query functions start with "query" and so on. Why not call the JSON functions JSONEncode() and JSONDecode()? Instead they called them SerialJSON() and DESerialJSON() ....go figure!

So here we have one line of nested functions to extract a single row from a query and return it as a structure object instead of a query object.

<cfscript>
    q = queryNew("");
    queryAddColumn(q, "col1", listToArray("a,b,c,d,e,f"));
    queryAddColumn(q, "col2", listToArray("g,h,i,j,k,l"));
 
    function queryRowToStruct(query, row) {
        return DESerializeJson("{#evaluate(DE("#REReplaceNoCase(q.columnList, '\b([^,]*)\b', '"\1" : "##IIF(row GT q.recordCount, DE(''''), ''query.\1[1]'')##"', 'ALL')#"))#}");
        }
</cfscript>
<cfdump var="#queryRowToStruct(q, 2)#">

The process ends up returning a string that looks like this:

{"COL1" : "a","COL2" : "g"}

This is just a JSON string so we can just decode it with DESerializeJson() to create out ColdFusion structure.  The key here i think is the use of the regular expression and using the backreference to inject into our JSON string.

I think this is pretty!  What do you think?

Comments

Todd Sharp

Todd Sharp wrote on 07/23/10 1:25 AM

Interesting approach. I think I prefer the old method though. I'd bet that using evaluate makes your new method even slower then looping it.

Also - I could be wrong, but I'd be willing to bet they used serialize/deserializeJSON because a lot of people were rolling their own JSONserialize/encode functions before CF added them. This naming convention would prevent breaking existing code....but I could be totally wrong.
Robert Zehnder

Robert Zehnder wrote on 07/23/10 2:31 AM

I am with Todd on this one. Your first version looks cleaner and I always try to avoid using an evaluate() call when it is avoidable.

Also, a few years down the road I know which would be easier to decipher if something needed to be modified for one reason or another.
Steve Onnis

Steve Onnis wrote on 07/23/10 5:23 PM

This was merely just a "what if" exercise though to be honest I would actually think that this would run faster than the loop method so I ended up testing it and found the column loop was actually a bit slower than the regex and the evaluate

Write your comment



(it will not be displayed)



Leave this field empty: