Custom Search Multiple Records

Ken83
Mega Guru

Hello Community,

 

        I am creating a custom search on a UI Page. I have a single text box for input. After you type at least 3 characters, it triggers a timer for 2 seconds, then it automatically builds an encoded query using the search terms entered and runs that query to the sc_cat_item table against the meta fields to find related records using GlideAjax. The script creates <div> elements on the UI Page and sets the text inside of those DIVs to the name of the catalog items returned. There are a couple of problems I've run into so far..

 

1) I'm getting multiple queries running after the timeout period ends. If I typed "password" into the search, it will run the query 8 times (once for each letter) instead of just once for the word.

 

2) If I typed "password verizon", it will only return the records relevant to the term "password" instead of both terms.

 

3) Even using an Encoded Query, i'm not getting accurate search results. If I create a GlideRecord query using the 'Background Scripts' module, and insert the same encoded query, i get accurate results. When it runs via GlideAjax on the UI Page, i don't get the same results.

 

The script that i'm using is a simple GlideRecord query...

 

var ir = new GlideRecord('sc_cat_item');

ir.addEncodedQuery('metaLIKEpassword^ORmetaLIKEverizon');

ir.query();

while(ir.next()){

return ir.name;

}

 

Any suggestions on any of these problems?

1 ACCEPTED SOLUTION

Hi Kenneth,



I can't see anything immediately wrong with your ajax function, except that _getUserGroups() has no return value. Try adding the following statements in red text.



  getUserGroups: function(myUser){


            myUser = this.getParameter('sysparm_user');


            gs.log('getUserGroups() parameter myUser: '+myUser);


            return this._getUserGroups(myUser);


  },



  _getUserGroups: function(myUser){


gs.log('_getUserGroups() parameter myUser: '+myUser);


var gr = new GlideRecord('sys_user_grmember');


  gr.addQuery('user',myUser);


  gr.query();


  gs.log('_getUserGroups() getRowCount(): '+gr.getRowCount());


  var groupsArray = [];


  while(gr.next()){


            //gs.log("SCRIPT INCLUDE :" + gr.group);


            groupsArray.push(gr.group.sys_id.toString());   // collect sys_id values in array of string values


            }


      gs.log('groupsArray: '+groupsArray.toString());


      return groupsArray.toString();


  }



Check that all of the log statements make it to the system log. Confirm the row count value as this will tell you how many results were returned by the query, and then check the final log statement, which should be a comma separated list of sys_id values (the same number as the row count etc). After the final log statement, I've added a return statement which should return the comma separated string of sys_id values, to the first function (getUserGroups()). That function should return the value to your client side function.



Regards,


Jake


View solution in original post

10 REPLIES 10

Hi Kenneth,



I can't see anything immediately wrong with your ajax function, except that _getUserGroups() has no return value. Try adding the following statements in red text.



  getUserGroups: function(myUser){


            myUser = this.getParameter('sysparm_user');


            gs.log('getUserGroups() parameter myUser: '+myUser);


            return this._getUserGroups(myUser);


  },



  _getUserGroups: function(myUser){


gs.log('_getUserGroups() parameter myUser: '+myUser);


var gr = new GlideRecord('sys_user_grmember');


  gr.addQuery('user',myUser);


  gr.query();


  gs.log('_getUserGroups() getRowCount(): '+gr.getRowCount());


  var groupsArray = [];


  while(gr.next()){


            //gs.log("SCRIPT INCLUDE :" + gr.group);


            groupsArray.push(gr.group.sys_id.toString());   // collect sys_id values in array of string values


            }


      gs.log('groupsArray: '+groupsArray.toString());


      return groupsArray.toString();


  }



Check that all of the log statements make it to the system log. Confirm the row count value as this will tell you how many results were returned by the query, and then check the final log statement, which should be a comma separated list of sys_id values (the same number as the row count etc). After the final log statement, I've added a return statement which should return the comma separated string of sys_id values, to the first function (getUserGroups()). That function should return the value to your client side function.



Regards,


Jake


This fixes the issue with the second item I was working on. Now I just have to fix an issue on that table with some random empty records that got created. Anywho, jumping back to my first problem. In your previous post, you mentioned that I should have the script check to see if there is already a query running since I'm using the onkeyup trigger. How would I go about doing that?



I modified my script include to accept the terms that are typed into the box and return those same values just to see what I get back and surprisingly, i'm getting back null values. I tried using the gs.log again but i'm still getting the samething...



syslog_transaction not found for AjaxClientTiming: sysId: , table: , view: Default view, form: ui_page_render


Hi Kenneth,



Query Check


