comparison query_builder/querybuild.html @ 32:acc60a20582c

Temporary Changes for querybuilder. Selecting attribute and having results list filter not fully working yet. Need 'ALL' option in attributes.
author arussell
date Sat, 19 Dec 2015 09:15:43 -0500
parents 5384b71df52a
children 4bbb832c53ac
comparison
equal deleted inserted replaced
28:5384b71df52a 32:acc60a20582c
5 <title>Query Builder</title> 5 <title>Query Builder</title>
6 <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> 6 <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
7 <link rel="stylesheet" href="select2-4.0.1/dist/css/select2.min.css"> 7 <link rel="stylesheet" href="select2-4.0.1/dist/css/select2.min.css">
8 8
9 <script type="text/javascript" src="js/d3.min.js"></script> 9 <script type="text/javascript" src="js/d3.min.js"></script>
10 <script type="text/javascript" src="js/underscore-min.js"></script>
10 <script type="text/javascript" src="select2-4.0.1/vendor/jquery-2.1.0.js"></script> 11 <script type="text/javascript" src="select2-4.0.1/vendor/jquery-2.1.0.js"></script>
11 <script type="text/javascript" src="select2-4.0.1/dist/js/select2.full.min.js"></script> 12 <script type="text/javascript" src="select2-4.0.1/dist/js/select2.full.min.js"></script>
13
12 14
13 15
14 </head> 16 </head>
15 <body style="background:none;"> 17 <body style="background:none;">
16 <div role="navigation" class="navbar navbar-default navbar-static-top"> 18 <div role="navigation" class="navbar navbar-default navbar-static-top">
47 <div class="s2-example"> 49 <div class="s2-example">
48 <div id="filters"> 50 <div id="filters">
49 <div class="row" id="startrow" style="margin-top: 15px"> 51 <div class="row" id="startrow" style="margin-top: 15px">
50 <div class="col-sm-4 col-md-4" id="select-col1"> 52 <div class="col-sm-4 col-md-4" id="select-col1">
51 <select class="selected-object form-control"> 53 <select class="selected-object form-control">
52 <option selected>Selected object is: </option> 54 <option selected>Object type : </option>
53 </select> 55 </select>
54 </div> 56 </div>
55 <div class="col-sm-4 col-md-4" id="select-col2"> 57 <div class="col-sm-4 col-md-4" id="select-col2">
56 <select class="select-object1 form-control"> 58 <select class="select-object1 form-control">
57 <option selected>TEXT</option> 59 <option selected>TEXT</option>
58 <option>WITNESS</option> 60 <option>WITNESS</option>
59 <option>PERSON</option> 61 <option>PERSON</option>
62 <option>CODEX</option>
63 <option>PLACE</option>
64 <option>REPOSITORY</option>
65 <option>COLLECTION</option>
60 </select> 66 </select>
61 </div> 67 </div>
62 </div> 68 </div>
63 <div class="row" id="row1" style="margin-top: 15px"> 69 <div class="row" id="row1" style="margin-top: 15px">
64 <div class="col-sm-4 col-md-4" id="1filter-col1"> 70 <div class="col-sm-4 col-md-4" id="1filter-col1">
103 109
104 // Defining initial variables and helper functions 110 // Defining initial variables and helper functions
105 var sourceType = "TEXT"; 111 var sourceType = "TEXT";
106 var targets = []; 112 var targets = [];
107 var targetTypes = []; 113 var targetTypes = [];
108 var listTolerance = 100; 114 var targetObj = []; // The current objects on the display box
109 var numFilters = 1; 115 var numFilters = 1;
110 var filters = [ 116 var filters = [
111 { id: 0, text: 'has relation' }, 117 { id: 0, text: 'has relation' },
112 { id: 1, text: 'attribute contains' } 118 { id: 1, text: 'attribute' }
113 ]; 119 ];
114 $(".filter-box"+numFilters).select2({ 120 $(".filter-box"+numFilters).select2({
115 // Initialize first filter box 121 // Initialize first filter box
116 data: filters, 122 data: filters,
117 minimumResultsForSearch: Infinity 123 minimumResultsForSearch: Infinity
118 }); 124 });
125 var listTolerance = 100;
126 //$("")
127
119 function filter_html(n) { 128 function filter_html(n) {
120 return '<div class="row" id="row'+n+'" style="margin-top: 15px">' + 129 return '<div class="row" id="row'+n+'" style="margin-top: 15px">' +
121 '<div class="col-sm-4 col-md-4" id="'+n+'filter-col1">' + 130 '<div class="col-sm-4 col-md-4" id="'+n+'filter-col1">' +
122 '<select class="filter-box'+n+' form-control">' + 131 '<select class="filter-box'+n+' form-control">' +
123 '<option value="" disabled selected>Choose filter...</option>' + 132 '<option value="" disabled selected>Choose filter...</option>' +
125 '</div>' + 134 '</div>' +
126 '</div>'; 135 '</div>';
127 } 136 }
128 function constraint_html(classn) { 137 function constraint_html(classn) {
129 return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col2">' + 138 return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col2">' +
130 '<select class="constraint-box'+classn+' form-control" >' + 139 '<select class="rel-constraint-box'+classn+' form-control" >' +
131 '</select>' + 140 '</select>' +
132 '</div>'; 141 '</div>';
133 } 142 }
143 function attr_constraint_html(classn) {
144 return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col2">' +
145 '<select class="attr-constraint-box'+classn+' form-control" >' +
146 '</select>' +
147 '</div>';
148 }
134 function submit_html(classn) { 149 function submit_html(classn) {
135 return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col2">' + 150 return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col3">' +
136 '<div class="form-group'+classn+'">' + 151 '<div class="form-group'+classn+'">' +
137 '<textarea class="form-control" rows="1" id="attribute-text-search'+classn+'">' + 152 '<form onsubmit="return false;"> ' +
138 '</textarea>' + 153 '<input type="text" name="attr-field" class="form-control" id="attr-input'+classn+'">' +
154 '</input>' +
155 '</form>' +
139 '</div>' + 156 '</div>' +
140 '</div>'; 157 '</div>';
141 } 158 }
142 function select_html(selection) { 159 function select_html(selection, n) {
143 return '<div class="row" id="startrow" style="margin-top: 15px">' + 160 return '<div class="row" id="row'+n+'" style="margin-top: 15px">' +
144 '<div class="col-sm-4 col-md-4" id="select-col1"> ' + 161 '<div class="col-sm-4 col-md-4" id="select-col1"> ' +
145 '<select class="selected-object form-control"> ' + 162 '<select class="selected-object form-control"> ' +
146 '<option selected>Selected object is: </option> ' + 163 '<option selected>Selected object is: </option> ' +
147 '</select> ' + 164 '</select> ' +
148 '</div> ' + 165 '</div> ' +
149 '<div class="col-sm-4 col-md-4" id="select-col2"> ' + 166 '<div class="col-sm-4 col-md-4" id="select-col2"> ' +
150 '<select class="select-object1 form-control"> ' + 167 '<h4>' + selection +
151 '<option selected>TEXT</option> ' + 168 '</h4>' +
152 '<option>WITNESS</option> ' +
153 '<option>PERSON</option> ' +
154 '</select> ' +
155 '</div> ' + 169 '</div> ' +
156 '</div>'; 170 '</div>';
157 } 171 }
158 172
159 173
173 console.log("minus filter"); 187 console.log("minus filter");
174 $("#row"+numFilters).remove(); 188 $("#row"+numFilters).remove();
175 numFilters--; 189 numFilters--;
176 }); 190 });
177 $("#results-container").dblclick(function() { 191 $("#results-container").dblclick(function() {
178 if ($("#results-container option:selected").length == 1) { 192 var displayChoice = $("#results-container").find("option:selected").text();
179 var selection = $(this).text(); 193 console.log(displayChoice + ".........");
180 console.log("add filter"); 194 var selection;
181 numFilters++; 195
182 $("#filters").append(select_html(selection, numFilters)); 196 _.map(targetObj, function(obj){
183 // Generate inner relation list 197
184 genRelations(sourceType, ".constraint-box"+classnum); 198 if (_.values(_.values(targetObj))[1] === displayChoice) {
185 199 selection = (_.values(_.values(obj))[0])[1];
186 } 200 }
201 else {
202 selection = (_.values(_.values(obj))[0])[0];
203 }
204 });
205 numFilters++;
206 $("#filters").append(select_html(selection, numFilters));
207 // Generate inner relation list
208 //genRelations(sourceType, ".constraint-box"+numFilters);
209 sourceType = selection;
187 console.log("add filter"); 210 console.log("add filter");
188 }); 211 });
189 212 $(function() {
190 213 $("input").submit(function(event, data) {
191 214 console.log($('.form-control').find("option:selected").text());
192 215 console.log(event);
193 // For use in the onchange events below 216 console.log(data);
194 function genRelations(sourceNodeType, constraintBox) { 217 event.preventDefault();
195 var query = "match (source:"+sourceNodeType+")-[rel]-target return distinct rel.type"; 218
196 ajax1(query, constraintBox); 219 });
197 } 220 });
198 function genResults(sourceNodeType, selected, constraintBox) { 221
199 var query = "match (source:"+sourceNodeType+")-["+selected+"]-(target) return target.label, target.type";
200 ajax1(query, constraintBox);
201 }
202 // Can be used to return a list of all available attributes
203 // TODO: Not implemented
204 function genAttributes(sourceNodeType, constraintBox) {
205 var query = "match (n:"+sourceNodeType+") with keys(n) as collection unwind collection as attributes return distinct attributes";
206 ajax1(query, constraintBox);
207 }
208 222
209 // Change Events 223 // Change Events
210 $('body').on('change', function(event){ 224 $('body').on('change', function(event){
211 var classname = (event.target.className).substr(0, (event.target.className).indexOf(" ")); 225 var classname = (event.target.className).substr(0, (event.target.className).indexOf(" "));
212 var classnum = (event.target.className.match(/\d+\.\d+|\d+\b|\d+(?=\w)/g) || [] ) 226 var classnum = (event.target.className.match(/\d+\.\d+|\d+\b|\d+(?=\w)/g) || [] ).map(function (v) {return +v;}).shift();
213 .map(function (v) {return +v;}).shift(); 227 var allclass = '.'+classname;
214 var selectedOption = $('.'+classname+' option:selected').text(); 228 var selectedOption = $(allclass).find('option:selected').text();
229 console.log("SEL "+ selectedOption);
215 230
216 // Change is on the filter box 231 // Change is on the filter box
217 if (classname.indexOf("filter-box") > -1) { 232 if (classname.indexOf("filter-box") > -1) {
218 if (selectedOption === "has relation") { 233 if (selectedOption === "has relation") {
219 // Remove any other boxes in the row 234 // Remove any other boxes in the row
220 $("#"+classnum+"filter-col2").remove(); 235 $("#"+classnum+"filter-col2").remove();
221 $("#row"+classnum).append(constraint_html(classnum)); 236 $("#row"+classnum).append(constraint_html(classnum));
222 // Generate inner relation list 237 // Generate inner relation list
223 genRelations(sourceType, ".constraint-box"+classnum); 238 genRelations(sourceType, ".rel-constraint-box"+classnum);
224 } 239 }
225 if (selectedOption === "attribute contains") { 240 if (selectedOption === "attribute") {
226 $("#"+classnum+"filter-col2").remove(); 241 $("#"+classnum+"filter-col2").remove();
242 $("#row"+classnum).append(attr_constraint_html(classnum));
243 genAttributes(sourceType, ".attr-constraint-box"+classnum);
244 //$("#row"+classnum).append(submit_html(classnum));
227 $("#row"+classnum).append(submit_html(classnum)); 245 $("#row"+classnum).append(submit_html(classnum));
228 //genAttributes(sourceType, ".constraint-box"+classnum); 246
229 } 247 }
248
249 // NOT IMPLEMENTED
250 /*
230 if (selectedOption === "return") { 251 if (selectedOption === "return") {
231 $("#"+classnum+"filter-col2").remove(); 252 $("#"+classnum+"filter-col2").remove();
232 $("#row"+classnum).append(constraint_html(classnum)); 253 $("#row"+classnum).append(constraint_html(classnum));
233 genAttributes(targetTypes, ".constraint-box"+classnum); 254 genAttributes(targetTypes, ".constraint-box"+classnum);
234 } 255 }
256 */
235 } 257 }
236 258
237 // Change is on the relationship constraint box 259 // Change is on the relationship constraint box
238 if (classname.indexOf("constraint-box") > -1) { 260 if (classname.indexOf("rel-constraint-box") > -1) {
239 //$().remove(); 261 //$().remove();
240 genResults(sourceType, selectedOption, "results-container"); 262 genResults(sourceType, selectedOption, "results-container");
241 } 263 }
242 264
243 // TODO: if the select object is changed update the sourceType var that we are looking at 265 // Change is on the attr constraint box
266 if (classname.indexOf("attr-constraint-box") > -1) {
267 //$().remove();
268 console.log("CHANGE ON ATTR");
269 console.log("changing currentqueryattr to" + selectedOption);
270 currentQueryAttr = selectedOption;
271 console.log("currentQueryAttr is" + currentQueryAttr);
272 //genResults(sourceType, selectedOption, "results-container");
273 }
274
244 // Change is on subject scope 275 // Change is on subject scope
245 if (classname.indexOf("select-object") > -1) { 276 if (classname.indexOf("select-object") > -1) {
277 sourceType = selectedOption;
278 console.log("CHANGE ON SCOPE");
279 genResults(sourceType, "New Source", "results-container");
246 280
247 //ajax1(query, "results-container") 281 //ajax1(query, "results-container")
248 } 282 }
249 }); 283 });
250 284 $("#filters").submit(function (event) {
285 var inputId = $("input:text[name=attr-field]")[0].id;
286 var inputValue = $("input:text[name=attr-field]").val();
287 // Alter results list
288 console.log("QUERY TYPE IS ATTRIBUTE NOW");
289 currentQueryType = "attribute";
290 console.log("SUBMITTED");
291 genResults(sourceType, inputValue, "results-container")
292
293
294 });
295 // For use in the onchange events above
296 function genRelations(sourceNodeType, constraintBox) {
297 console.log("GENERATE ALL RELATIONS");
298 var query = "match (source:"+sourceNodeType+")-[rel]-target return distinct rel.type";
299 console.log("QUERY TYPE IS RELATION NOW");
300 currentQueryType = "relation";
301 ajax1(query, constraintBox);
302 }
303 function genAttributes(sourceNodeType, constraintBox) {
304 console.log("GENERATE ALL ATTRIBUTES");
305 var query = "match (n:"+sourceNodeType+") with keys(n) as collection unwind collection as attributes return distinct attributes";
306 ajax1(query, constraintBox);
307 }
308 function genResults(sourceNodeType, selected, constraintBox) {
309 // TODO: Return first the list of current values of the display box
310
311 if (selected === "New Source") {
312 console.log("NEW SOURCE - UPDATE RESULTS");
313 var query = "match (source:"+sourceNodeType+") return source._n_label, source.type";
314 }
315 else if (currentQueryType === "attribute") {
316 console.log("ATTR CHANGE - UPDATE RESULTS");
317 console.log("before update currentQueryAttr is " + currentQueryAttr);
318 var qAttr = currentQueryAttr;
319 var query = "match (source:"+sourceNodeType+") where "+get_result_labels()+" AND source."+qAttr+"=~\"(?i).*"+selected+".*\" return distinct source._n_label, source.type limit 5";
320 console.log(query);
321 }
322 else if (currentQueryType === "relation") {
323 var query = "match (source:" + sourceNodeType + ")-[rel:" + selected + "]->(target) return distinct target._n_label, target.type";
324 console.log("REL CHANGE - UPDATE RESULTS");
325 }
326 else {
327 console.log("GENRESULTS IMPROPERLY CALLED");
328 return;
329 }
330 ajax1(query, constraintBox);
331 }
332 var currentQueryType;
333 var currentQueryAttr;
334 var resultLabelString = "";
335 function get_result_labels() {
336 return resultLabelString;
337 }
338 function set_result_labels(currentResults) {
339 resultLabelString = "(";
340 for (var i = 0; i < currentResults.length; i++) {
341 if (i > 0) resultLabelString += " OR ";
342 resultLabelString += 'source._n_label=\"'+currentResults[i].text.substr(1)+'\"';
343 }
344 resultLabelString += ")"
345 }
346 // to use contains
347 // TODO: start n = node(*) where n.Name =~ '.*SUBSTRING.*' return n.Name, n;
251 348
252 349
253 350
254 // Ajax request function 351 // Ajax request function
255 function ajax1(q, resBox) { 352 function ajax1(q, resBox) {
256 console.log(q);
257 $.ajaxSetup({ 353 $.ajaxSetup({
258 headers: { 354 headers: {
259 "Authorization": 'Basic ' + window.btoa("neo4j"+":"+"neo5j") 355 "Authorization": 'Basic ' + window.btoa("neo4j"+":"+"neo5j")
260 } 356 }
261 }); 357 });
299 dataGen(a1.data, resBox); 395 dataGen(a1.data, resBox);
300 } 396 }
301 // On success, generate new data 397 // On success, generate new data
302 // TODO: might want to just always return nodes and then deal with the queryData different for each type of return 398 // TODO: might want to just always return nodes and then deal with the queryData different for each type of return
303 function dataGen(dataArr, resBox) { 399 function dataGen(dataArr, resBox) {
304 console.log(dataArr);
305 var d = dataArr; 400 var d = dataArr;
306 targets = []; 401 targets = [];
307 402
308 // Consider implementing localStorage to avoid reloading every time 403 // Consider implementing localStorage to avoid reloading every time
309 var queryData = []; 404 var queryData = [];
316 queryData.sort(function(a,b){ 411 queryData.sort(function(a,b){
317 var c = a.text.replace(/<|>/g, ''); 412 var c = a.text.replace(/<|>/g, '');
318 var d = b.text.replace(/<|>/g, ''); 413 var d = b.text.replace(/<|>/g, '');
319 return c.localeCompare(d); 414 return c.localeCompare(d);
320 }); 415 });
321 console.log(queryData[0].text);
322 416
323 // Change the displayed results 417 // Change the displayed results
324 if (resBox === "results-container") { 418 if (resBox === "results-container") {
325 process_display_results(queryData); 419 process_display_results(queryData);
326 } 420 }
421 // Or fill any select boxes
327 else { 422 else {
328 $(resBox).select2({ 423 $(resBox).select2({
329 data: queryData 424 data: queryData
330 }); 425 });
331 } 426 }
332 } 427 }
333 function process_display_results(queryData) { 428 function process_display_results(queryData) {
334 var weights = []; 429 var weights = [];
335 var resultLength = queryData.length; 430 var resultLength = queryData.length;
336 targetTypes = []; 431 targetTypes = [];
337 432 targetObj = queryData;
433 set_result_labels(queryData);
338 $('#results-container') 434 $('#results-container')
339 .find('option') 435 .find('option')
340 .remove() 436 .remove()
341 .end() 437 .end()
342 ; 438 ;
439 for (var i = 0; i < targets.length; i++) {
440 if (targetTypes.indexOf(targets[i]) == -1) {
441 targetTypes.push(targets[i]);
442 weights.push(1);
443 }
444 else {
445 weights[targetTypes.indexOf(targets[i])] += 1;
446 }
447 }
343 if (resultLength < listTolerance) { 448 if (resultLength < listTolerance) {
344 for (var i = 0; i < resultLength; i++) { 449 for (var i = 0; i < resultLength; i++) {
345 // TODO: put this jQuery call into its own function to be called if the else statement occurs 450 // TODO: put this jQuery call into its own function to be called if the else statement occurs
346 // TODO: but the user actually does want to see the entire huge list. 451 // TODO: but the user actually does want to see the entire huge list.
452 // This next bit removes duplicate labels if we want to use that for things like cities
453 // would this throw issues with other things??
454 //$('#results-container option:contains('+queryData[i].text+')').each(function(){
455 // $(this).remove();
456 //});
347 $('#results-container') 457 $('#results-container')
348 .append($("<option></option>") 458 .append($("<option></option>")
349 .attr("value", i) 459 .attr("value", i)
350 .text(queryData[i].text)); 460 .text(queryData[i].text));
351 } 461 }
352 } 462 }
353 else { 463 else {
354 for (var i = 0; i < targets.length; i++) {
355 if (targetTypes.indexOf(targets[i]) == -1) {
356 targetTypes.push(targets[i]);
357 weights.push(1);
358 }
359 else {
360 weights[targetTypes.indexOf(targets[i])] += 1;
361 }
362 }
363 for (var i = 0; i < targetTypes.length; i++) { 464 for (var i = 0; i < targetTypes.length; i++) {
364 $('#results-container') 465 $('#results-container')
365 .append($("<option></option>") 466 .append($("<option></option>")
366 .text(targetTypes[i] + " [ " + weights[i] + " ]")); 467 .text(targetTypes[i] + " [ " + weights[i] + " ]"));
367 } 468 }