From fabde8de5f6151c7b9bfb7e18551ab8912adf059 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Sun, 23 Aug 2015 19:56:07 +0900 Subject: [PATCH] Cleanup work --- server.go | 54 +++++++++++++-------------------------------- types.go | 66 ++++++++++++++++++------------------------------------- util.go | 60 +++++++++++++++++++++++++------------------------- 3 files changed, 66 insertions(+), 114 deletions(-) diff --git a/server.go b/server.go index dcf868a..481a75b 100644 --- a/server.go +++ b/server.go @@ -44,16 +44,16 @@ import ( 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() - *column = jsonColumn{ - Bracket: jsonBracket{Max: -1.0, Min: 1.0}, + *col = column{ + Bracket: bracket{Max: -1.0, Min: 1.0}, Mode: modes[name].String(), Steps: steps, Value: features[name]} - hints := project( + col.Hints = project( allEntries, features, modes, @@ -61,11 +61,6 @@ func prepareColumn(steps int, minScore float64, allEntries, matchedEntries recor minScore, steps) - for _, hint := range hints { - jsonHint := jsonProjection{hint.compatibility, hint.count, hint.sample} - column.Hints = append(column.Hints, jsonHint) - } - var d stats.Stats for _, record := range matchedEntries { if feature, ok := record.features[name]; ok { @@ -81,8 +76,8 @@ func prepareColumn(steps int, minScore float64, allEntries, matchedEntries recor mean := d.Mean() - column.Bracket.Max = math.Min(mean+dev, d.Max()) - column.Bracket.Min = math.Max(mean-dev, d.Min()) + col.Bracket.Max = math.Min(mean+dev, d.Max()) + col.Bracket.Min = math.Max(mean-dev, d.Min()) } } @@ -92,7 +87,7 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) { var ( request struct { Features featureMap `json:"features"` - Geo *jsonGeoData `json:"geo"` + Geo *geoData `json:"geo"` MaxResults int `json:"maxResults"` MinScore float64 `json:"minScore"` Modes map[string]string `json:"modes"` @@ -104,11 +99,11 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) { } response struct { - Columns map[string]*jsonColumn `json:"columns"` - Count int `json:"count"` - MinScore float64 `json:"minScore"` - Records []jsonRecord `json:"records"` - ElapsedTime int64 `json:"elapsedTime"` + Columns map[string]*column `json:"columns"` + Count int `json:"count"` + MinScore float64 `json:"minScore"` + Records []record `json:"records"` + 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.sort() - response.Columns = make(map[string]*jsonColumn) + response.Columns = make(map[string]*column) response.Count = len(matchedEntries) response.MinScore = request.MinScore - response.Records = make([]jsonRecord, 0) + response.Records = matchedEntries //[:request.MaxResults] var wg sync.WaitGroup wg.Add(len(features)) for name := range features { - response.Columns[name] = new(jsonColumn) + response.Columns[name] = new(column) go prepareColumn( request.Resolution, request.MinScore, @@ -154,25 +149,6 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) { 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() js, err := json.Marshal(response) diff --git a/types.go b/types.go index e8cca79..537f38c 100644 --- a/types.go +++ b/types.go @@ -29,7 +29,6 @@ import ( type featureMap map[string]float64 type modeMap map[string]modeType -type records []record type modeType int const ( @@ -37,26 +36,21 @@ const ( modeTypeDist ) -type jsonGeoData struct { - Latitude float64 `json:"latitude"` - Longitude float64 `json:"longitude"` +type column struct { + Bracket bracket `json:"bracket"` + Hints []projection `json:"hints"` + Mode string `json:"mode"` + Steps int `json:"steps"` + Value float64 `json:"value"` } -type jsonColumn struct { - Bracket jsonBracket `json:"bracket"` - Hints []jsonProjection `json:"hints"` - Mode string `json:"mode"` - Steps int `json:"steps"` - Value float64 `json:"value"` -} - -type jsonProjection struct { +type projection struct { Compatibility float64 `json:"compatibility"` Count int `json:"count"` Sample float64 `json:"sample"` } -type jsonRecord struct { +type record struct { AccessCount int `json:"accessCount"` ClosestStn string `json:"closestStn"` Compatibility float64 `json:"compatibility"` @@ -66,9 +60,11 @@ type jsonRecord struct { Name string `json:"name"` Score float64 `json:"score"` Url string `json:"url"` + features featureMap + geo geoData } -type jsonBracket struct { +type bracket struct { Min float64 `json:"min"` Max float64 `json:"max"` } @@ -79,34 +75,14 @@ type queryContext struct { walkingDist float64 } -type queryProjection struct { - compatibility float64 - count int - sample float64 -} - type geoData struct { - latitude float64 - longitude float64 -} - -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 + Latitude float64 `json:"latitude"` + Longitude float64 `json:"longitude"` } type recordSorter struct { ascending bool - entries records + entries []record key string } @@ -128,19 +104,19 @@ func (s recordSorter) Less(i, j int) bool { switch s.key { case "accessCount": - return entry1.accessCount < entry2.accessCount + return entry1.AccessCount < entry2.AccessCount case "closestStn": - return entry1.closestStn < entry2.closestStn + return entry1.ClosestStn < entry2.ClosestStn case "compatibility": - return entry1.compatibility < entry2.compatibility + return entry1.Compatibility < entry2.Compatibility case "distanceToStn": - return entry1.distanceToStn < entry2.distanceToStn + return entry1.DistanceToStn < entry2.DistanceToStn case "distanceToUser": - return entry1.distanceToUser < entry2.distanceToUser + return entry1.DistanceToUser < entry2.DistanceToUser case "name": - return entry1.name < entry2.name + return entry1.Name < entry2.Name default: - return entry1.score < entry2.score + return entry1.Score < entry2.Score } } diff --git a/util.go b/util.go index 12d5e9a..91f96f2 100644 --- a/util.go +++ b/util.go @@ -100,7 +100,7 @@ func compare(features1 featureMap, features2 featureMap, modes modeMap) float64 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 { if score := compare(features, entry.features, modes); score >= minScore { 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 count int walkMatches(entries, features, modes, minScore, func(entry record, score float64) { - compatibility += entry.compatibility + compatibility += entry.Compatibility 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 { - var foundEntries records +func findRecords(entries []record, features featureMap, modes modeMap, minScore float64) []record { + var matchedEntries []record walkMatches(entries, features, modes, minScore, func(entry record, score float64) { - entry.score = score - foundEntries = append(foundEntries, entry) + entry.Score = score + 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) for key, value := range features { sampleFeatures[key] = value } - var projection []queryProjection + var projections []projection stepRange(-1.0, 1.0, steps, func(sample float64) { sample, sampleFeatures[featureName] = sampleFeatures[featureName], sample compatibility, count := statRecords(entries, sampleFeatures, modes, minScore) 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 distUserMax := 0.0 @@ -169,13 +169,13 @@ func computeRecordsGeo(entries records, context queryContext) { entry := &entries[index] if context.geo != nil { - userPoint := geo.NewPoint(context.geo.latitude, context.geo.longitude) - entryPoint := geo.NewPoint(entry.geo.latitude, context.geo.longitude) - entry.distanceToUser = userPoint.GreatCircleDistance(entryPoint) + userPoint := geo.NewPoint(context.geo.Latitude, context.geo.Longitude) + entryPoint := geo.NewPoint(entry.geo.Latitude, context.geo.Longitude) + entry.DistanceToUser = userPoint.GreatCircleDistance(entryPoint) } - distUserMin = math.Min(entry.distanceToUser, distUserMin) - distUserMax = math.Max(entry.distanceToUser, distUserMax) + distUserMin = math.Min(entry.DistanceToUser, distUserMin) + distUserMax = math.Max(entry.DistanceToUser, distUserMax) } distUserRange := distUserMax - distUserMin @@ -185,9 +185,9 @@ func computeRecordsGeo(entries records, context queryContext) { var accessible, nearby float64 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.Min(accessible, 1.0) } @@ -198,7 +198,7 @@ func computeRecordsGeo(entries records, context queryContext) { } 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 { log.Fatal(err) } @@ -242,13 +242,13 @@ func computeRecordCompat(entry *record, context queryContext, wg *sync.WaitGroup } if groupCount > 0 { - entry.compatibility = groupSum / float64(groupCount) + entry.Compatibility = groupSum / float64(groupCount) } wg.Done() } -func computeRecordsCompat(entries records, context queryContext) { +func computeRecordsCompat(entries []record, context queryContext) { count := len(entries) 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") if err != nil { log.Fatal(err) @@ -297,13 +297,13 @@ func getRecords(context queryContext) records { &id) entry := record{ - name: name, - url: url, - distanceToStn: distanceToStn, - closestStn: closestStn, - accessCount: accessCount, + Name: name, + Url: url, + DistanceToStn: distanceToStn, + ClosestStn: closestStn, + AccessCount: accessCount, geo: geoData{latitude, longitude}, - id: id} + Id: id} entry.features = featureMap{ "delicious": delicious,