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
|
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)
|
||||||
|
60
types.go
60
types.go
@ -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
60
util.go
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user