Commit deab35cfc388688e50121389710a201a414e6170

Authored by nbm
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
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 &nbsp;<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 &nbsp;<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 &nbsp;<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 &nbsp;<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 +
... ...