You can only make one GlideAjax call to the server at a time. That is, the client needs to wait for the response (and the callback to run etc) before making another call to the server. Your "onkeyup" action however will keep getting triggered every time you finish typing etc. Therefore, what I would do is use a global boolean variable (initialised as false), which is set to true when the Ajax call is triggered, and set back to false when the callback has finished. The Ajax "send" function should be modified to check this variable, and only send the request if it is false. See below for any idea:


var queryRunning = false;   // global variable


function startSearch(){


      var string = gel("stextbox").value;


      if(string.length > 3){


      setTimeout(function(){


      if(!queryRunning){


          // query was not already running  


          queryRunning = true;     //set to true as query is now running


              var words = gel("stextbox").value;


              var searchTerms = words.replace(/ /g,"^ORmetaLIKE")


              jslog(searchTerms);


              var ga = new GlideAjax("SuperSearchUtils");


              ga.addParam('sysparm_name','findMetaStuff');


              ga.addParam('sysparm_meta_search', searchTerms);


              ga.getXML(findMetaStuffParse);


          }


      },2000);


}


}



function findMetaStuffParse(response){


var answer = response.responseXML.documentElement.getAttribute("answer");


jslog(answer);


queryRunning = false;     //set to false as query has finished


}



You could also separate the Ajax call component into a separate function instead of putting all of the code inside the "setTimeout()" call.



Null Values Returned


This sounds like you have an issue with the ajax call/response. If you've modified the script include handler function (the function that is called by adding the "sysparm_name" parameter to your request) to return the same value that was sent, and it's coming back null, then you should check the following.




(1) Check that you have the name of the script include correctly spelled in your client side function. The red text below needs to be the same name as your script include (extending GlideAjax).


var ga = new GlideAjax("SuperSearchUtils");


(2) Check that the function you're calling is spelled correctly. This is always the first parameter added after instantiating the GlideAjax object (sysparm_name etc).


ga.addParam('sysparm_name','findMetaStuff');


(3) Check that you've used the same parameter name for the value you're sending to the script include (e.g sysparm_meta_search).


ga.addParam('sysparm_meta_search', searchTerms);


(4) Add a gs.log() statement to the script include function, immediately after


var words = this.getParameter('sysparm_meta_search').toString();


gs.log("received sysparm_meta_search: "+words);


(5) Add a console.log() or js.log() statement to the beginning of your callback function, to indicate when the function has been called (this should happen when the server response is received by the client.


function findMetaStuffParse(response){


console.log('findMetaStuffParse() called..');


var answer = response.responseXML.documentElement.getAttribute("answer");


jslog(answer);



If the above doesn't work, I would suggest modifying your script include to return multiple response parameters instead of a singe "answer". See Wiki for details. The advantage to this (albeit more code), is that you can add more than one reply packet for testing. I usually add one element as "status" which is returned as true/false depending on if I found any results in the query, and then x number of elements as your return data. In you case I would stick with a single string value, using comma separated sys_id values, as this can easily be processed by your client function.



If you haven't already, open the developer console in your browser and go to the javascript console. This is where you can run code, but it will also show error messages generated by your code. Once you have this open, initiate your client function to make the ajax call (either by calling it manually - typing it in.. or by typing text into your search field). Watch the JS console to see if any errors are generated. A common error is when the ajax return pack contains no element called "answer" or "reply" etc.



Regards,


Jake


Awesome, I've rebuilt the script and I seem to be getting much better results. Now there is another problem that I keep running into and I've reviewed other Script Includes just to make sure it wasn't something weird with mine but, those NULL values that I kept getting back, I still get them if I pass a value to a separate function in the Script Include. For example...



Here are 2 functions from my Script Include. When attempt to get the result from Function 2, the value comes back as NULL.



var SuperSearchUtils = Class.create();


SuperSearchUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {




  receiveSearchTerms: function(){ //FUNCTION 1


  var searchTerms = this.getParameter('sysparm_search_terms');


  var newTerms = this._separateWords(searchTerms);


  return newTerms.toString();


  },



  _separateWords: function(searchTerms){ // FUNCTION 2


  var separate = searchTerms.replace(/ /g,",");


  return separate;


  //return this._createSubstrings(separate);


  },



Now, if I just have Function 1 return the same values that are passed into it, it returns accurate results. Have you encountered this before?


Hi Kenneth,



I think your issue is related to the data type of "searchTerms" in your function receiveSearchTerms(). Try converting it to a string by adding the following code (in red):



receiveSearchTerms: function(){ //FUNCTION 1


  var searchTerms = this.getParameter('sysparm_search_terms')+'';   // by adding + '' this forces searchTerms to be a string value


  var newTerms = this._separateWords(searchTerms);


  return newTerms.toString();


  },



I've used the +'' method above for converting the data type to a string, however you can usually use .toString(). However, this is only available if the data type supports the .toString() method. By using my method, it will convert any data type to a string, albeit an empty string.



Regards,


Jake