1

Cleanup work

This commit is contained in:
Alex Yatskov 2015-08-23 19:56:07 +09:00
parent 2eb6d0e431
commit fabde8de5f
3 changed files with 66 additions and 114 deletions

View File

@ -44,16 +44,16 @@ import (
var db *sql.DB var db *sql.DB
func prepareColumn(steps int, minScore float64, allEntries, matchedEntries records, features featureMap, modes modeMap, name string, column *jsonColumn, wg *sync.WaitGroup) { func prepareColumn(steps int, minScore float64, allEntries, matchedEntries []record, features featureMap, modes modeMap, name string, col *column, wg *sync.WaitGroup) {
defer wg.Done() defer wg.Done()
*column = jsonColumn{ *col = column{
Bracket: jsonBracket{Max: -1.0, Min: 1.0}, Bracket: bracket{Max: -1.0, Min: 1.0},
Mode: modes[name].String(), Mode: modes[name].String(),
Steps: steps, Steps: steps,
Value: features[name]} Value: features[name]}
hints := project( col.Hints = project(
allEntries, allEntries,
features, features,
modes, modes,
@ -61,11 +61,6 @@ func prepareColumn(steps int, minScore float64, allEntries, matchedEntries recor
minScore, minScore,
steps) steps)
for _, hint := range hints {
jsonHint := jsonProjection{hint.compatibility, hint.count, hint.sample}
column.Hints = append(column.Hints, jsonHint)
}
var d stats.Stats var d stats.Stats
for _, record := range matchedEntries { for _, record := range matchedEntries {
if feature, ok := record.features[name]; ok { if feature, ok := record.features[name]; ok {
@ -81,8 +76,8 @@ func prepareColumn(steps int, minScore float64, allEntries, matchedEntries recor
mean := d.Mean() mean := d.Mean()
column.Bracket.Max = math.Min(mean+dev, d.Max()) col.Bracket.Max = math.Min(mean+dev, d.Max())
column.Bracket.Min = math.Max(mean-dev, d.Min()) col.Bracket.Min = math.Max(mean-dev, d.Min())
} }
} }
@ -92,7 +87,7 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
var ( var (
request struct { request struct {
Features featureMap `json:"features"` Features featureMap `json:"features"`
Geo *jsonGeoData `json:"geo"` Geo *geoData `json:"geo"`
MaxResults int `json:"maxResults"` MaxResults int `json:"maxResults"`
MinScore float64 `json:"minScore"` MinScore float64 `json:"minScore"`
Modes map[string]string `json:"modes"` Modes map[string]string `json:"modes"`
@ -104,10 +99,10 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
} }
response struct { response struct {
Columns map[string]*jsonColumn `json:"columns"` Columns map[string]*column `json:"columns"`
Count int `json:"count"` Count int `json:"count"`
MinScore float64 `json:"minScore"` MinScore float64 `json:"minScore"`
Records []jsonRecord `json:"records"` Records []record `json:"records"`
ElapsedTime int64 `json:"elapsedTime"` ElapsedTime int64 `json:"elapsedTime"`
} }
) )
@ -130,16 +125,16 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
sorter := recordSorter{entries: matchedEntries, key: request.SortKey, ascending: request.SortAsc} sorter := recordSorter{entries: matchedEntries, key: request.SortKey, ascending: request.SortAsc}
sorter.sort() sorter.sort()
response.Columns = make(map[string]*jsonColumn) response.Columns = make(map[string]*column)
response.Count = len(matchedEntries) response.Count = len(matchedEntries)
response.MinScore = request.MinScore response.MinScore = request.MinScore
response.Records = make([]jsonRecord, 0) response.Records = matchedEntries //[:request.MaxResults]
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(features)) wg.Add(len(features))
for name := range features { for name := range features {
response.Columns[name] = new(jsonColumn) response.Columns[name] = new(column)
go prepareColumn( go prepareColumn(
request.Resolution, request.Resolution,
request.MinScore, request.MinScore,
@ -154,25 +149,6 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
wg.Wait() wg.Wait()
for index, entry := range matchedEntries {
if index >= request.MaxResults {
break
}
item := jsonRecord{
Name: entry.name,
Url: entry.url,
Score: entry.score,
Compatibility: entry.compatibility,
DistanceToUser: entry.distanceToUser,
DistanceToStn: entry.distanceToStn,
ClosestStn: entry.closestStn,
AccessCount: entry.accessCount,
Id: entry.id}
response.Records = append(response.Records, item)
}
response.ElapsedTime = time.Since(startTime).Nanoseconds() response.ElapsedTime = time.Since(startTime).Nanoseconds()
js, err := json.Marshal(response) js, err := json.Marshal(response)

View File

@ -29,7 +29,6 @@ import (
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 type modeType int
const ( const (
@ -37,26 +36,21 @@ const (
modeTypeDist modeTypeDist
) )
type jsonGeoData struct { type column struct {
Latitude float64 `json:"latitude"` Bracket bracket `json:"bracket"`
Longitude float64 `json:"longitude"` Hints []projection `json:"hints"`
}
type jsonColumn struct {
Bracket jsonBracket `json:"bracket"`
Hints []jsonProjection `json:"hints"`
Mode string `json:"mode"` Mode string `json:"mode"`
Steps int `json:"steps"` Steps int `json:"steps"`
Value float64 `json:"value"` Value float64 `json:"value"`
} }
type jsonProjection struct { type projection struct {
Compatibility float64 `json:"compatibility"` Compatibility float64 `json:"compatibility"`
Count int `json:"count"` Count int `json:"count"`
Sample float64 `json:"sample"` Sample float64 `json:"sample"`
} }
type jsonRecord struct { type record struct {
AccessCount int `json:"accessCount"` AccessCount int `json:"accessCount"`
ClosestStn string `json:"closestStn"` ClosestStn string `json:"closestStn"`
Compatibility float64 `json:"compatibility"` Compatibility float64 `json:"compatibility"`
@ -66,9 +60,11 @@ type jsonRecord struct {
Name string `json:"name"` Name string `json:"name"`
Score float64 `json:"score"` Score float64 `json:"score"`
Url string `json:"url"` Url string `json:"url"`
features featureMap
geo geoData
} }
type jsonBracket struct { type bracket struct {
Min float64 `json:"min"` Min float64 `json:"min"`
Max float64 `json:"max"` Max float64 `json:"max"`
} }
@ -79,34 +75,14 @@ type queryContext struct {
walkingDist float64 walkingDist float64
} }
type queryProjection struct {
compatibility float64
count int
sample float64
}
type geoData struct { type geoData struct {
latitude float64 Latitude float64 `json:"latitude"`
longitude float64 Longitude float64 `json:"longitude"`
}
type record struct {
accessCount int
closestStn string
compatibility float64
distanceToStn float64
distanceToUser float64
features featureMap
geo geoData
id int
name string
score float64
url string
} }
type recordSorter struct { type recordSorter struct {
ascending bool ascending bool
entries records entries []record
key string key string
} }
@ -128,19 +104,19 @@ func (s recordSorter) Less(i, j int) bool {
switch s.key { switch s.key {
case "accessCount": case "accessCount":
return entry1.accessCount < entry2.accessCount return entry1.AccessCount < entry2.AccessCount
case "closestStn": case "closestStn":
return entry1.closestStn < entry2.closestStn return entry1.ClosestStn < entry2.ClosestStn
case "compatibility": case "compatibility":
return entry1.compatibility < entry2.compatibility return entry1.Compatibility < entry2.Compatibility
case "distanceToStn": case "distanceToStn":
return entry1.distanceToStn < entry2.distanceToStn return entry1.DistanceToStn < entry2.DistanceToStn
case "distanceToUser": case "distanceToUser":
return entry1.distanceToUser < entry2.distanceToUser return entry1.DistanceToUser < entry2.DistanceToUser
case "name": case "name":
return entry1.name < entry2.name return entry1.Name < entry2.Name
default: default:
return entry1.score < entry2.score return entry1.Score < entry2.Score
} }
} }

60
util.go
View File

@ -100,7 +100,7 @@ func compare(features1 featureMap, features2 featureMap, modes modeMap) float64
return result return result
} }
func walkMatches(entries records, features featureMap, modes modeMap, minScore float64, callback func(record, float64)) { func walkMatches(entries []record, features featureMap, modes modeMap, minScore float64, callback func(record, float64)) {
for _, entry := range entries { for _, entry := range entries {
if score := compare(features, entry.features, modes); score >= minScore { if score := compare(features, entry.features, modes); score >= minScore {
callback(entry, score) callback(entry, score)
@ -108,12 +108,12 @@ func walkMatches(entries records, features featureMap, modes modeMap, minScore f
} }
} }
func statRecords(entries records, features featureMap, modes modeMap, minScore float64) (float64, int) { func statRecords(entries []record, features featureMap, modes modeMap, minScore float64) (float64, int) {
var compatibility float64 var compatibility float64
var count int var count int
walkMatches(entries, features, modes, minScore, func(entry record, score float64) { walkMatches(entries, features, modes, minScore, func(entry record, score float64) {
compatibility += entry.compatibility compatibility += entry.Compatibility
count++ count++
}) })
@ -132,36 +132,36 @@ func stepRange(min, max float64, steps int, callback func(float64)) {
} }
} }
func findRecords(entries records, features featureMap, modes modeMap, minScore float64) records { func findRecords(entries []record, features featureMap, modes modeMap, minScore float64) []record {
var foundEntries records var matchedEntries []record
walkMatches(entries, features, modes, minScore, func(entry record, score float64) { walkMatches(entries, features, modes, minScore, func(entry record, score float64) {
entry.score = score entry.Score = score
foundEntries = append(foundEntries, entry) matchedEntries = append(matchedEntries, entry)
}) })
return foundEntries return matchedEntries
} }
func project(entries records, features featureMap, modes modeMap, featureName string, minScore float64, steps int) []queryProjection { func project(entries []record, features featureMap, modes modeMap, featureName string, minScore float64, steps int) []projection {
sampleFeatures := make(featureMap) sampleFeatures := make(featureMap)
for key, value := range features { for key, value := range features {
sampleFeatures[key] = value sampleFeatures[key] = value
} }
var projection []queryProjection var projections []projection
stepRange(-1.0, 1.0, steps, func(sample float64) { stepRange(-1.0, 1.0, steps, func(sample float64) {
sample, sampleFeatures[featureName] = sampleFeatures[featureName], sample sample, sampleFeatures[featureName] = sampleFeatures[featureName], sample
compatibility, count := statRecords(entries, sampleFeatures, modes, minScore) compatibility, count := statRecords(entries, sampleFeatures, modes, minScore)
sample, sampleFeatures[featureName] = sampleFeatures[featureName], sample sample, sampleFeatures[featureName] = sampleFeatures[featureName], sample
projection = append(projection, queryProjection{compatibility, count, sample}) projections = append(projections, projection{compatibility, count, sample})
}) })
return projection return projections
} }
func computeRecordsGeo(entries records, context queryContext) { func computeRecordsGeo(entries []record, context queryContext) {
distUserMin := math.MaxFloat64 distUserMin := math.MaxFloat64
distUserMax := 0.0 distUserMax := 0.0
@ -169,13 +169,13 @@ func computeRecordsGeo(entries records, context queryContext) {
entry := &entries[index] entry := &entries[index]
if context.geo != nil { if context.geo != nil {
userPoint := geo.NewPoint(context.geo.latitude, context.geo.longitude) userPoint := geo.NewPoint(context.geo.Latitude, context.geo.Longitude)
entryPoint := geo.NewPoint(entry.geo.latitude, context.geo.longitude) entryPoint := geo.NewPoint(entry.geo.Latitude, context.geo.Longitude)
entry.distanceToUser = userPoint.GreatCircleDistance(entryPoint) entry.DistanceToUser = userPoint.GreatCircleDistance(entryPoint)
} }
distUserMin = math.Min(entry.distanceToUser, distUserMin) distUserMin = math.Min(entry.DistanceToUser, distUserMin)
distUserMax = math.Max(entry.distanceToUser, distUserMax) distUserMax = math.Max(entry.DistanceToUser, distUserMax)
} }
distUserRange := distUserMax - distUserMin distUserRange := distUserMax - distUserMin
@ -185,9 +185,9 @@ func computeRecordsGeo(entries records, context queryContext) {
var accessible, nearby float64 var accessible, nearby float64
if distUserRange > 0 { if distUserRange > 0 {
nearby = -((entry.distanceToUser-distUserMin)/distUserRange - 0.5) * 2.0 nearby = -((entry.DistanceToUser-distUserMin)/distUserRange - 0.5) * 2.0
accessible = 1.0 - (entry.distanceToStn / (context.walkingDist * 1000)) accessible = 1.0 - (entry.DistanceToStn / (context.walkingDist * 1000))
accessible = math.Max(accessible, -1.0) accessible = math.Max(accessible, -1.0)
accessible = math.Min(accessible, 1.0) accessible = math.Min(accessible, 1.0)
} }
@ -198,7 +198,7 @@ func computeRecordsGeo(entries records, context queryContext) {
} }
func computeRecordCompat(entry *record, context queryContext, wg *sync.WaitGroup) { func computeRecordCompat(entry *record, context queryContext, wg *sync.WaitGroup) {
historyRows, err := db.Query("SELECT id FROM history WHERE reviewId = (?)", entry.id) historyRows, err := db.Query("SELECT id FROM history WHERE reviewId = (?)", entry.Id)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -242,13 +242,13 @@ func computeRecordCompat(entry *record, context queryContext, wg *sync.WaitGroup
} }
if groupCount > 0 { if groupCount > 0 {
entry.compatibility = groupSum / float64(groupCount) entry.Compatibility = groupSum / float64(groupCount)
} }
wg.Done() wg.Done()
} }
func computeRecordsCompat(entries records, context queryContext) { func computeRecordsCompat(entries []record, context queryContext) {
count := len(entries) count := len(entries)
limit := 32 limit := 32
@ -269,7 +269,7 @@ func computeRecordsCompat(entries records, context queryContext) {
} }
} }
func getRecords(context queryContext) records { func getRecords(context queryContext) []record {
recordRows, err := db.Query("SELECT name, url, delicious, accommodating, affordable, atmospheric, latitude, longitude, closestStnDist, closestStnName, accessCount, id FROM reviews") recordRows, err := db.Query("SELECT name, url, delicious, accommodating, affordable, atmospheric, latitude, longitude, closestStnDist, closestStnName, accessCount, id FROM reviews")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -297,13 +297,13 @@ func getRecords(context queryContext) records {
&id) &id)
entry := record{ entry := record{
name: name, Name: name,
url: url, Url: url,
distanceToStn: distanceToStn, DistanceToStn: distanceToStn,
closestStn: closestStn, ClosestStn: closestStn,
accessCount: accessCount, AccessCount: accessCount,
geo: geoData{latitude, longitude}, geo: geoData{latitude, longitude},
id: id} Id: id}
entry.features = featureMap{ entry.features = featureMap{
"delicious": delicious, "delicious": delicious,