Commit deab35cfc388688e50121389710a201a414e6170
1 parent
eaa69e2a
Add complex boolean search ability - being able to search for this group
of things and/or this group of things. git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@3906 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing
3 changed files
with
125 additions
and
30 deletions
presentation/lookAndFeel/knowledgeTree/js/constructed_search.js
| ... | ... | @@ -19,6 +19,22 @@ function removeFromElementEvent(elem, event_name, func) { |
| 19 | 19 | } |
| 20 | 20 | } |
| 21 | 21 | |
| 22 | +var booleanGroups = Array(); | |
| 23 | + | |
| 24 | +function getBooleanGroupId(table) { | |
| 25 | + for (var i=0; i<booleanGroups.length; i++) { | |
| 26 | + if (booleanGroups[i] == table) { | |
| 27 | + return i; | |
| 28 | + } | |
| 29 | + } | |
| 30 | + // nothing found. | |
| 31 | + simpleLog('DEBUG','no entry found for table.'); | |
| 32 | + booleanGroups.push(table); | |
| 33 | + simpleLog('DEBUG','added entry at '+(booleanGroups.length-1)); | |
| 34 | + return booleanGroups.length-1; // int loc. | |
| 35 | +} | |
| 36 | + | |
| 37 | + | |
| 22 | 38 | // quick and dirty helper - find the nearest parent item matching tagName. |
| 23 | 39 | // FIXME steal the klass or tagName logic from MochiK. |
| 24 | 40 | function breadcrumbFind(elem, tagName) { |
| ... | ... | @@ -41,7 +57,9 @@ var autoIndexCriteria = Array(); |
| 41 | 57 | // initiate the criteria creation process. |
| 42 | 58 | function addNewCriteria(add_button) { |
| 43 | 59 | var parent_row = breadcrumbFind(add_button, 'TR'); |
| 60 | + var parent_table = breadcrumbFind(parent_row, 'TABLE'); | |
| 44 | 61 | simpleLog('DEBUG','addNewCriteria found parent row: '+parent_row); |
| 62 | + simpleLog('DEBUG','addNewCriteria found parent table: '+parent_table); | |
| 45 | 63 | var select_source = parent_row.getElementsByTagName('select'); |
| 46 | 64 | if (select_source.length == 0) { |
| 47 | 65 | simpleLog('ERROR','addNewCriteria found no criteria specification source.: '+parent_row); |
| ... | ... | @@ -59,12 +77,15 @@ function addNewCriteria(add_button) { |
| 59 | 77 | // make this one identifiable. |
| 60 | 78 | autoIndexCriteria.push(0); |
| 61 | 79 | var critId = autoIndexCriteria.length; |
| 80 | + | |
| 81 | + var tableId = getBooleanGroupId(parent_table); | |
| 82 | + simpleLog('DEBUG','got boolean group id'+tableId); | |
| 62 | 83 | |
| 63 | 84 | // ok, warn the user that we're loading the item. |
| 64 | 85 | replaceChildNodes(notify_message, 'loading...'); |
| 65 | 86 | var newCriteriaText = scrapeText(select.options[select.selectedIndex])+' '; // FIXME insert the "input" here. |
| 66 | - replaceChildNodes(select.parentNode, newCriteriaText, INPUT({'type':'hidden', 'name':'boolean_search['+critId+'][type]','value':select.value})); // works thanks to DOM co-ercion. | |
| 67 | - createAdditionalCriteriaOption(); | |
| 87 | + replaceChildNodes(select.parentNode, newCriteriaText, INPUT({'type':'hidden', 'name':'boolean_search['+tableId+']['+critId+'][type]','value':select.value})); // works thanks to DOM co-ercion. | |
| 88 | + createAdditionalCriteriaOption(parent_table); | |
| 68 | 89 | var removeButton = INPUT({'type':'button', 'value':'Remove'}); |
| 69 | 90 | attachToElementEvent(removeButton, 'click', partial(removeCriteria, removeButton)); |
| 70 | 91 | add_button.parentNode.replaceChild(removeButton, add_button); |
| ... | ... | @@ -76,7 +97,7 @@ function addNewCriteria(add_button) { |
| 76 | 97 | simpleLog('DEBUG','addNewCriteria initiating request to: '+targeturl); |
| 77 | 98 | |
| 78 | 99 | var deferred = doSimpleXMLHttpRequest(targeturl); |
| 79 | - deferred.addCallbacks(partial(do_addNewCriteria, dest_cell, critId), handleAjaxError); | |
| 100 | + deferred.addCallbacks(partial(do_addNewCriteria, dest_cell, critId, tableId), handleAjaxError); | |
| 80 | 101 | } |
| 81 | 102 | |
| 82 | 103 | |
| ... | ... | @@ -84,7 +105,7 @@ function addNewCriteria(add_button) { |
| 84 | 105 | // - check for the presence of [ or ]. if so, use everything before [ as |
| 85 | 106 | // the key, and append everything after [. |
| 86 | 107 | // actually replace the contents of the specified td with the responseText. |
| 87 | -function do_addNewCriteria(destination_cell, crit_id, req) { | |
| 108 | +function do_addNewCriteria(destination_cell, crit_id, table_id, req) { | |
| 88 | 109 | simpleLog('DEBUG','replacing content of cell with: \n'+req.responseText); |
| 89 | 110 | destination_cell.innerHTML = req.responseText; |
| 90 | 111 | // whatever was passed in almost certainly has the wrong name, but that's what |
| ... | ... | @@ -96,11 +117,11 @@ function do_addNewCriteria(destination_cell, crit_id, req) { |
| 96 | 117 | |
| 97 | 118 | for (var i=0; i<inputs.length; i++) { |
| 98 | 119 | var obj = inputs[i]; |
| 99 | - obj.name = "boolean_search["+crit_id+"][data]["+obj.name+"]"; | |
| 120 | + obj.name = "boolean_search["+table_id+"]["+crit_id+"][data]["+obj.name+"]"; | |
| 100 | 121 | } |
| 101 | 122 | for (var i=0; i<selects.length; i++) { |
| 102 | 123 | var obj = selects[i]; |
| 103 | - obj.name = "boolean_search["+crit_id+"][data]["+obj.name+"]"; | |
| 124 | + obj.name = "boolean_search["+table_id+"]["+crit_id+"][data]["+obj.name+"]"; | |
| 104 | 125 | } |
| 105 | 126 | simpleLog('DEBUG','criteria addition complete.'); |
| 106 | 127 | } |
| ... | ... | @@ -119,8 +140,8 @@ function removeCriteria(removeButton) { |
| 119 | 140 | simpleLog('DEBUG','criteria removed.'); |
| 120 | 141 | } |
| 121 | 142 | |
| 122 | -function createAdditionalCriteriaOption() { | |
| 123 | - var tbody = getSearchContainer(); | |
| 143 | +function createAdditionalCriteriaOption(parent_table) { | |
| 144 | + var tbody = getSearchContainer(parent_table); | |
| 124 | 145 | if (tbody == null) { |
| 125 | 146 | simpleLog('ERROR','createAdditionalCriteriaOption: No criteria table found.'); |
| 126 | 147 | return null; |
| ... | ... | @@ -144,8 +165,8 @@ function createAdditionalCriteriaOption() { |
| 144 | 165 | tbody.appendChild(TR(null, select_entry, notification_entry, add_entry)); |
| 145 | 166 | } |
| 146 | 167 | |
| 147 | -function getSearchContainer() { | |
| 148 | - var container = getElement('advanced-search-form'); | |
| 168 | +function getSearchContainer(parent_table) { | |
| 169 | + var container = parent_table; | |
| 149 | 170 | if (container == null) { |
| 150 | 171 | simpleLog('ERROR','No criteria table found.'); |
| 151 | 172 | return null; |
| ... | ... | @@ -163,3 +184,48 @@ function getSearchContainer() { |
| 163 | 184 | return container; |
| 164 | 185 | } |
| 165 | 186 | |
| 187 | +// uses addbutton to insertBefore | |
| 188 | +function addBooleanGroup(addbutton) { | |
| 189 | + bodyObj = addbutton.parentNode; | |
| 190 | + simpleLog('DEBUG','adding boolean group to '+bodyObj); | |
| 191 | + | |
| 192 | + // i hate me. i also want sane multiline | |
| 193 | + sourceString = ' <fieldset> <legend>Criteria Group</legend> <table class="advanced-search-form"> <tbody> <tr> <th>Criteria</th> <th>Values</th> </tr></tbody></table> </fieldset>'; | |
| 194 | + | |
| 195 | + | |
| 196 | + // add the fieldset | |
| 197 | + var t = DIV(null); | |
| 198 | + t.innerHTML = sourceString; // urgh. | |
| 199 | + var fieldsetObj = t.getElementsByTagName('FIELDSET')[0]; | |
| 200 | + bodyObj.insertBefore(fieldsetObj, addbutton); | |
| 201 | + tableObj = fieldsetObj.getElementsByTagName('TABLE')[0]; | |
| 202 | + // get an id for the table. | |
| 203 | + var table_id = getBooleanGroupId(tableObj); | |
| 204 | + // add the grouping string | |
| 205 | + groupingString = '<p class="helpText">Return items which match <select name="boolean_condition['+table_id+']"><option value="AND">all</option><option value="OR">any</option></select> of the criteria specified.</p>'; | |
| 206 | + t = DIV(null); | |
| 207 | + t.innerHTML = groupingString; | |
| 208 | + var paraObj = t.getElementsByTagName('P')[0]; | |
| 209 | + fieldsetObj.insertBefore(paraObj, tableObj); | |
| 210 | + | |
| 211 | + // add a basic item to the table. | |
| 212 | + createAdditionalCriteriaOption(tableObj); | |
| 213 | + // done. | |
| 214 | + simpleLog('DEBUG','done adding boolean group.'); | |
| 215 | +} | |
| 216 | + | |
| 217 | +// FIXME do we want a "remove boolean group" setting? | |
| 218 | +// FIXME yes, and its easy (find parent, find ITS parent, toast.) | |
| 219 | +function initialiseChecks() { | |
| 220 | + | |
| 221 | + var initialTables = getElementsByTagAndClassName('TABLE','advanced-search-form'); | |
| 222 | + simpleLog('DEBUG','initialising '+initialTables.length+' criteria groups.'); | |
| 223 | + var t = TABLE(null); | |
| 224 | + for (var i=0; i<initialTables.length; i++) { | |
| 225 | + if (typeof(initialTables[i]) == typeof(t)) { | |
| 226 | + getBooleanGroupId(initialTables[i]); | |
| 227 | + } | |
| 228 | + } | |
| 229 | +} | |
| 230 | + | |
| 231 | +addLoadEvent(initialiseChecks); | ... | ... |
presentation/lookAndFeel/knowledgeTree/search/booleanSearch.php
| ... | ... | @@ -48,7 +48,7 @@ class BooleanSearchDispatcher extends KTStandardDispatcher { |
| 48 | 48 | // TODO finally return via PatternBrowseableSearchResults (urgh.) |
| 49 | 49 | |
| 50 | 50 | $datavars = KTUtil::arrayGet($_REQUEST, 'boolean_search'); |
| 51 | - $booleanJoinName = KTUtil::arrayGet($_REQUEST, 'boolean_condition'); | |
| 51 | + $booleanJoinName = KTUtil::arrayGet($_REQUEST, 'outer_boolean_condition'); | |
| 52 | 52 | |
| 53 | 53 | if (empty($datavars)) { |
| 54 | 54 | $this->errorRedirectToMain('You need to have at least 1 condition.'); |
| ... | ... | @@ -59,23 +59,24 @@ class BooleanSearchDispatcher extends KTStandardDispatcher { |
| 59 | 59 | |
| 60 | 60 | // Step 1: extract the criteria selection, and create an array of criteria. |
| 61 | 61 | $criteria_set = array(); |
| 62 | - foreach ($datavars as $order => $dataset) { | |
| 63 | - $oCriterion = Criteria::getCriterionByNumber($dataset["type"]); | |
| 64 | - if (PEAR::isError($oCriterion)) { | |
| 65 | - $this->errorRedirectToMain('Invalid criteria specified.'); | |
| 62 | + foreach (array_keys($datavars) as $k) { | |
| 63 | + foreach ($datavars[$k] as $order => $dataset) { | |
| 64 | + $oCriterion = Criteria::getCriterionByNumber($dataset["type"]); | |
| 65 | + if (PEAR::isError($oCriterion)) { | |
| 66 | + $this->errorRedirectToMain('Invalid criteria specified.'); | |
| 67 | + } | |
| 68 | + $criteria_set[$k][] = array($oCriterion, $dataset["data"]); | |
| 66 | 69 | } |
| 67 | - $criteria_set[] = array($oCriterion, $dataset["data"]); | |
| 68 | 70 | } |
| 69 | - $res = $this->handleCriteriaSet($criteria_set, $booleanJoinName); | |
| 71 | + $res = $this->handleCriteriaSet($criteria_set, $booleanJoinName, $_REQUEST['boolean_condition']); | |
| 70 | 72 | |
| 71 | 73 | return $res; |
| 72 | 74 | } |
| 73 | - | |
| 74 | - function handleCriteriaSet($aCriteriaSet, $mergeType='AND') { | |
| 75 | - global $default; | |
| 76 | - $aSQL = array(); | |
| 77 | - $aJoinSQL = array(); | |
| 78 | - foreach ($aCriteriaSet as $oCriterionPair) { | |
| 75 | + | |
| 76 | + function _oneCriteriaSetToSQL($aOneCriteriaSet) { | |
| 77 | + $aSQL = array(); | |
| 78 | + $aJoinSQL = array(); | |
| 79 | + foreach ($aOneCriteriaSet as $oCriterionPair) { | |
| 79 | 80 | $oCriterion = $oCriterionPair[0]; |
| 80 | 81 | $aReq = $oCriterionPair[1]; |
| 81 | 82 | $res = $oCriterion->searchSQL($aReq); |
| ... | ... | @@ -104,8 +105,27 @@ class BooleanSearchDispatcher extends KTStandardDispatcher { |
| 104 | 105 | exit(0); |
| 105 | 106 | } |
| 106 | 107 | |
| 107 | - $sSQLSearchString = join("\n ".$mergeType." ", $aCritQueries); | |
| 108 | + return array($aCritQueries, $aCritParams, $aJoinSQL); | |
| 109 | + } | |
| 110 | + | |
| 111 | + function criteriaSetToSQL($aCriteriaSet, $mergeType='AND', $innerMergeTypes = null) { | |
| 112 | + $aJoinSQL = array(); | |
| 113 | + $aSearchStrings = array(); | |
| 114 | + $aParams = array(); | |
| 115 | + foreach ($aCriteriaSet as $k => $aOneCriteriaSet) { | |
| 116 | + list($aThisCritQueries, $aThisParams, $aThisJoinSQL) = $this->_oneCriteriaSetToSQL($aOneCriteriaSet); | |
| 117 | + $aJoinSQL = array_merge($aJoinSQL, $aThisJoinSQL); | |
| 118 | + $aParams = array_merge($aParams, $aThisParams); | |
| 119 | + $aSearchStrings[] = "\n\t\t(\n\t\t\t" . join("\n " . KTUtil::arrayGet($innerMergeTypes, $k, "AND") . " ", $aThisCritQueries) . "\n\t\t)"; | |
| 120 | + } | |
| 108 | 121 | $sJoinSQL = join(" ", $aJoinSQL); |
| 122 | + $sSearchString = "\n\t(" . join("\n\t\t" . $mergeType . " ", $aSearchStrings) . "\n\t)"; | |
| 123 | + return array($sSearchString, $aParams, $sJoinSQL); | |
| 124 | + } | |
| 125 | + | |
| 126 | + function handleCriteriaSet($aCriteriaSet, $mergeType='AND', $innerMergeTypes = null) { | |
| 127 | + global $default; | |
| 128 | + list($sSQLSearchString, $aCritParams, $sJoinSQL) = $this->criteriaSetToSQL($aCriteriaSet, $mergeType); | |
| 109 | 129 | |
| 110 | 130 | $sToSearch = KTUtil::arrayGet($aOrigReq, 'fToSearch', 'Live'); // actually never present in this version. |
| 111 | 131 | |
| ... | ... | @@ -141,7 +161,7 @@ class BooleanSearchDispatcher extends KTStandardDispatcher { |
| 141 | 161 | $aParams[] = $sToSearch; |
| 142 | 162 | $aParams = array_merge($aParams, $aCritParams); |
| 143 | 163 | |
| 144 | - //'<pre>'.var_dump(DBUtil::getResultArray(array($sQuery, $aParams))); | |
| 164 | + //print '<pre>';var_dump(DBUtil::getResultArray(array($sQuery, $aParams))); | |
| 145 | 165 | //exit(0); |
| 146 | 166 | //return '<pre>'.print_r(DBUtil::getResultArray(array($sQuery, $aParams)), true).'</pre>'; |
| 147 | 167 | $iStartIndex = 1; | ... | ... |
templates/ktcore/boolean_search.smarty
| ... | ... | @@ -50,12 +50,15 @@ legend { border: 1px dotted #999;} |
| 50 | 50 | |
| 51 | 51 | <form> |
| 52 | 52 | <input type="hidden" name="action" value="performSearch" /> |
| 53 | + | |
| 54 | + <p class="helpText">Return items which match <select name="outer_boolean_condition"><option value="AND">all</option><option value="OR">any</option></select> of the <strong>criteria groups</strong> specified.</p> | |
| 55 | + | |
| 53 | 56 | <fieldset> |
| 54 | - <legend>Boolean Search.</legend> | |
| 57 | + <legend>Criteria Group</legend> | |
| 55 | 58 | |
| 56 | - <p class="helpText">Return items which match <select name="boolean_condition"><option value="AND">all</option><option value="OR">any</option></select> of the criteria specified.</p> | |
| 59 | + <p class="helpText">Return items which match <select name="boolean_condition[0]"><option value="AND">all</option><option value="OR">any</option></select> of the criteria specified.</p> | |
| 57 | 60 | |
| 58 | - <table id="advanced-search-form"> | |
| 61 | + <table class="advanced-search-form"> | |
| 59 | 62 | <tbody> |
| 60 | 63 | <tr> |
| 61 | 64 | <th>Criteria</th> |
| ... | ... | @@ -76,8 +79,13 @@ legend { border: 1px dotted #999;} |
| 76 | 79 | </table> |
| 77 | 80 | |
| 78 | 81 | </fieldset> |
| 79 | - <input type="submit" name="submit" value="performSearch" /> | |
| 82 | + | |
| 83 | + <input type="button" value="add another set of criteria" onclick="addBooleanGroup(this)"/> | |
| 84 | + | |
| 85 | + | |
| 86 | + <input type="submit" name="submit" value="Search" /> | |
| 80 | 87 | </form> |
| 88 | + | |
| 81 | 89 | <!-- |
| 82 | 90 | <table id="brad-log" width="100%"> |
| 83 | 91 | <thead> |
| ... | ... | @@ -88,7 +96,8 @@ legend { border: 1px dotted #999;} |
| 88 | 96 | </tr> |
| 89 | 97 | </thead> |
| 90 | 98 | <tbody> |
| 99 | +--> | |
| 91 | 100 | |
| 92 | 101 | </tbody> |
| 93 | 102 | </table> |
| 94 | ---> | |
| 103 | + | ... | ... |