Cleanup work
This commit is contained in:
parent
2eb6d0e431
commit
fabde8de5f
48
server.go
48
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,10 +99,10 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
|
||||
response struct {
|
||||
Columns map[string]*jsonColumn `json:"columns"`
|
||||
Columns map[string]*column `json:"columns"`
|
||||
Count int `json:"count"`
|
||||
MinScore float64 `json:"minScore"`
|
||||
Records []jsonRecord `json:"records"`
|
||||
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)
|
||||
|
60
types.go
60
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 jsonColumn struct {
|
||||
Bracket jsonBracket `json:"bracket"`
|
||||
Hints []jsonProjection `json:"hints"`
|
||||
type column struct {
|
||||
Bracket bracket `json:"bracket"`
|
||||
Hints []projection `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
60
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,
|
||||
|
Loading…
Reference in New Issue
Block a user