ColdFusion UDF to test if a Java Class implements a method
I recently started implementing a couple of our full text search requirements using Sphinx. I am extremely happy with this search engine, as it's lightning fast and provides some quite easy integration with the data we store in our PostgreSQL databases, is highly scalable and fairly easy to implement in ColdFusion via the Sphinx Client API.
As I started writing the search component for our articles, I decided that it would be an extremely valuable feature to use the weighting functionality Sphinx provides. This would allow to set different weights to the indexed fields on a per-query basis, which is extremely cool as you don't have to decide on a general per-field weighting setting during indexing.
The Sphinx API documentation does mention a SetFieldWeights-method which takes a HashMap (i.e. an associative array for those of the PHP persuasion) as argument. I constructed the Hashmap like this (excerpt from a function):
var jHashMap = createobject("java", "java.util.HashMap");
jHashMap.put('keywords',JavaCast('int',arguments.iWeightType));
jHashMap.put('text',JavaCast('int',arguments.iWeightText));So far, so good. Now I tried calling
variables.sphinx.SetFieldWeights(jHashMap);
- and received an error that this function does not exist. One dump of the Sphinx-API later, I saw the error was a mere typo: In org.sphx.api.SphinxClient the method was actually spelled SetFieldeights. Being a responsible user, I did report this bug in the forums, but what to do for the time being? Two options: Correct the spelling mistake in the API-source, recompile and redeploy - and risk that a subsequent update might have to be patched as well (and one tends to forget something like that) or just accept the typo in my own code. I sort of opted for the latter, but I wanted to be equally safe from a later correction of this typo in an updated API.
So I wanted to make my code work with both SetFieldeights or SetFieldWeights. This could be done either by CFTRY/CFCATCHING an error on calling the method (something I try to avoid at all costs), or simply checking if the method is implemented by the class.
Now my first try with structKeyExists() failed. Apparently I cannot test a java object as if it were a structure, or probably more like I cannot test a method of such an object as if it were a member of a conventional structure. isDefined() failed, probably for similar reasons. Okay, on to the good Java stuff. Java provides reflection for such cases, in fact this is what ColdFusion uses when you cfdump such an object.
To make this reusable, decided to write this as as more generic function I could use in another CFC:
<cffunction name="methodExists" returntype="boolean" access="public" output="no" hint="checks if a java class implements a method"> <cfargument name="strClassName" type="string" required="yes"> <cfargument name="strMethodName" type="string" required="yes"> <cfscript> var a = arrayNew(1); var myClass = a.getClass().forName(arguments.strClassName); var arrMethods = myClass.getDeclaredMethods(); var i = 1; var arrMethodNames = arrayNew(1); for (i=1; i lte ArrayLen(arrMethods); i++) { arrMethodNames[i] = arrMethods[i].getName(); } if (arrMethodNames.indexOf(arguments.strMethodName) eq -1) { return false; } else { return true; } </cfscript> </cffunction>
Now to check if the Sphinx-API implements SetFieldWeights, I just need to test the boolean return value like
<cfscript> if (methodExists(strClassName='org.sphx.api.SphinxClient',strMethodName='SetFieldWeights')) { variables.sphinx.SetFieldWeights(jHashMap); } else { variables.sphinx.SetFieldeights(jHashMap); } </cfscript>
Mission accomplished! For those curious to know, the affected Sphinx version was sphinx-0.9.9-rc2.
May 5th, 2010 - 19:13
Hi Markus,
Thanks for the great info about how to setup a hashmap for SetFieldWeights in ColdFusion.
I’ve implemented SetFieldWeights in my Sphinx-related code, but am not seeing any difference in the weight values coming back with the search results. Any idea what the problem might be? Do I need to set the field weights for ALL non-attribute index columns (right now I’m just trying to boost the weight for one indexed column)?
Any insight you might have would be greatly appreciated.
Thanks,
Russ
May 7th, 2010 - 09:20
Hi Russ,
I haven’t actually tested the field-weight feature to any great extent, but as it so happens I am currently in the process of doing a somewhat longer blog post on how to compile, set up, configure and use Sphinx from ColdFusion. I’ll try and cover the field-weight feature as well there with a couple of examples, maybe there’s some conclusive evidence as to what works and what doesn’t to be had there. By the way, which version of Sphinx do you currently use? 0.9.9-final is current at the moment. I hope to have my blog post online by monday.
Kind regards
Markus
May 7th, 2010 - 17:41
I’ve had some SetWeight-related trouble today, too. I did manage to get the expected result as long as I used the extended search mode only. There are plans to implement the full weighting functionality with other search modes, too, but this has not made it into the current version yet. You might be interested in my new post on Sphinx, maybe you can make some sense of your SetWeight-problem by taking a peek at my search-component.
May 11th, 2010 - 23:55
Hi Markus,
I’ve been using 0.9.9-final and extended search mode (SPH_MATCH_EXTENDED2), but haven’t been able to see any visible differences when using the field weights. I’ll check out your new search component. Hopefully that will provide some guidance!
Thanks,
Russ
May 12th, 2010 - 09:10
You’ll need to sort by relevance (SPH_SORT_RELEVANCE), too – so if you’ve got some other sort mechanism (like SPH_SORT_ATTR_DESC) set, you won’t see any difference.