Stolperfalle: ColdFusion Array, contains und Query-Daten

1 Kommentare
Mischa Sameli, Geschäftsführer & Leiter Entwicklung

In ColdFusion sind Array-Funktionen String-Methoden in Sachen Performance und Speicherbedarf in aller Regel ziemlich überlegen. Allerdings sind Suchfunktion für Arrays erst ab CF9 erhältlich. Und so bedient man sich mit älteren Versionen oftmals direkt den Java-Funktionen contains() und indexof(). Das kann böse ins Auge gehen, wie das folgende Beispiel zeigt.

Gehen wir vom folgenden Szenario aus: Wir haben eine umfangreiche Liste mit IDs. Diese Liste wird aufgrund manuell zusammengeschustert. Etwa so:

1<cfset lstObjects = getParentObjectsAsList() />
2<cfloop query="qryGet">
3 <cfif not ListFind(lstObjects, qryGet.ID) and qryGet.isVisible>
4 <cfset lstObjects = ListAppend(lstObjects, qryGet.ID) />
5 </cfif>
6</cfloop>

Die gute Nachricht: das funktioniert so tiptop. Die schlechte: Der Loop frisst RAM wie Heu. Ergo gehen wir hin und ersetzen die Liste durch ein Array:

1<cfset aryObjects = getParentObjectsAsArray() />
2<cfloop query="qryGet">
3 <cfif not ListFind(ArrayToList(aryObjects), qryGet.ID) and qryGet.isVisible>
4 <cfset ArrayAppend(aryObjects, qryGet.ID) />
5 </cfif>
6</cfloop>

Da ColdFusion vor CF9 die Funktion ArrayContains() nicht kannte, musste das Array für den Vergleich entweder wie oben in eine Liste umgewandelt werden, oder aber man benutzte gleich die Java-Methode contains():

1<cfset aryObjects = getParentObjectsAsArray() />
2<cfloop query="qryGet">
3 <cfif not aryObjects.contains(qryGet.ID) and qryGet.isVisible>
4 <cfset ArrayAppend(aryObjects, qryGet.ID) />
5 </cfif>
6</cfloop>

Sieht gut aus, funktioniert aber leider nicht. Und zwar nur in diesem Zusammenhang, weil wir mit numerischen Daten aus einem Query-Objekt vergleichen. Hätten wir das Query manuell erstellt mit QueryNew(), also nicht direkt von einer Datenbank bezogen, würde die Abfrage tadellos funktionieren. Würden wir eine andere Liste, eine Struktur oder ein Array loopen – kein Problem.

Aber kein Problem ohne Lösung, oder in diesem Fall eher ein Workaround:

1<cfset aryObjects = getParentObjectsAsArray() />
2<cfloop query="qryGet">
3 <cfif not aryObjects.contains(qryGet.ID) and qryGet.isVisible>
4 <cfset ArrayAppend(aryObjects, toString(qryGet.ID) ) />
5 </cfif>
6</cfloop>

Das funktioniert, ist schnell, geht aber leider wohl nur allzu schnell vergessen. Darum Vorsicht bei der Verwendung!

Schlimmer noch. Der Workaround muss nicht nur in Query-Fällen angewendet werden, sondern immer wenn ColdFusion numerische Werte liefert als Rückgabewert von internen Funktionen. So liefert beispielsweise auch DatePart() einen numerischen Wert, der mit toString() umgewandelt werden muss. Zusammenfassend erhärtet sich die Theorie:

Wenn ColdFusion Funktionen und Tags numerische Werte liefern, müssen Sie mit toString() konvertiert werden, wenn Java-Operationen wie contains() für Arrays ausgeführt werden sollen.