Mercurial > hg > NetworkVis
view 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 |
line wrap: on
line source
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html" charset="UTF-8"> <title>Query Builder</title> <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <link rel="stylesheet" href="select2-4.0.1/dist/css/select2.min.css"> <script type="text/javascript" src="js/d3.min.js"></script> <script type="text/javascript" src="js/underscore-min.js"></script> <script type="text/javascript" src="select2-4.0.1/vendor/jquery-2.1.0.js"></script> <script type="text/javascript" src="select2-4.0.1/dist/js/select2.full.min.js"></script> </head> <body style="background:none;"> <div role="navigation" class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="row"> <div class="col-sm-6 col-md-6"> <ul class="nav navbar-nav"> </ul> </div> <div class="navbar-header col-sm-6 col-md-6" style="height: auto;"> <div class="col-md-6"> <div class="navbar-brand"> <div class="brand">ISMI Query Builder</div> </div> </div> <div class="col-md-offset-6"> <div class="logo-well" style="height: 60%; width: 60%;"> <a href="//neo4j.com/developer-resources"> <img src="//neo4j-contrib.github.io/developer-resources/language-guides/assets/img/logo-white.svg" alt="Neo4j World's Leading Graph Database" id="logo" style="max-height: 50%; width: 50%"> </a> </div> </div> </div> </div> </div> </div> <div class="container"> <div class="row" style="width: 95%"> <div class="col-lg"> <div class="panel panel-default"> <div class="panel-heading" id="title">Query Builder</div> <section> <div class="s2-example"> <div id="filters"> <div class="row" id="startrow" style="margin-top: 15px"> <div class="col-sm-4 col-md-4" id="select-col1"> <select class="selected-object form-control"> <option selected>Object type : </option> </select> </div> <div class="col-sm-4 col-md-4" id="select-col2"> <select class="select-object1 form-control"> <option selected>TEXT</option> <option>WITNESS</option> <option>PERSON</option> <option>CODEX</option> <option>PLACE</option> <option>REPOSITORY</option> <option>COLLECTION</option> </select> </div> </div> <div class="row" id="row1" style="margin-top: 15px"> <div class="col-sm-4 col-md-4" id="1filter-col1"> <select class="filter-box1 form-control"> <option value="" disabled selected>Choose filter...</option> </select> </div> </div> </div> <div align="left" style="margin-top: 30px"><button id="minus_filter_button" class="btn btn-warning btn-md" type="button"> - </button> <button id="plus_filter_button" class="btn btn-warning btn-md" type="button"> + </button> </div> </div> <div class="container-fluid" style="margin-top: 15px"> <form role="form"> <div class="form-group"> <label for="results-container">Results list</label> <select multiple class="form-control" id="results-container" style="min-height: 300px;"> </select> </div> </form> </div> <!-- <pre data-fill-from=".js-code-data-array"></pre> --> </section> </div> </div> </div> </div> <script type="text/javascript" class="js-code-data-array"> //var queryData = []; // Defining initial variables and helper functions var sourceType = "TEXT"; var targets = []; var targetTypes = []; var targetObj = []; // The current objects on the display box var numFilters = 1; var filters = [ { id: 0, text: 'has relation' }, { id: 1, text: 'attribute' } ]; $(".filter-box"+numFilters).select2({ // Initialize first filter box data: filters, minimumResultsForSearch: Infinity }); var listTolerance = 100; //$("") function filter_html(n) { return '<div class="row" id="row'+n+'" style="margin-top: 15px">' + '<div class="col-sm-4 col-md-4" id="'+n+'filter-col1">' + '<select class="filter-box'+n+' form-control">' + '<option value="" disabled selected>Choose filter...</option>' + '</select>' + '</div>' + '</div>'; } function constraint_html(classn) { return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col2">' + '<select class="rel-constraint-box'+classn+' form-control" >' + '</select>' + '</div>'; } function attr_constraint_html(classn) { return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col2">' + '<select class="attr-constraint-box'+classn+' form-control" >' + '</select>' + '</div>'; } function submit_html(classn) { return '<div class="col-sm-4 col-md-4" id="'+classn+'filter-col3">' + '<div class="form-group'+classn+'">' + '<form onsubmit="return false;"> ' + '<input type="text" name="attr-field" class="form-control" id="attr-input'+classn+'">' + '</input>' + '</form>' + '</div>' + '</div>'; } function select_html(selection, n) { return '<div class="row" id="row'+n+'" style="margin-top: 15px">' + '<div class="col-sm-4 col-md-4" id="select-col1"> ' + '<select class="selected-object form-control"> ' + '<option selected>Selected object is: </option> ' + '</select> ' + '</div> ' + '<div class="col-sm-4 col-md-4" id="select-col2"> ' + '<h4>' + selection + '</h4>' + '</div> ' + '</div>'; } // Button filters $("#plus_filter_button").click(function() { console.log("add filter"); numFilters++; $("#filters").append(filter_html(numFilters)); $(".filter-box"+numFilters).select2({ data: filters, minimumResultsForSearch: Infinity }); }); $("#minus_filter_button").click(function() { console.log("minus filter"); $("#row"+numFilters).remove(); numFilters--; }); $("#results-container").dblclick(function() { var displayChoice = $("#results-container").find("option:selected").text(); console.log(displayChoice + "........."); var selection; _.map(targetObj, function(obj){ if (_.values(_.values(targetObj))[1] === displayChoice) { selection = (_.values(_.values(obj))[0])[1]; } else { selection = (_.values(_.values(obj))[0])[0]; } }); numFilters++; $("#filters").append(select_html(selection, numFilters)); // Generate inner relation list //genRelations(sourceType, ".constraint-box"+numFilters); sourceType = selection; console.log("add filter"); }); $(function() { $("input").submit(function(event, data) { console.log($('.form-control').find("option:selected").text()); console.log(event); console.log(data); event.preventDefault(); }); }); // Change Events $('body').on('change', function(event){ var classname = (event.target.className).substr(0, (event.target.className).indexOf(" ")); var classnum = (event.target.className.match(/\d+\.\d+|\d+\b|\d+(?=\w)/g) || [] ).map(function (v) {return +v;}).shift(); var allclass = '.'+classname; var selectedOption = $(allclass).find('option:selected').text(); console.log("SEL "+ selectedOption); // Change is on the filter box if (classname.indexOf("filter-box") > -1) { if (selectedOption === "has relation") { // Remove any other boxes in the row $("#"+classnum+"filter-col2").remove(); $("#row"+classnum).append(constraint_html(classnum)); // Generate inner relation list genRelations(sourceType, ".rel-constraint-box"+classnum); } if (selectedOption === "attribute") { $("#"+classnum+"filter-col2").remove(); $("#row"+classnum).append(attr_constraint_html(classnum)); genAttributes(sourceType, ".attr-constraint-box"+classnum); //$("#row"+classnum).append(submit_html(classnum)); $("#row"+classnum).append(submit_html(classnum)); } // NOT IMPLEMENTED /* if (selectedOption === "return") { $("#"+classnum+"filter-col2").remove(); $("#row"+classnum).append(constraint_html(classnum)); genAttributes(targetTypes, ".constraint-box"+classnum); } */ } // Change is on the relationship constraint box if (classname.indexOf("rel-constraint-box") > -1) { //$().remove(); genResults(sourceType, selectedOption, "results-container"); } // Change is on the attr constraint box if (classname.indexOf("attr-constraint-box") > -1) { //$().remove(); console.log("CHANGE ON ATTR"); console.log("changing currentqueryattr to" + selectedOption); currentQueryAttr = selectedOption; console.log("currentQueryAttr is" + currentQueryAttr); //genResults(sourceType, selectedOption, "results-container"); } // Change is on subject scope if (classname.indexOf("select-object") > -1) { sourceType = selectedOption; console.log("CHANGE ON SCOPE"); genResults(sourceType, "New Source", "results-container"); //ajax1(query, "results-container") } }); $("#filters").submit(function (event) { var inputId = $("input:text[name=attr-field]")[0].id; var inputValue = $("input:text[name=attr-field]").val(); // Alter results list console.log("QUERY TYPE IS ATTRIBUTE NOW"); currentQueryType = "attribute"; console.log("SUBMITTED"); genResults(sourceType, inputValue, "results-container") }); // For use in the onchange events above function genRelations(sourceNodeType, constraintBox) { console.log("GENERATE ALL RELATIONS"); var query = "match (source:"+sourceNodeType+")-[rel]-target return distinct rel.type"; console.log("QUERY TYPE IS RELATION NOW"); currentQueryType = "relation"; ajax1(query, constraintBox); } function genAttributes(sourceNodeType, constraintBox) { console.log("GENERATE ALL ATTRIBUTES"); var query = "match (n:"+sourceNodeType+") with keys(n) as collection unwind collection as attributes return distinct attributes"; ajax1(query, constraintBox); } function genResults(sourceNodeType, selected, constraintBox) { // TODO: Return first the list of current values of the display box if (selected === "New Source") { console.log("NEW SOURCE - UPDATE RESULTS"); var query = "match (source:"+sourceNodeType+") return source._n_label, source.type"; } else if (currentQueryType === "attribute") { console.log("ATTR CHANGE - UPDATE RESULTS"); console.log("before update currentQueryAttr is " + currentQueryAttr); var qAttr = currentQueryAttr; var query = "match (source:"+sourceNodeType+") where "+get_result_labels()+" AND source."+qAttr+"=~\"(?i).*"+selected+".*\" return distinct source._n_label, source.type limit 5"; console.log(query); } else if (currentQueryType === "relation") { var query = "match (source:" + sourceNodeType + ")-[rel:" + selected + "]->(target) return distinct target._n_label, target.type"; console.log("REL CHANGE - UPDATE RESULTS"); } else { console.log("GENRESULTS IMPROPERLY CALLED"); return; } ajax1(query, constraintBox); } var currentQueryType; var currentQueryAttr; var resultLabelString = ""; function get_result_labels() { return resultLabelString; } function set_result_labels(currentResults) { resultLabelString = "("; for (var i = 0; i < currentResults.length; i++) { if (i > 0) resultLabelString += " OR "; resultLabelString += 'source._n_label=\"'+currentResults[i].text.substr(1)+'\"'; } resultLabelString += ")" } // to use contains // TODO: start n = node(*) where n.Name =~ '.*SUBSTRING.*' return n.Name, n; // Ajax request function function ajax1(q, resBox) { $.ajaxSetup({ headers: { "Authorization": 'Basic ' + window.btoa("neo4j"+":"+"neo5j") } }); return $.ajax({ type: "POST", url: "https://ismi-dev.mpiwg-berlin.mpg.de/neo4j-ismi/db/data/cypher", accepts: "application/json", dataType: "json", data: { "query": q, "params": {} }, beforeSend: function (xhr) { xhr.setRequestHeader ("Authorization", "Basic " + btoa('neo4j'+":"+'neo5j')); }, processResults: function (data, params) { // parse the results into the format expected by Select2 // since we are using custom formatting functions we do not need to // alter the remote JSON data, except to indicate that infinite // scrolling can be used params.page = params.page || 1; return { pagination: { more: (params.page * 30) < data.length } }; }, success: function (res, textStatus, jqXHR) { ajaxCheck(res, resBox, textStatus); console.log(textStatus); }, error: function (jqXHR, textStatus, errorThrown) { console.log(textStatus); } }); } // Check if request was successful function ajaxCheck(a1, resBox, status){ if (status === 'error') console.log("error: bad ajax request"); //else dataGen(a1); dataGen(a1.data, resBox); } // On success, generate new data // TODO: might want to just always return nodes and then deal with the queryData different for each type of return function dataGen(dataArr, resBox) { var d = dataArr; targets = []; // Consider implementing localStorage to avoid reloading every time var queryData = []; for (var i= 0; i<d.length; i++) { var j = d[i]; queryData.push({ id: j, text: j[0] }); targets.push(j[1]); } // Now that queryData array has been defined we will sort it by its label value and initialize the option. queryData.sort(function(a,b){ var c = a.text.replace(/<|>/g, ''); var d = b.text.replace(/<|>/g, ''); return c.localeCompare(d); }); // Change the displayed results if (resBox === "results-container") { process_display_results(queryData); } // Or fill any select boxes else { $(resBox).select2({ data: queryData }); } } function process_display_results(queryData) { var weights = []; var resultLength = queryData.length; targetTypes = []; targetObj = queryData; set_result_labels(queryData); $('#results-container') .find('option') .remove() .end() ; for (var i = 0; i < targets.length; i++) { if (targetTypes.indexOf(targets[i]) == -1) { targetTypes.push(targets[i]); weights.push(1); } else { weights[targetTypes.indexOf(targets[i])] += 1; } } if (resultLength < listTolerance) { for (var i = 0; i < resultLength; i++) { // TODO: put this jQuery call into its own function to be called if the else statement occurs // TODO: but the user actually does want to see the entire huge list. // This next bit removes duplicate labels if we want to use that for things like cities // would this throw issues with other things?? //$('#results-container option:contains('+queryData[i].text+')').each(function(){ // $(this).remove(); //}); $('#results-container') .append($("<option></option>") .attr("value", i) .text(queryData[i].text)); } } else { for (var i = 0; i < targetTypes.length; i++) { $('#results-container') .append($("<option></option>") .text(targetTypes[i] + " [ " + weights[i] + " ]")); } } } </script> </body> </html>