Updating to use features instead of keywords
This commit is contained in:
parent
bfadd2038b
commit
8b7759a82a
@ -16,7 +16,6 @@
|
|||||||
"bootstrap": "~3.2.0",
|
"bootstrap": "~3.2.0",
|
||||||
"handlebars": "~1.3.0",
|
"handlebars": "~1.3.0",
|
||||||
"underscore": "~1.6.0",
|
"underscore": "~1.6.0",
|
||||||
"bootstrap-select": "~1.6.2",
|
|
||||||
"tinycolor": "~1.0.0",
|
"tinycolor": "~1.0.0",
|
||||||
"seiyria-bootstrap-slider": "~4.0.1",
|
"seiyria-bootstrap-slider": "~4.0.1",
|
||||||
"snap.svg": "~0.3.0"
|
"snap.svg": "~0.3.0"
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- query input -->
|
<!-- query input -->
|
||||||
<div id="input" class="form-horizontal">
|
<div class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="keywordsToSearch" class="col-md-2 control-label">Keywords</label>
|
<label for="keywordsToSearch" class="col-md-2 control-label">Keywords</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<select id="keywordsToSearch" class="form-control" multiple="multiple" data-max-options="4" data-live-search="data-live-search"></select>
|
<select id="keywordsToSearch" class="form-control"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -39,13 +39,6 @@
|
|||||||
<input class="form-control" type="number" value="100" id="maxResults">
|
<input class="form-control" type="number" value="100" id="maxResults">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-md-offset-2 col-md-10">
|
|
||||||
<button class="btn btn-primary" id="searchKeywords" type="button" disabled="disabled">
|
|
||||||
<span class="glyphicon glyphicon-search"></span> Search
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- options dialog -->
|
<!-- options dialog -->
|
||||||
@ -113,9 +106,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- query output -->
|
<!-- query output -->
|
||||||
<div id="output" style="display: none;">
|
<div>
|
||||||
<!-- semantic tweaker -->
|
<!-- semantic tweaker -->
|
||||||
<div class="panel panel-default unselectable">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<big>Semantic tweaks to: <span id="query"></span></big>
|
<big>Semantic tweaks to: <span id="query"></span></big>
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
|
@ -27,15 +27,16 @@
|
|||||||
(function(hscd) {
|
(function(hscd) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ctx = {};
|
var ctx = {
|
||||||
var log = [];
|
log: []
|
||||||
|
};
|
||||||
|
|
||||||
function onAdjust(name, value) {
|
function onAdjust(name, value) {
|
||||||
ctx.searchParams[name] = value;
|
ctx.features[name] = value;
|
||||||
|
|
||||||
var query = {
|
var query = {
|
||||||
searchParams: ctx.searchParams,
|
features: ctx.features,
|
||||||
searchRange: ctx.searchRange,
|
range: ctx.range,
|
||||||
minScore: ctx.minScore,
|
minScore: ctx.minScore,
|
||||||
hintSteps: ctx.hintSteps,
|
hintSteps: ctx.hintSteps,
|
||||||
maxResults: ctx.maxResults
|
maxResults: ctx.maxResults
|
||||||
@ -49,25 +50,75 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSearch() {
|
function onReady() {
|
||||||
var keywords = $('#keywordsToSearch').val() || [];
|
$('#history').on('slideStop', onSelectSnapshot);
|
||||||
var searchParams = {};
|
$('#history').slider({
|
||||||
|
formatter: function(value) {
|
||||||
|
var delta = ctx.log.length - (value + 1);
|
||||||
|
switch (delta) {
|
||||||
|
case 0:
|
||||||
|
return 'Most recent query';
|
||||||
|
case 1:
|
||||||
|
return 'Previous query';
|
||||||
|
default:
|
||||||
|
return String(delta) + ' queries back';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#forgetKeyword').click(onForget);
|
||||||
|
$('#forgetDialog').on('show.bs.modal', function() {
|
||||||
|
$('#forgetError').hide();
|
||||||
|
$.getJSON('/get_keywords', function(keywords) {
|
||||||
|
$('#keywordToForget').empty();
|
||||||
for (var i = 0, count = keywords.length; i < count; ++i) {
|
for (var i = 0, count = keywords.length; i < count; ++i) {
|
||||||
searchParams[keywords[i]] = 1.0;
|
$('#keywordToForget').append($('<option></option>', {
|
||||||
|
value: keywords[i],
|
||||||
|
text: keywords[i]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#learnKeyword').click(onLearn);
|
||||||
|
$('#learnDialog').on('show.bs.modal', function() {
|
||||||
|
$('#learnError').hide();
|
||||||
|
$('#learnKeyword').prop('disabled', true);
|
||||||
|
$('#keywordToLearn').val('');
|
||||||
|
});
|
||||||
|
$('#keywordToLearn').bind('input', function() {
|
||||||
|
$('#learnKeyword').prop('disabled', !$(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
$.getJSON('/get_keywords', function(keywords) {
|
||||||
|
ctx.keywords = keywords;
|
||||||
|
for (var keyword in keywords) {
|
||||||
|
$('#keywordsToSearch').append($('<option></option>', { value: keyword, text: keyword }));
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSearch() {
|
||||||
|
var keyword = $('#keywordsToSearch').val();
|
||||||
|
var features = {};
|
||||||
|
|
||||||
|
for (var feature in ctx.keywords[keyword]) {
|
||||||
|
features[feature] = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = {
|
var query = {
|
||||||
searchParams: searchParams,
|
features: features,
|
||||||
searchRange: { min: -1.0, max: 1.0 },
|
range: { min: -1.0, max: 1.0 },
|
||||||
minScore: parseFloat($('#minScore').val()),
|
minScore: parseFloat($('#minScore').val()),
|
||||||
hintSteps: parseInt($('#hintSteps').val()),
|
hintSteps: parseInt($('#hintSteps').val()),
|
||||||
maxResults: parseInt($('#maxResults').val())
|
maxResults: parseInt($('#maxResults').val())
|
||||||
};
|
};
|
||||||
|
|
||||||
$.getJSON('/search', query, function(results) {
|
$.getJSON('/search', query, function(results) {
|
||||||
ctx.searchParams = query.searchParams;
|
ctx.features = query.features;
|
||||||
ctx.searchRange = query.searchRange;
|
ctx.range = query.range;
|
||||||
ctx.minScore = query.minScore;
|
ctx.minScore = query.minScore;
|
||||||
ctx.hintSteps = query.hintSteps;
|
ctx.hintSteps = query.hintSteps;
|
||||||
ctx.maxResults = query.maxResults;
|
ctx.maxResults = query.maxResults;
|
||||||
@ -75,7 +126,7 @@
|
|||||||
ctx.grapher = new grapher.Grapher({
|
ctx.grapher = new grapher.Grapher({
|
||||||
canvas: new Snap('#svg'),
|
canvas: new Snap('#svg'),
|
||||||
steps: ctx.hintSteps,
|
steps: ctx.hintSteps,
|
||||||
range: ctx.searchRange,
|
range: ctx.range,
|
||||||
onValueChanged: onAdjust,
|
onValueChanged: onAdjust,
|
||||||
useLocalScale: true,
|
useLocalScale: true,
|
||||||
useRelativeScale: true
|
useRelativeScale: true
|
||||||
@ -85,7 +136,7 @@
|
|||||||
saveSnapshot(results);
|
saveSnapshot(results);
|
||||||
outputMatches(results.items, results.count);
|
outputMatches(results.items, results.count);
|
||||||
|
|
||||||
$('#query').text(keywords.join(', '));
|
$('#query').text(keyword);
|
||||||
$('#useLocalScale').click(function() {
|
$('#useLocalScale').click(function() {
|
||||||
var useLocalScale = $('#useLocalScale').is(':checked');
|
var useLocalScale = $('#useLocalScale').is(':checked');
|
||||||
ctx.grapher.setUseLocalScale(useLocalScale);
|
ctx.grapher.setUseLocalScale(useLocalScale);
|
||||||
@ -94,9 +145,6 @@
|
|||||||
var useRelativeScale = $('#useRelativeScale').is(':checked');
|
var useRelativeScale = $('#useRelativeScale').is(':checked');
|
||||||
ctx.grapher.setUseRelativeScale(useRelativeScale);
|
ctx.grapher.setUseRelativeScale(useRelativeScale);
|
||||||
});
|
});
|
||||||
$('#input').fadeOut(function() {
|
|
||||||
$('#output').fadeIn();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +153,7 @@
|
|||||||
$('#learnError').slideUp(function() {
|
$('#learnError').slideUp(function() {
|
||||||
var query = {
|
var query = {
|
||||||
keyword: $('#keywordToLearn').val(),
|
keyword: $('#keywordToLearn').val(),
|
||||||
params: ctx.searchParams
|
params: ctx.features
|
||||||
};
|
};
|
||||||
|
|
||||||
$.getJSON('/add_keyword', query, function(results) {
|
$.getJSON('/add_keyword', query, function(results) {
|
||||||
@ -143,13 +191,13 @@
|
|||||||
|
|
||||||
function onSelectSnapshot() {
|
function onSelectSnapshot() {
|
||||||
var index = $('#history').slider('getValue');
|
var index = $('#history').slider('getValue');
|
||||||
outputSnapshot(log[index]);
|
outputSnapshot(ctx.log[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveSnapshot(results) {
|
function saveSnapshot(results) {
|
||||||
log.push(results);
|
ctx.log.push(results);
|
||||||
|
|
||||||
var count = log.length;
|
var count = ctx.log.length;
|
||||||
var history = $('#history').slider();
|
var history = $('#history').slider();
|
||||||
history.slider('setAttribute', 'max', count - 1);
|
history.slider('setAttribute', 'max', count - 1);
|
||||||
history.slider('setValue', count - 1);
|
history.slider('setValue', count - 1);
|
||||||
@ -161,7 +209,7 @@
|
|||||||
|
|
||||||
function outputSnapshot(results) {
|
function outputSnapshot(results) {
|
||||||
for (var name in results.columns) {
|
for (var name in results.columns) {
|
||||||
ctx.searchParams[name] = results.columns[name].value;
|
ctx.features[name] = results.columns[name].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.grapher.setColumns(results.columns);
|
ctx.grapher.setColumns(results.columns);
|
||||||
@ -181,70 +229,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(document).on({
|
$(document).on({
|
||||||
ajaxStart: function() {
|
ajaxStart: function() { $('#spinner').show(); },
|
||||||
$('#spinner').show();
|
ajaxStop: function() { $('#spinner').hide(); },
|
||||||
},
|
ready: onReady()
|
||||||
|
|
||||||
ajaxStop: function() {
|
|
||||||
$('#spinner').hide();
|
|
||||||
},
|
|
||||||
|
|
||||||
ready: function() {
|
|
||||||
$('#keywordsToSearch').selectpicker();
|
|
||||||
$('#history').slider({
|
|
||||||
formatter: function(value) {
|
|
||||||
var delta = log.length - (value + 1);
|
|
||||||
switch (delta) {
|
|
||||||
case 0:
|
|
||||||
return 'Most recent query';
|
|
||||||
case 1:
|
|
||||||
return 'Previous query';
|
|
||||||
default:
|
|
||||||
return String(delta) + ' queries back';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$.getJSON('/get_keywords', function(keywords) {
|
|
||||||
$('#searchKeywords').click(onSearch);
|
|
||||||
for (var i = 0, count = keywords.length; i < count; ++i) {
|
|
||||||
$('#keywordsToSearch').append($('<option></option>', {
|
|
||||||
value: keywords[i],
|
|
||||||
text: keywords[i]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
$('#keywordsToSearch').selectpicker('refresh');
|
|
||||||
$('#keywordsToSearch').change(function() {
|
|
||||||
$('#searchKeywords').prop('disabled', !$(this).val());
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#forgetKeyword').click(onForget);
|
|
||||||
$('#forgetDialog').on('show.bs.modal', function() {
|
|
||||||
$('#forgetError').hide();
|
|
||||||
$.getJSON('/get_keywords', function(keywords) {
|
|
||||||
$('#forgetKeyword').prop('disabled', keywords.length === 0);
|
|
||||||
$('#keywordToForget').empty();
|
|
||||||
for (var i = 0, count = keywords.length; i < count; ++i) {
|
|
||||||
$('#keywordToForget').append($('<option></option>', {
|
|
||||||
value: keywords[i],
|
|
||||||
text: keywords[i]
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#history').on('slideStop', onSelectSnapshot);
|
|
||||||
$('#learnKeyword').click(onLearn);
|
|
||||||
$('#keywordToLearn').bind('input', function() {
|
|
||||||
$('#learnKeyword').prop('disabled', !$(this).val());
|
|
||||||
});
|
|
||||||
$('#learnDialog').on('show.bs.modal', function() {
|
|
||||||
$('#learnKeyword').prop('disabled', true);
|
|
||||||
$('#keywordToLearn').val('');
|
|
||||||
$('#learnError').hide();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}(window.hscd = window.hscd || {}));
|
}(window.hscd = window.hscd || {}));
|
||||||
|
@ -281,7 +281,7 @@
|
|||||||
var _canvas = params.canvas;
|
var _canvas = params.canvas;
|
||||||
var _columns = {};
|
var _columns = {};
|
||||||
var _data = {};
|
var _data = {};
|
||||||
var _range = new Range(-1.0, 1.0);
|
var _range = new Range(params.range.min || -1.0, params.range.max || 1.0);
|
||||||
var _steps = params.steps || 20;
|
var _steps = params.steps || 20;
|
||||||
var _useLocalScale = params.useLocalScale || true;
|
var _useLocalScale = params.useLocalScale || true;
|
||||||
var _useRelativeScale = params.useRelativeScale || true;
|
var _useRelativeScale = params.useRelativeScale || true;
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
.unselectable {
|
|
||||||
-moz-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
@ -83,9 +83,7 @@ function combine(dict, params) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function walkRecords(data, searchParams, minScore, callback) {
|
function walkMatches(data, features, minScore, callback) {
|
||||||
var features = combine(data.keywords, searchParams);
|
|
||||||
|
|
||||||
for (var i = 0, count = data.records.length; i < count; ++i) {
|
for (var i = 0, count = data.records.length; i < count; ++i) {
|
||||||
var record = data.records[i];
|
var record = data.records[i];
|
||||||
var score = innerProduct(features, record.rating);
|
var score = innerProduct(features, record.rating);
|
||||||
@ -94,22 +92,20 @@ function walkRecords(data, searchParams, minScore, callback) {
|
|||||||
callback(record, score);
|
callback(record, score);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return features;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function countRecords(data, searchParams, minScore) {
|
function countRecords(data, features, minScore) {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
walkRecords(data, searchParams, minScore, function(record, score) {
|
walkMatches(data, features, minScore, function(record, score) {
|
||||||
++count;
|
++count;
|
||||||
});
|
});
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findRecords(data, searchParams, minScore) {
|
function findRecords(data, features, minScore) {
|
||||||
var results = [];
|
var results = [];
|
||||||
walkRecords(data, searchParams, minScore, function(record, score) {
|
walkMatches(data, features, minScore, function(record, score) {
|
||||||
results.push({
|
results.push({
|
||||||
name: record.name,
|
name: record.name,
|
||||||
url: 'http://www.tripadvisor.com' + record.relativeUrl,
|
url: 'http://www.tripadvisor.com' + record.relativeUrl,
|
||||||
@ -137,27 +133,27 @@ function step(range, steps, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function project(data, searchParams, minScore, keyword, range, steps) {
|
function project(data, features, feature, minScore, range, steps) {
|
||||||
var testParams = _.clone(searchParams);
|
var sample = _.clone(features);
|
||||||
var results = [];
|
var results = [];
|
||||||
|
|
||||||
step(range, steps, function(position) {
|
step(range, steps, function(position) {
|
||||||
testParams[keyword] = position;
|
sample[feature] = position;
|
||||||
results.push({
|
results.push({
|
||||||
sample: position,
|
sample: position,
|
||||||
count: countRecords(data, testParams, minScore)
|
count: countRecords(data, sample, minScore)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildHints(data, searchParams, minScore, keyword, range, steps) {
|
function buildHints(data, features, feature, minScore, range, steps) {
|
||||||
var projection = project(
|
var projection = project(
|
||||||
data,
|
data,
|
||||||
searchParams,
|
features,
|
||||||
|
feature,
|
||||||
minScore,
|
minScore,
|
||||||
keyword,
|
|
||||||
range,
|
range,
|
||||||
steps
|
steps
|
||||||
);
|
);
|
||||||
@ -275,23 +271,24 @@ function execQuery(query, callback) {
|
|||||||
getData(function(data) {
|
getData(function(data) {
|
||||||
var searchResults = findRecords(
|
var searchResults = findRecords(
|
||||||
data,
|
data,
|
||||||
query.searchParams,
|
query.features,
|
||||||
query.minScore
|
query.minScore
|
||||||
);
|
);
|
||||||
|
|
||||||
var graphColumns = {};
|
var graphColumns = {};
|
||||||
for (var keyword in query.searchParams) {
|
console.log(query);
|
||||||
|
for (var feature in query.features) {
|
||||||
var searchHints = buildHints(
|
var searchHints = buildHints(
|
||||||
data,
|
data,
|
||||||
query.searchParams,
|
query.features,
|
||||||
|
feature,
|
||||||
query.minScore,
|
query.minScore,
|
||||||
keyword,
|
query.range,
|
||||||
query.searchRange,
|
|
||||||
query.hintSteps
|
query.hintSteps
|
||||||
);
|
);
|
||||||
|
|
||||||
graphColumns[keyword] = {
|
graphColumns[feature] = {
|
||||||
value: query.searchParams[keyword],
|
value: query.features[feature],
|
||||||
hints: searchHints,
|
hints: searchHints,
|
||||||
steps: query.hintSteps
|
steps: query.hintSteps
|
||||||
};
|
};
|
||||||
|
@ -45,7 +45,7 @@ function main(staticFiles, port) {
|
|||||||
|
|
||||||
app.use('/get_keywords', function(req, res) {
|
app.use('/get_keywords', function(req, res) {
|
||||||
search.getKeywords(function(keywords) {
|
search.getKeywords(function(keywords) {
|
||||||
res.json(_.keys(keywords).sort());
|
res.json(keywords);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user