How to compare column values in cfscript? - coldfusion

I would like to loop over query and compare column values. Here is example of CFML code:
<cfquery name="qryUserPerm" datasource="#Application.dsn#">
SELECT AccessType, AccessLevel, State, City, Building
FROM Permissions
WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>
<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">
<cfif qryUserPerm.AccessLevel EQ "S">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = qryUserPerm.State>
<cfelseif qryUserPerm.AccessLevel EQ "C">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City))>
<cfelseif qryUserPerm.AccessLevel EQ "B">
<cfset local.permissionType = qryUserPerm.AccessType>
<cfset local.permissionLevel = qryUserPerm.AccessLevel>
<cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building))>
</cfif>
Code above should be translated to cfscript, I got this far but can't figure it out how to access column values.
<cfscript>
public string function permissionList(required string AccountID) {
local.fnResults = "";
local.permissionList = "";
try{
local.qryPermissions = new Query();
local.qryPermissions.setDatasource("#Application.dsn#");
local.qryPermissions.setSQL("SELECT AccessType, AccessLevel, State, City, Building FROM Permissions WHERE AccountID = :AccountID");
local.qryPermissions.addParam(name="AccountID",value="#trim(arguments.AccountID)#",cfsqltype="cf_sql_idstamp");
local.qryRes = qryPermissions.execute();
for ( i = 1 ; i <= qryRes.getResult().recordCount ; i++ ) {
if(qryRes["AccessLevel"][i] EQ "S"){
local.permissionList = "";
}else if(qryRes["AccessLevel"][i] EQ "S"){
local.permissionList = ListRemoveDuplicates(ValueList(qryRes.Agency,","));
}else if(qryRes["AccessLevel"][i] EQ "C"){
local.permissionList = ListRemoveDuplicates(ValueList(qryRes.District,","));
}else if(qryRes["AccessLevel"][i] EQ "B"){
local.permissionList = ListRemoveDuplicates(ValueList(qryRes.Building,","));
}
}
local.fnResults = permissionList;
}catch(any e){
local.fnResults = e.message;
//writeOutput(e.message);
}
return fnResults;
}
writeOutput(permissionList(AccountID));
</cfscript>
If anyone can help please let me know.

(From comments ...)
The issue is local.qryRes doesn't actually contain a query object. Confusingly, calling execute() doesn't return a query, but calling execute().getResult() does. Try changing the assignment from:
local.qryRes = qryPermissions.execute();
To:
local.qryRes = qryPermissions.execute().getResult();
A few other observations:
It is important to local scope ALL function variables, including your loop index i. Otherwise, you may get some bizarre and unpredictable results if the component is stored in a shared scope.
Although I don't think a loop is necessary, if you do loop, consider the simpler for..in syntax, instead of an indexed loop:
for (local.row in local.qryPermissions ) {
if (local.row.AccessType eq "S") {
//... code here
}
....
}
Since the access fields are so closely related, I'd probably have the function return a structure containing all three keys (AccessType, AccessLevel, PermissionList) rather than having three separate functions.
Rather than using a loop, consider going with one of the suggestions on your other thread,
Best way to store permissions for the user account?

You can also use :
local.qryPermissions = queryExecute(
"SELECT AccessType, AccessLevel, State, City, Building
FROM Permissions
WHERE AccountID = :AccountID" ,
{AccountID={value="#trim(arguments.AccountID)#", cfsqltype="cf_sql_idstamp"}} // Or "?" and "[value=xxx,cfsqltype=xxx]"
) ;
And then just build out your permissions pieces without the loop:
local.permissionType = qryPermissions.AccessType ;
local.permissionLevel = qryPermissions.AccessLevel ;
switch( qryPermissions.AccessLevel ) {
case "S" : local.permissionList = qryPermissions.State ;
break ;
case "C" : local.permissionList = ListRemoveDuplicates(ValueList(qryPermissions.City)) ;
break ;
case "B" : local.permissionList = ListRemoveDuplicates(ValueList(qryPermissions.Building)) ;
break ;
}
Also see my notes on the other question about potential for unintentional, semi-related data.

