This is a demo page (Main) for the "JSON Search UI with JavaScript" CloudSpokes Challenge (CSC1903). The page uses some mockup json data defined in javascript, allowing the user to query results from this dataset. The query can be filtered for specific properties of the objects in the dataset controlled by some checkboxes. The query string itself may contain the following control commands:
- OR term, example query 'Macao green'
- AND term, example query 'Macao +green'
- NOT term, example query 'Macao -indigo'
- any of these mixed, example query 'Macao -9 +green +indigo'
The results obtained by the CSC1903.search method are returned as a "weight-sorted" array, where the "weight" of each result depends on the number of occurrence of the matching search terms. Best match is the result with the most weight.
Classes are tested with Jasmine, source for test classes can be found inside the folder ./src/test/spec.
Main class is inside the folder ./src/main/js.
csc1903 project root | |-lib contains third-party libraries |---google.closure google closure compiler binaries, used in ANT |---jasmine-1.2.0 jasmine unit testing | |-src contains the source of the project |---main this folder contains the application source |-----css styles |-------cupertino |---------images |-----js here is the CSC1903.js class located |---test folder with files for running tests |-----spec test specifications
To get the results from an array of Objects, call the CSC1903.search public method of the class. The methods signature is
CSC1903.search(searchTerm, inputArray, attributes)
About details of the calling arguments, please read the function description inside the class.
Create a folder in Your workspace:
$ cd ~/workspace ~workspace$ mkdir csc1903
Unpack the submitted zip archive:
~workspace/csc1903$ unzip csc1903.zip
or alternatively get source from git:
~workspace$ git clone https://github.com/bitbay/csc1884.git
The only configuration is located in the CSC1903.js file, inside the Logger class - DEBUG. This controls whether the application should log it's progress to the the console or not.
The ANT build.xml has a browser.exe property that may need adjustments for Your system. If the browser You want to use is inside Your $PATH, just change this property value to the name of the browsers executable You would like to use. Alternatively You can define the absolute path containing the executable (with the executable at the end).
The application uses ANT for the compiling, deploying and testing, so compilation is as straightforward as:
~workspace/csc1903$ ant all
This will compile all sources into the ./build folder, populate the ./dist folder with a ready to deploy version of the demo application (with the production-ready, compressed js class), and finally open up a browser with the main html page.
By default, the application does not log debug messages to the console. To turn them on, set the Logger class DEBUG field to true.
To see all the available ANT tasks, type
~workspace/csc1903$ ant help
or simply
~workspace/csc1903$ ant
from the project directory (where the build.xml is).
Run with ANT, typing:
~workspace/csc1903$ ant run
Alternatively, You could navigate with a browser to ./dist/Jsearch.html.
To run the Jasmin specs included using ANT, type:
~workspace/csc1903$ ant test-run
This will build the application including ./build/test folder, and open the ./build/test/SpecRunner.html file in the browser.
Notes on test warnings. The application depends on/uses the jQuery plugin, and throws some warnings about not defined variables/methods of it, even with the closure compilers @externs_url directive. Till now i was not able to determine if it is caused by bugs or my lack of knowledge.
Basically, the search term may contain three type of control commands, OR, AND, and NOT, as many times as the User needs and in any order/mixing. All of these can be without quotes (words) or quoted (complex terms). The AND control is defined by the plus '+' sign, the NOT command is defined by the minus '-' sign, and the OR has no special control character (each matched term in the search string without +/- will be considered to be of this type).
Some modifications done to the original html:
- the results are passed around as argument to the following functions: showResults(results) createTable(results)
- generally, checkbox inputs in html should have their value property set. In this case these value properties should match the attribute field names in the to-be-checked objects. (as in --input type="checkbox" value="color"-- would correspond to {color:""}) These could be interpreted as "hard-coded" constants/values...
More inplementation changes are done inside the function searchclick(). Please read the comments/annotations inside that function for details.
The search function basically does two things: splits the search term into OR, AND, and NOT chunks, saving them in a splittedTerms object with these three properties.
var splittedTerms = { or: [], and: [], not: [] }
After separating the search terms, it parses the inputArray for possible matches, as in the following pseudo-code. One important thing to notice is that during the preliminatory matching phase the matched terms are saved as keys with arrays as values where the matched json-properties get pushed as in:
var matchedAttrs = { or:{}, and:{}, not:{} } where or = { "matched term string": [ "mathced json.property 1", "mathced json.property 2" ] }
This way the final record matching algorithm is TRIVIAL (especially the AND matching).
Pseudo-code:
// store the matched records in an array
var matchedRecords = [];
for each inputObj in inputArray
for each attr in inputObj
/*
* if the attribute is on the "whitelist", do the term and record
* matching logic
*/
if attributes.contains(attr)
var matchedAttrs = { or:{}, and:{}, not:{} }
/*
* all search terms ("or" and "and") are checked against all
* whitelisted attributes
*/
// OR term matches
for each orTerm in splittedTerms.or
if inputObj.attr.contains(orTerm)
matchedAttrs.or.orTerm.push( attr )
endif
endfor // OR matches
// AND term matches
for each andTerm in splittedTerms.and
if inputObj.attr.contains(andTerm)
matchedAttrs.and.andTerm.push( attr )
endif
endfor // AND term matches
// NOT term matches
for each notTerm in splittedTerms.not
if inputObj.attr.contains(notTerm)
matchedAttrs.not.notTerm.push( attr )
endif
endfor // NOT term matches
endif // attribute whitelist
endfor // attr
/*
* if there is(are) OR term(s), the matchedAttrs.or must not be empty,
* if there is(are) NOT term(s), the matchedAttrs.not must be empty,
* if there is(are) AND term(s), the matchedAttrs.and must not be empty
* and has to match the length of splittedTerms.and
*/
// record matching
/*
* now this is just a really simple explanation on how the matching
* algorithm for each record works, please see full details in the
* actual implementation...
*/
var matchType
if splittedTerms.or.length > 0
matchType.add(OR)
if splittedTerms.and.length > 0
matchType.add(AND)
if splittedTerms.not.length > 0
matchType.add(NOT)
/*
* just the basic conditions are listed here, to see all the combinations
* please see the source file.
*/
switch matchType
case OR
if matchedAttrs.or.length > 0
matched = true
break
case AND
if matchedAttrs.and.length == splittedTerms.and.length
matched = true
break
case NOT
if matchedAttrs.not.length == 0
matched = true
break
endswitch
if matched matchedRecords.push({
record: inputObj,
matches: matchedAttrs,
weight: calculatedWeight(matchedAttrs) });
endfor // inputObj
return matchedRecords
with the following assertion:
- if there are no search terms, the records automatically match, eg. return all inputRecords as matched. (This should not be the case, since the original script does not even call the searching function in case the input query equals "", @see original jsearch.html at line 6769).
For in-detail function of the searching and matching methods see the corresponding methods in the CSC1903 class.