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
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)

View File

@ -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
}
}

60
util.go
View File

@ -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,