Related

Function that includes JSP file is returning an empty result in Lucee but not ACF

I have a function utilizing an include, of a JSP file, to retrieve thread information which is then converted into a query object. The function returns an empty query Lucee, but it executes properly in ColdFusion.
CFML:
<cffunction name="mainThreads" output="false" returntype="query" access="public">
<cfargument name="filterPages" type="boolean" required="true">
<cfscript>
var threadStackDump = "";
var thread = 0;
var stackTrace = "";
request.threads = arraynew(1);
GetPageContext().include("putParentThreadInRequestScope.jsp");
ThreadQuery = QueryNew("id, name, group, stacktrace, alive", "Integer, VarChar, VarChar, VarChar, Bit");
QueryAddRow(ThreadQuery, arrayLen(request.threads));
for ( thread = 1; thread lte arrayLen(request.threads); thread = thread + 1 )
{
QuerySetCell(ThreadQuery, "id", request.threads[thread].getId(), thread);
QuerySetCell(ThreadQuery, "name", request.threads[thread].getName(), thread);
QuerySetCell(ThreadQuery, "group", request.threads[thread].getThreadGroup().getName(), thread);
QuerySetCell(ThreadQuery, "alive", request.threads[thread].isAlive(), thread);
threadStackDump = "";
stackTrace = request.threads[thread].getStackTrace();
for ( element = 1; element lte arrayLen(stackTrace); element = element + 1 )
if ( arguments.filterPages )
{
if ( findNoCase('runPage',stackTrace[element]) neq 0 or findNoCase('runFunction',stackTrace[element]) neq 0 )
threadStackDump = threadStackDump & stackTrace[element] & "#chr(13)#";
}
else
threadStackDump = threadStackDump & stackTrace[element] & "#chr(13)#";
QuerySetCell(ThreadQuery, "stacktrace", threadStackDump, thread);
}
return ThreadQuery;
</cfscript>
</cffunction>
JSP
<%
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
Thread threadList[]=new Thread[Thread.activeCount()];
threadGroup.enumerate(threadList);
request.setAttribute("threads", threadList);
%>
The code is not working in Lucee, but I'm not sure why. Does it have something to do with the java versions?
I'd guess you forgot to enable handling of .jsp in your web.xml, but ... you're not aware of it because the problem code executes inside a cffunction that suppresses all output!
Take a leaf out of Troubleshooting 101 and test the problem code in small chunks. Start by executing the JSP include separately. If it displays the JSP code on screen, instead of executing it, then you know JSP handling isn't enabled, and that's your problem.
<cfscript>
GetPageContext().include("putParentThreadInRequestScope.jsp");
writeDump( request );
</cfscript>

Looping over Structures and storing result in a variable using ColdFusion [duplicate]

Is there a simple way to serialize a single-level structure as a string for use in a url?
for example:
?key1=val1&key2=val2
<cfscript>
// create simple struct
x = { a=1, b=2, c=3 };
WriteDump(x);
// serialize in JSON format and encode for URL transport
y = URLEncodedFormat( SerializeJSON(x));
WriteOutput( 'url: #SCRIPT_NAME#?#y#');
// now receive the URL variable and dump it
if ( StructKeyExists( url, 'z' )) {
writeOutput( '<h3>URL Data:</h3>' );
writeDump( DeserializeJSON( URLDecode( z)));
}
</cfscript>
How does this look?
<cfset tmpStruct = {"firstItem" = "one", "secondItem" = "two"} />
<cfset myUrl = "http://domain.com/file.cfm?" />
<cfloop list="#structKeyList(tmpStruct)#" index="i" >
<cfset myUrl = myUrl & i & "=" & tmpStruct[i] & "&" />
</cfloop>
<cfset myUrl = left(myUrl,len(myUrl)-1) />
<cfdump var="#myUrl#" />

