Add fuzzy search to /graph textarea (#2081)

* Add fuzzy search to /graph textarea

We have a few thousands different metrics and looking up some of them
can be quite annoying with the simple string matching.

This patch adds a fuzzy search to the textarea lookup box on the /graph
page. It uses a small neat library from github.com/mattyork/fuzzy.

* Add fuzzy lib to NOTICE and re-build assets

Previously built assets changed the mode.
This commit is contained in:
Or Cohen 2016-10-17 10:37:11 +03:00 committed by Fabian Reinartz
parent 630b96c5f3
commit 50f8e35c54
5 changed files with 130 additions and 39 deletions

6
NOTICE
View file

@ -18,6 +18,12 @@ Original written by @mdo and @fat
Copyright 2014 Bass Jobsen @bassjobsen
Licensed under the Apache License, Version 2.0
fuzzy
https://github.com/mattyork/fuzzy
Original written by @mattyork
Copyright 2012 Matt York
Licensed under the MIT License
bootstrap-datetimepicker.js
http://www.eyecon.ro/bootstrap-datepicker
Copyright 2012 Stefan Petre

File diff suppressed because one or more lines are too long

View file

@ -196,9 +196,44 @@ Prometheus.Graph.prototype.populateInsertableMetrics = function() {
self.insertMetric[0].options.add(new Option(metrics[i], metrics[i]));
}
self.fuzzyResult = {
query: null,
result: null,
map: {}
}
self.expr.typeahead({
source: metrics,
items: "all"
items: "all",
matcher: function(item) {
// If we have result for current query, skip
if (!self.fuzzyResult.query || self.fuzzyResult.query !== this.query) {
self.fuzzyResult.query = this.query;
self.fuzzyResult.map = {};
self.fuzzyResult.result = fuzzy.filter(this.query.replace('_', ' '), metrics, {
pre: '<strong>',
post: '</strong>',
extract: function(el) { return el.replace('_', ' ') }
});
self.fuzzyResult.result.forEach(function(r) {
self.fuzzyResult.map[r.original] = r;
});
}
return item in self.fuzzyResult.map;
},
sorter: function(items) {
items.sort(function(a,b) {
var i = self.fuzzyResult.map[b].score - self.fuzzyResult.map[a].score;
return i === 0 ? a.localeCompare(b) : i;
});
return items;
},
highlighter: function (item) {
return $('<div>' + self.fuzzyResult.map[item].string.replace(' ', '_') + '</div>')
},
});
// This needs to happen after attaching the typeahead plugin, as it
// otherwise breaks the typeahead functionality.

26
web/ui/static/vendor/fuzzy.js vendored Normal file
View file

@ -0,0 +1,26 @@
// Copyright (c) 2012 Matt York
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// https://github.com/mattyork/fuzzy/blob/3613086aa40c180ca722aeaf48cef575dc57eb5d/fuzzy-min.js
(function(){var root=this;var fuzzy={};if(typeof exports!=="undefined"){module.exports=fuzzy}else{root.fuzzy=fuzzy}fuzzy.simpleFilter=function(pattern,array){return array.filter(function(str){return fuzzy.test(pattern,str)})};fuzzy.test=function(pattern,str){return fuzzy.match(pattern,str)!==null};fuzzy.match=function(pattern,str,opts){opts=opts||{};var patternIdx=0,result=[],len=str.length,totalScore=0,currScore=0,pre=opts.pre||"",post=opts.post||"",compareString=opts.caseSensitive&&str||str.toLowerCase(),ch;pattern=opts.caseSensitive&&pattern||pattern.toLowerCase();for(var idx=0;idx<len;idx++){ch=str[idx];if(compareString[idx]===pattern[patternIdx]){ch=pre+ch+post;patternIdx+=1;currScore+=1+currScore}else{currScore=0}totalScore+=currScore;result[result.length]=ch}if(patternIdx===pattern.length){totalScore=compareString===pattern?Infinity:totalScore;return{rendered:result.join(""),score:totalScore}}return null};fuzzy.filter=function(pattern,arr,opts){if(!arr||arr.length===0){return[]}if(typeof pattern!=="string"){return arr}opts=opts||{};return arr.reduce(function(prev,element,idx,arr){var str=element;if(opts.extract){str=opts.extract(element)}var rendered=fuzzy.match(pattern,str,opts);if(rendered!=null){prev[prev.length]={string:rendered.rendered,score:rendered.score,index:idx,original:element}}return prev},[]).sort(function(a,b){var compare=b.score-a.score;if(compare)return compare;return a.index-b.index})}})();

View file

@ -9,6 +9,7 @@
<script src="{{ pathPrefix }}/static/vendor/rickshaw/rickshaw.min.js"></script>
<script src="{{ pathPrefix }}/static/vendor/bootstrap-datetimepicker/bootstrap-datetimepicker.js"></script>
<script src="{{ pathPrefix }}/static/vendor/bootstrap3-typeahead/bootstrap3-typeahead.min.js"></script>
<script src="{{ pathPrefix }}/static/vendor/fuzzy.js"></script>
<script src="{{ pathPrefix }}/static/vendor/js/handlebars.js"></script>
<script src="{{ pathPrefix }}/static/vendor/js/jquery.selection.js"></script>