Mercurial > hg > NetworkVis
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 } |