Create a gist now

Instantly share code, notes, and snippets.

ES Function Score Visualization

ES Function Score Visualization

Shows the score curves when given different settings for the three types of scoring functions available in Elasticsearch.

A Pen by Xiao Yu on CodePen.

License.

<form id="chart_control" style="display:none">
<div>
<label for="offset">Offset (days):</label>
<input id="offset" type="number" min="0" max="365" step="1" />
</div>
<div>
<label for="scale">Scale (days):</label>
<input id="scale" type="number" min="0" max="365" step="1" />
</div>
<div class="break">
<label for="decay">Decay:</label>
<input id="decay" type="number" min="0.000001" max="0.999999" step="0.000001" />
</div>
<div>
<input type="submit" value="Update Graph" />
</div>
</form>
<div class="break">
<canvas id="chart" width="600" height="220"></canvas>
</div>
( function( w ) {
var day_ms = 86400000;
var config = {
origin : 365 * day_ms, // 1971-01-01
offset : 2419200000, // 28 days
scale : 4838400000, // 56 days
decay : 0.5
};
var scorer = {
_distance: function( val ) {
return Math.max( 0, Math.abs( val - config.origin ) - config.offset );
},
gauss: function( val ) {
var segma_sq = -1 * Math.pow( config.scale, 2 ) / ( 2 * Math.log( config.decay ) );
return Math.exp( -1 * ( Math.pow( scorer._distance( val ), 2 ) / ( 2 * segma_sq ) ) )
},
exp: function( val ) {
var lambda = Math.log( config.decay ) / config.scale;
return Math.exp( lambda * scorer._distance( val ) );
},
linear: function( val ) {
var s = config.scale / ( 1 - config.decay );
return Math.max( ( ( s - scorer._distance( val ) ) / s ), 0 );
}
}
w.day_scorer = {
set_configs: function( offset_days, scale_days, decay ) {
config.offset = day_ms * offset_days;
config.scale = day_ms * scale_days;
config.decay = decay;
},
get_configs: function() {
return {
offset : config.offset / day_ms,
scale : config.scale / day_ms,
decay : config.decay,
}
},
// Calc scores for range of days defaults to a year
calc_days: function( first, last ) {
// Origin is always last day of range
config.origin = last * day_ms;
var s = {
gauss : [],
exp : [],
linear : []
};
for ( var d = first; d <= last; d++ ) {
s.gauss.push( scorer.gauss( d * day_ms ) );
s.exp.push( scorer.exp( d * day_ms ) );
s.linear.push( scorer.linear( d * day_ms ) );
}
return s;
}
};
} )( window );
( function( w, d ) {
// Set default chart configs
Chart.defaults.global.responsive = true;
Chart.defaults.global.scaleOverride = true;
Chart.defaults.global.scaleSteps = 4;
Chart.defaults.global.scaleStepWidth = 0.25;
Chart.defaults.global.scaleStartValue = 0;
Chart.defaults.global.tooltipXOffset = 0;
Chart.defaults.global.multiTooltipTemplate = "<%= value.toFixed(3) %><%if (datasetLabel){%> - <%=datasetLabel%><%}%>";
var first = 0,
last = 365;
var get_labels = function( all ) {
var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
var labels = [];
for ( var day = first; day <= last; day++ ) {
var date = new Date( 86400000 * day );
if ( all || date.getUTCDate() == 1 ) {
labels.push( months[ date.getUTCMonth() ] + ' ' + date.getUTCDate() );
} else {
labels.push( '' );
}
}
return labels;
};
var get_filler = function() {
var filler = new Array( 1 + last - first );
filler.fill( 0 );
return filler;
};
var update_chart = function() {
var scores = w.day_scorer.calc_days( first, last );
var labels = get_labels( true );
[ 'gauss', 'exp', 'linear' ].forEach( function( type, i ) {
scores[ type ].forEach( function( s, j ) {
graph.datasets[ i ].points[ j ].value = s;
graph.datasets[ i ].points[ j ].label = labels[ j ];
} );
} );
graph.update()
}
var graph = null,
ctx = null,
data = {
labels: get_labels( false ),
datasets: [
{
label: "gauss",
fillColor: "rgba( 0, 135, 190, 0.05 )",
strokeColor: "rgba( 0, 135, 190, 1 )",
pointColor: "rgba( 0, 135, 190, 1)",
data: get_filler()
},
{
label: "exp",
fillColor: "rgba( 213, 78, 33, 0.05 )",
strokeColor: "rgba( 213, 78, 33, 1 )",
pointColor: "rgba( 213, 78, 33, 1 )",
data: get_filler()
},
{
label: "linear",
fillColor: "rgba( 168, 190, 206, 0.05 )",
strokeColor: "rgba( 168, 190, 206, 1 )",
pointColor: "rgba( 168, 190, 206, 1 )",
data: get_filler()
}
]
},
options = {
scaleShowVerticalLines: false,
pointDot: false,
pointHitDetectionRadius: 1
},
ids = {
chart : '',
offset : '',
scale : '',
decay : ''
};
w.decay_chart = {
init: function( chart_id, offset_id, scale_id, decay_id ) {
// Store IDs
ids = {
chart : chart_id,
offset : offset_id,
scale : scale_id,
decay : decay_id
}
// Set UI to default values
var configs = w.day_scorer.get_configs();
d.getElementById( ids.offset ).value = configs.offset;
d.getElementById( ids.scale ).value = configs.scale;
d.getElementById( ids.decay ).value = configs.decay;
ctx = d.getElementById( ids.chart ).getContext( "2d" );
graph = new Chart( ctx ).Line( data, options );
update_chart();
},
update: function() {
// Check new values from UI
var valid = true;
[ ids.offset, ids.scale, ids.decay ].forEach( function( id ) {
var elem = d.getElementById( id );
if ( false == elem.validity.valid ) {
valid = false;
if ( 'function' == typeof elem.reportValidity ) {
elem.reportValidity();
}
elem.className = "invalid";
} else {
elem.className = "";
}
} );
if ( !valid ) {
return;
}
w.day_scorer.set_configs(
parseInt( d.getElementById( ids.offset ).value ),
parseInt( d.getElementById( ids.scale ).value ),
parseFloat( d.getElementById( ids.decay ).value )
);
update_chart();
}
}
} )( window, document );
window.onload = function() {
var elem = document.getElementById( 'chart_control' );
elem.addEventListener( 'submit', function( event ) {
event.preventDefault();
window.decay_chart.update();
} );
elem.style.display = '';
window.decay_chart.init( 'chart', 'offset', 'scale', 'decay' );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.1.1/Chart.js"></script>
body {
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 13px;
}
label, input {
margin-bottom: 0.25em;
width: 7em;
display: block;
float: left;
}
input[type="number"] {
text-align: right;
}
input[type="submit"] {
width: 10em;
}
form div {
width: 14em;
float: left;
}
div.break {
clear: left;
}
.invalid {
color: #d94f4f;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment