1
This commit is contained in:
Alex Yatskov 2015-06-29 19:09:34 +09:00
parent ae5dd26df3
commit 4fac0774cd
6 changed files with 91 additions and 91 deletions

View File

@ -59,8 +59,8 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
sorter.sort() sorter.sort()
response := jsonQueryResponse{ response := jsonQueryResponse{
Count: len(foundEntries),
Columns: make(map[string]jsonColumn), Columns: make(map[string]jsonColumn),
Count: len(foundEntries),
MinScore: request.MinScore, MinScore: request.MinScore,
Records: make([]jsonRecord, 0)} Records: make([]jsonRecord, 0)}
@ -68,10 +68,10 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
mode, _ := modes[name] mode, _ := modes[name]
column := jsonColumn{ column := jsonColumn{
Bracket: jsonBracket{Max: -1, Min: 1}, Bracket: jsonBracket{Max: -1.0, Min: 1.0},
Mode: mode.String(), Mode: mode.String(),
Value: value, Steps: request.Resolution,
Steps: request.Resolution} Value: value}
hints := project( hints := project(
entries, entries,
@ -216,7 +216,6 @@ func removeCategory(rw http.ResponseWriter, req *http.Request) {
} }
_, err := db.Exec("DELETE FROM categories WHERE id = (?)", request.Id) _, err := db.Exec("DELETE FROM categories WHERE id = (?)", request.Id)
js, err := json.Marshal(jsonRemoveCategoryResponse{err == nil}) js, err := json.Marshal(jsonRemoveCategoryResponse{err == nil})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -295,8 +294,7 @@ func main() {
flag.Parse() flag.Parse()
var err error var err error
db, err = sql.Open("mysql", *dataSrc) if db, err = sql.Open("mysql", *dataSrc); err != nil {
if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer db.Close() defer db.Close()

View File

@ -11,7 +11,7 @@
</style> </style>
</head> </head>
<body> <body>
<div class="container-fluid"> <div class="container">
<!-- busy spinner --> <!-- busy spinner -->
<div class="page-header"> <div class="page-header">
<img id="spinner" alt="loading" class="pull-right" src="images/spinner.gif" style="display: none;" width="32" height="32"> <img id="spinner" alt="loading" class="pull-right" src="images/spinner.gif" style="display: none;" width="32" height="32">
@ -80,16 +80,9 @@
<!-- visualizer --> <!-- visualizer -->
<div class="col-md-9"> <div class="col-md-9">
<div class="panel panel-default">
<div class="panel-heading">
<big>Visualizer</big>
</div>
<div class="panel-body text-center">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="500"></svg> <svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="500"></svg>
</div> </div>
</div> </div>
</div>
</div>
<!-- result listing --> <!-- result listing -->
<div class="panel panel-default" style="display: none;" id="resultPanel"> <div class="panel panel-default" style="display: none;" id="resultPanel">

View File

@ -100,7 +100,7 @@
var _name = params.name; var _name = params.name;
var _valueTrans = params.data.value; var _valueTrans = params.data.value;
var _bracketTrans = params.data.bracket; var _bracketTrans = params.data.bracket;
var _onStateChanged = params.onStateChanged; var _stateChanged = params.stateChanged;
var _range = params.range; var _range = params.range;
var _scale = params.scale; var _scale = params.scale;
var _elements = {}; var _elements = {};
@ -298,23 +298,25 @@
} }
function updateMode() { function updateMode() {
var modeText = '( ' + _data.mode + ' )'; var modes = {'product': 'importance', 'distance': 'similarity'};
var mode = modes[_data.mode];
if (_.has(_elements, 'mode')) { if (_.has(_elements, 'mode')) {
_elements.mode.attr({ _elements.mode.attr({
text: modeText text: mode
}); });
} }
else { else {
_elements.mode = _canvas.text( _elements.mode = _canvas.text(
(_width - _bracketSize) / 2, (_width - _bracketSize) / 2,
_height - _panelSize / 4, _height - _panelSize / 4,
modeText mode
).attr({ ).attr({
'dominant-baseline': 'middle', 'dominant-baseline': 'middle',
'text-anchor': 'middle', 'text-anchor': 'middle',
cursor: 'hand', 'text-decoration': 'underline',
'fill': _modeColor 'fill': _modeColor,
cursor: 'hand'
}).click(modeClick); }).click(modeClick);
} }
} }
@ -354,8 +356,8 @@
_data.value = _range.clamp(value); _data.value = _range.clamp(value);
_data.mode = mode; _data.mode = mode;
if (_onStateChanged) { if (_stateChanged) {
_onStateChanged(_name, _data.value, _data.mode); _stateChanged(_name, _data.value, _data.mode);
} }
animateIndicator(_valueTrans, _data.value); animateIndicator(_valueTrans, _data.value);
@ -499,7 +501,7 @@
var _range = new Range(-1.0, 1.0); var _range = new Range(-1.0, 1.0);
var _useLocalScale = params.useLocalScale || false; var _useLocalScale = params.useLocalScale || false;
var _displayType = params.displayType || 'density'; var _displayType = params.displayType || 'density';
var _onStateChanged = params.onStateChanged; var _stateChanged = params.stateChanged;
function processHintParameters(columns) { function processHintParameters(columns) {
var displayTypes = {compatibility: 'compatibility', density: 'count'}; var displayTypes = {compatibility: 'compatibility', density: 'count'};
@ -557,7 +559,7 @@
} }
else { else {
_columns[name] = new Column({ _columns[name] = new Column({
onStateChanged: _onStateChanged, stateChanged: _stateChanged,
range: _range, range: _range,
canvas: _canvas, canvas: _canvas,
data: columnData, data: columnData,

View File

@ -25,7 +25,7 @@
var _ctx = {}; var _ctx = {};
function onStateChanged(name, value, mode) { function stateChanged(name, value, mode) {
_ctx.query.features[name] = value; _ctx.query.features[name] = value;
_ctx.query.modes[name] = mode; _ctx.query.modes[name] = mode;
@ -35,7 +35,7 @@
}, 'json'); }, 'json');
} }
function onReady(geo) { function ready(geo) {
_ctx = { _ctx = {
sortKey: 'score', sortKey: 'score',
sortAsc: false, sortAsc: false,
@ -99,7 +99,7 @@
if (!_.has(_ctx, 'grapher')) { if (!_.has(_ctx, 'grapher')) {
_ctx.grapher = new grapher.Grapher({ _ctx.grapher = new grapher.Grapher({
canvas: new Snap('#svg'), canvas: new Snap('#svg'),
onStateChanged: onStateChanged, stateChanged: stateChanged,
displayType: $('#displayType').val(), displayType: $('#displayType').val(),
useLocalScale: $('#useLocalScale').is(':checked') useLocalScale: $('#useLocalScale').is(':checked')
}); });
@ -200,13 +200,13 @@
ready: function() { ready: function() {
if (navigator.geolocation) { if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition( navigator.geolocation.getCurrentPosition(
function(geo) { onReady(geo); }, function(geo) { ready(geo); },
function(err) { onReady(null); }, function(err) { ready(null); },
{ enableHighAccuracy: true } { enableHighAccuracy: true }
); );
} }
else { else {
onReady(null); ready(null);
} }
} }
}); });

View File

@ -22,18 +22,20 @@
package main package main
import "sort" import (
"errors"
type modeType int "sort"
const (
ModeTypeNone modeType = iota
ModeTypeProduct
ModeTypeDistance
) )
type featureMap map[string]float64 type featureMap map[string]float64
type modeMap map[string]modeType type modeMap map[string]modeType
type records []record
type modeType int
const (
modeTypeProd = iota + 1
modeTypeDist
)
type jsonAccessRequest struct { type jsonAccessRequest struct {
Id int `json:"id"` Id int `json:"id"`
@ -150,8 +152,6 @@ type record struct {
url string url string
} }
type records []record
type recordSorter struct { type recordSorter struct {
ascending bool ascending bool
entries records entries records
@ -198,22 +198,22 @@ func (s recordSorter) Swap(i, j int) {
func (m modeType) String() string { func (m modeType) String() string {
switch m { switch m {
case ModeTypeProduct: case modeTypeProd:
return "product" return "product"
case ModeTypeDistance: case modeTypeDist:
return "distance" return "distance"
default: default:
return "invalid" return ""
} }
} }
func strToModeType(mode string) modeType { func parseModeType(mode string) (modeType, error) {
switch mode { switch mode {
case "product": case "product":
return ModeTypeProduct return modeTypeProd, nil
case "distance": case "distance":
return ModeTypeDistance return modeTypeDist, nil
default: default:
return ModeTypeNone return 0, errors.New("invalid mode type")
} }
} }

37
util.go
View File

@ -40,51 +40,58 @@ func fixFeatures(features featureMap) featureMap {
"atmospheric": 0.0} "atmospheric": 0.0}
for name := range fixedFeatures { for name := range fixedFeatures {
value, _ := features[name] if value, ok := features[name]; ok {
fixedFeatures[name] = value fixedFeatures[name] = value
} }
}
return fixedFeatures return fixedFeatures
} }
func fixModes(modes map[string]string) modeMap { func fixModes(modes map[string]string) modeMap {
fixedModes := modeMap{ fixedModes := modeMap{
"nearby": ModeTypeProduct, "nearby": modeTypeProd,
"accessible": ModeTypeProduct, "accessible": modeTypeProd,
"delicious": ModeTypeProduct, "delicious": modeTypeProd,
"accommodating": ModeTypeProduct, "accommodating": modeTypeProd,
"affordable": ModeTypeProduct, "affordable": modeTypeProd,
"atmospheric": ModeTypeProduct} "atmospheric": modeTypeProd}
for name := range fixedModes { for name := range fixedModes {
if value, ok := modes[name]; ok { if value, ok := modes[name]; ok {
fixedModes[name] = strToModeType(value) if mode, err := parseModeType(value); err == nil {
fixedModes[name] = mode
}
} }
} }
return fixedModes return fixedModes
} }
func innerProduct(features1 featureMap, features2 featureMap) float64 { func distance(features1 featureMap, features2 featureMap) float64 {
var result float64 var sum float64
for key, value1 := range features1 { for key, value1 := range features1 {
value2, _ := features2[key] value2, _ := features2[key]
result += value1 * value2 sum += math.Pow(value1-value2, 2)
} }
return result return math.Sqrt(sum)
} }
func compare(features1 featureMap, features2 featureMap, modes modeMap) float64 { func compare(features1 featureMap, features2 featureMap, modes modeMap) float64 {
var result float64 var result float64
for key, value1 := range features1 { for key, value1 := range features1 {
value2, _ := features2[key] value2, _ := features2[key]
switch mode, _ := modes[key]; mode { switch mode, _ := modes[key]; mode {
case ModeTypeDistance: case modeTypeDist:
result += 1 - math.Abs(value1-value2) result += 1 - math.Abs(value1-value2)
default: case modeTypeProd:
result += value1 * value2 result += value1 * value2
default:
log.Fatal("unsupported compare mode")
} }
} }
@ -228,7 +235,7 @@ func computeRecordPopularity(entries records, context queryContext) {
log.Fatal(err) log.Fatal(err)
} }
groupSum += innerProduct(recordProfile, context.profile) groupSum += distance(recordProfile, context.profile)
groupCount++ groupCount++
} }
if err := historyRows.Err(); err != nil { if err := historyRows.Err(); err != nil {