Coldfusion Struct getting only numeric key list

I have a coldfusion Struct containing mix keys numeric and alpha, alphanumerics
I need to access only the numeric keys.
My code looks like
<cfset ids = structkeyList(st ) />
<cfset numericIDs = "" />
<cfloop list="#ids#" index="i">
<cfif IsNumeric(i)>
<cfset numericIDs = ListAppend( numericIDs , i ) />
</cfif>
</cfloop>
Is there a better method to solve such problems?
Is there a better method to solve such problems?
I would use something like this:
<cfset numericIDs = arrayToList(reMatch('\b\d+(?=,|$)\b', structKeyList(st)))>
Is there a better method to solve such problems?
I'd generally recommend working with arrays instead of lists.
In CF9 a loop similar to yours is as good as it gets. You can make a utility function out of it if you need it more than once. This one avoids StructKeyList() to be able to deal with all kinds of keys, independent of a separator character:
<cfscript>
function GetNumericKeys(struct) {
var keys = struct.keys();
var result = ArrayNew(1);
var key = "";
while (keys.hasNext()) {
key = keys.next();
if (IsNumeric(key)) ArrayAppend(result, key);
}
return result;
}
</cfscript>
and
<cfset nkeys = GetNumericKeys(st)>
In CF11 you can get a little more sophisticated (tested on CF11, can't say how CF10 handles this code).
<cfscript>
numericIDs = arrayFilter(structKeyArray(st), function (key) {
return IsNumeric(key);
});
</cfscript>
To ensure integer keys, use:
<cfscript>
numericIDs = arrayFilter(structKeyArray(st), function (key) {
return Int(key) eq key;
});
</cfscript>
I really don't see what's wrong with this. It should work quite well already, and it is very readable.
Sometimes working with a List is faster than an Array.
I had this:
<cfscript> function ListNumeric(principal) {
a=principal;
cleanlist = ''; for (i=1; i <= ListLen(a);i=i+1) { if(IsNumeric(ListGetAt(a,i))){ cleanlist = ListAppend(cleanlist,ListGetAt(a,i)); } } Return cleanlist; } </cfscript>
Also possible to work with regular expression:
inList2 = REReplace(inList,"[^0-9.]", "","ALL");

Why is CFTHREAD not running query and creating a file?

I have a function that should check the date of a file. If the file is greater than sixty seconds, a query should run and create a new file. The query takes sixty seconds to run.
This process works perfectly when it's not wrapped in a CFTHREAD. When CFTHREAD is used, nothing seems to happen. I get no errors. What I expect to see, is a new file being made. I never see that new file.
Where should I look for an error? What am I missing? Why is CFTHREAD not working?
<!--- GET CATEGORIES --->
<cffunction name="getCategories" access="remote">
<cfscript>
LOCAL.MaxFileAge = 60;
LOCAL.MaxFileUnits = 's';
// THE FILE
LOCAL.TheFileDaily = "#VARIABLES.JSDir#\#VARIABLES.DayMonth#-categories.json";
// THE FILE DOES NOT EXIST
if (fileExists(LOCAL.TheFileDaily) == false) {
LOCAL.MakeNewFile = true;
// THE FILE EXISTS
} else {
// GET THE DATE OF THE FILE
LOCAL.LastModified = getFileInfo(LOCAL.TheFileDaily).LastModified;
// GET FILE AGE
LOCAL.FileAge = dateDiff(LOCAL.MaxFileUnits, LOCAL.LastModified, now());
// FILE IS OLD
if (LOCAL.FileAge > LOCAL.MaxFileAge) {
LOCAL.MakeNewFile = true;
} else {
LOCAL.MakeNewFile = false;
}
}
</cfscript>
<cfif LOCAL.MakeNewFile eq true>
<cfthread action="run" priority="HIGH">
<cfquery name="Q">
SELECT Stuff
FROM Tables
</cfquery>
<!--- MAKE THE DAILY FILE --->
<cffile action="write" file="#LOCAL.TheFileDaily#" output="#serializeJSON(Q)#">
</cfthread>
</cfif>
</cffunction>
You can't write to and share the local scope to a seperate thread, you need to share them via the request scope (request is the ideal scope for this as developers have very tight control over what data is contained within). You might try something like this:
Create a struct within the request scope and write to that.
In fairness, only variables you need to transfer need be in the request scope's struct. This is just a generic update because I don't know what the contents of your CFTHREAD really looks like. In this case, it actually looks like TheFileDaily is the only variable you're sharing, so that would be the only thing that needed to be in the request scope.
<!--- GET CATEGORIES --->
<cffunction name="getCategories" access="remote">
<cfscript>
request.lData = StructNew();
request.lData.MaxFileAge = 60;
request.lData.MaxFileUnits = 's';
// THE FILE
request.lData.TheFileDaily = "#VARIABLES.JSDir#\#VARIABLES.DayMonth#-categories.json";
// THE FILE DOES NOT EXIST
if (fileExists(request.lData.TheFileDaily) == false) {
request.lData.MakeNewFile = true;
// THE FILE EXISTS
} else {
// GET THE DATE OF THE FILE
request.lData.LastModified = getFileInfo(request.lData.TheFileDaily).LastModified;
// GET FILE AGE
request.lData.FileAge = dateDiff(request.lData.MaxFileUnits, request.lData.LastModified, now());
// FILE IS OLD
if (request.lData.FileAge > request.lData.MaxFileAge) {
request.lData.MakeNewFile = true;
} else {
request.lData.MakeNewFile = false;
}
}
</cfscript>
<cfif request.lData.MakeNewFile eq true>
<cfthread action="run" priority="HIGH">
<cfquery name="Q">
SELECT Stuff
FROM Tables
</cfquery>
<!--- MAKE THE DAILY FILE --->
<cffile action="write" file="#request.lData.TheFileDaily#" output="#serializeJSON(Q)#">
</cfthread>
</cfif>
</cffunction>
Useful sources:
Working with Threads
Adam Cameron's post on the topic

Looping over lists in cf9

Page 116 of the developer's guide says
"Unlike the cfloop tag, CFScript for-in loops do not provide built-in support for looping over queries and lists."
Q: How do I loop over a list using the new script syntax in ColdFusion 9?
<cfloop list="#qry.Columnlist#" index="FieldName">
<cfset form[FieldName] = qry[FieldName][1]>
</cfloop>
You can also try the listToArray and then use the for-in construct for Arrays in CF9 as:
<cfscript>
aCol = listToArray (qry.ColumnList);
for( fieldName in aCol ){
form[fieldName] = qry[fieldName][1];
}
</cfscript>
<cfscript>
var i = 0;
var l = ListLen(qry.Columnlist);
var FieldName = "";
for (i = 1; i lte l; i = i + 1) // you also can use i++ instead
{
FieldName = ListGetAt(qry.Columnlist, i);
form[FieldName] = qry[FieldName][1];
}
</cfscript>
EDIT Nicer (maybe a even little faster, for really heavy loops) version of the above:
<cfscript>
var i = 0;
var Fields = ListToArray(qry.Columnlist);
var FieldName = "";
var l = arrayLen(Fields);
for (i = 1; i lte l; i = i + 1) // you also can use i++ instead
{
FieldName = Fields[i];
form[FieldName] = qry[FieldName][1];
}
</cfscript>
I would turn the list into an array first. ListGetAt() is not efficient to be called n times in a loop. ArrayLen() however should be quite fast.
<cfscript>
arr = ListToArray(qry.Columnlist);
for (i = 1; i <= ArrayLen(arr); i++)
{
fieldName = arr[i];
form[FieldName] = qry[FieldName][1];
}
</cfscript>