1
This commit is contained in:
Alex Yatskov 2015-08-23 19:34:45 +09:00
parent 65e9036a63
commit 2eb6d0e431
3 changed files with 80 additions and 69 deletions

106
server.go
View File

@ -44,22 +44,22 @@ import (
var db *sql.DB var db *sql.DB
func prepareColumn(request jsonQueryRequest, entries, foundEntries records, features featureMap, modes modeMap, name string, column *jsonColumn, wg *sync.WaitGroup) { func prepareColumn(steps int, minScore float64, allEntries, matchedEntries records, features featureMap, modes modeMap, name string, column *jsonColumn, wg *sync.WaitGroup) {
mode := modes[name] defer wg.Done()
*column = jsonColumn{ *column = jsonColumn{
Bracket: jsonBracket{Max: -1.0, Min: 1.0}, Bracket: jsonBracket{Max: -1.0, Min: 1.0},
Mode: mode.String(), Mode: modes[name].String(),
Steps: request.Resolution, Steps: steps,
Value: features[name]} Value: features[name]}
hints := project( hints := project(
entries, allEntries,
features, features,
modes, modes,
name, name,
request.MinScore, minScore,
request.Resolution) steps)
for _, hint := range hints { for _, hint := range hints {
jsonHint := jsonProjection{hint.compatibility, hint.count, hint.sample} jsonHint := jsonProjection{hint.compatibility, hint.count, hint.sample}
@ -67,7 +67,7 @@ func prepareColumn(request jsonQueryRequest, entries, foundEntries records, feat
} }
var d stats.Stats var d stats.Stats
for _, record := range foundEntries { for _, record := range matchedEntries {
if feature, ok := record.features[name]; ok { if feature, ok := record.features[name]; ok {
d.Update(feature) d.Update(feature)
} }
@ -84,14 +84,34 @@ func prepareColumn(request jsonQueryRequest, entries, foundEntries records, feat
column.Bracket.Max = math.Min(mean+dev, d.Max()) column.Bracket.Max = math.Min(mean+dev, d.Max())
column.Bracket.Min = math.Max(mean-dev, d.Min()) column.Bracket.Min = math.Max(mean-dev, d.Min())
} }
wg.Done()
} }
func executeQuery(rw http.ResponseWriter, req *http.Request) { func executeQuery(rw http.ResponseWriter, req *http.Request) {
startTime := time.Now() startTime := time.Now()
var request jsonQueryRequest var (
request struct {
Features featureMap `json:"features"`
Geo *jsonGeoData `json:"geo"`
MaxResults int `json:"maxResults"`
MinScore float64 `json:"minScore"`
Modes map[string]string `json:"modes"`
Profile featureMap `json:"profile"`
Resolution int `json:"resolution"`
SortAsc bool `json:"sortAsc"`
SortKey string `json:"sortKey"`
WalkingDist float64 `json:"walkingDist"`
}
response struct {
Columns map[string]*jsonColumn `json:"columns"`
Count int `json:"count"`
MinScore float64 `json:"minScore"`
Records []jsonRecord `json:"records"`
ElapsedTime int64 `json:"elapsedTime"`
}
)
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
return return
@ -102,44 +122,53 @@ func executeQuery(rw http.ResponseWriter, req *http.Request) {
geo = &geoData{request.Geo.Latitude, request.Geo.Longitude} geo = &geoData{request.Geo.Latitude, request.Geo.Longitude}
} }
entries := getRecords(queryContext{geo, request.Profile, request.WalkingDist}) allEntries := getRecords(queryContext{geo, request.Profile, request.WalkingDist})
features := fixFeatures(request.Features) features := fixFeatures(request.Features)
modes := fixModes(request.Modes) modes := fixModes(request.Modes)
foundEntries := findRecords(entries, features, modes, request.MinScore) matchedEntries := findRecords(allEntries, features, modes, request.MinScore)
sorter := recordSorter{entries: foundEntries, key: request.SortKey, ascending: request.SortAsc} sorter := recordSorter{entries: matchedEntries, key: request.SortKey, ascending: request.SortAsc}
sorter.sort() sorter.sort()
response := jsonQueryResponse{ response.Columns = make(map[string]*jsonColumn)
Columns: make(map[string]*jsonColumn), response.Count = len(matchedEntries)
Count: len(foundEntries), response.MinScore = request.MinScore
MinScore: request.MinScore, response.Records = make([]jsonRecord, 0)
Records: make([]jsonRecord, 0)}
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(features)) wg.Add(len(features))
for name := range features { for name := range features {
column := &jsonColumn{} response.Columns[name] = new(jsonColumn)
prepareColumn(request, entries, foundEntries, features, modes, name, column, &wg) go prepareColumn(
response.Columns[name] = column request.Resolution,
request.MinScore,
allEntries,
matchedEntries,
features,
modes,
name,
response.Columns[name],
&wg)
} }
for index, record := range foundEntries { wg.Wait()
for index, entry := range matchedEntries {
if index >= request.MaxResults { if index >= request.MaxResults {
break break
} }
item := jsonRecord{ item := jsonRecord{
Name: record.name, Name: entry.name,
Url: record.url, Url: entry.url,
Score: record.score, Score: entry.score,
Compatibility: record.compatibility, Compatibility: entry.compatibility,
DistanceToUser: record.distanceToUser, DistanceToUser: entry.distanceToUser,
DistanceToStn: record.distanceToStn, DistanceToStn: entry.distanceToStn,
ClosestStn: record.closestStn, ClosestStn: entry.closestStn,
AccessCount: record.accessCount, AccessCount: entry.accessCount,
Id: record.id} Id: entry.id}
response.Records = append(response.Records, item) response.Records = append(response.Records, item)
} }
@ -195,15 +224,17 @@ func getCategories(rw http.ResponseWriter, req *http.Request) {
} }
func addCategory(rw http.ResponseWriter, req *http.Request) { func addCategory(rw http.ResponseWriter, req *http.Request) {
var request struct { var (
request struct {
Description string `json:"description"` Description string `json:"description"`
} }
var response struct { response struct {
Description string `json:"description"` Description string `json:"description"`
Id int `json:"id"` Id int `json:"id"`
Success bool `json:"success"` Success bool `json:"success"`
} }
)
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
@ -242,13 +273,15 @@ func addCategory(rw http.ResponseWriter, req *http.Request) {
} }
func removeCategory(rw http.ResponseWriter, req *http.Request) { func removeCategory(rw http.ResponseWriter, req *http.Request) {
var request struct { var (
request struct {
Id int `json:"id"` Id int `json:"id"`
} }
var response struct { response struct {
Success bool `json:"success"` Success bool `json:"success"`
} }
)
if err := json.NewDecoder(req.Body).Decode(&request); err != nil { if err := json.NewDecoder(req.Body).Decode(&request); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
@ -360,7 +393,6 @@ func main() {
go func() { go func() {
<-c <-c
log.Print("interrupted")
pprof.StopCPUProfile() pprof.StopCPUProfile()
os.Exit(1) os.Exit(1)
}() }()

View File

@ -42,19 +42,6 @@ type jsonGeoData struct {
Longitude float64 `json:"longitude"` Longitude float64 `json:"longitude"`
} }
type jsonQueryRequest struct {
Features featureMap `json:"features"`
Geo *jsonGeoData `json:"geo"`
MaxResults int `json:"maxResults"`
MinScore float64 `json:"minScore"`
Modes map[string]string `json:"modes"`
Profile featureMap `json:"profile"`
Resolution int `json:"resolution"`
SortAsc bool `json:"sortAsc"`
SortKey string `json:"sortKey"`
WalkingDist float64 `json:"walkingDist"`
}
type jsonColumn struct { type jsonColumn struct {
Bracket jsonBracket `json:"bracket"` Bracket jsonBracket `json:"bracket"`
Hints []jsonProjection `json:"hints"` Hints []jsonProjection `json:"hints"`
@ -86,14 +73,6 @@ type jsonBracket struct {
Max float64 `json:"max"` Max float64 `json:"max"`
} }
type jsonQueryResponse struct {
Columns map[string]*jsonColumn `json:"columns"`
Count int `json:"count"`
MinScore float64 `json:"minScore"`
Records []jsonRecord `json:"records"`
ElapsedTime int64 `json:"elapsedTime"`
}
type queryContext struct { type queryContext struct {
geo *geoData geo *geoData
profile featureMap profile featureMap

View File

@ -298,7 +298,7 @@ func getRecords(context queryContext) records {
entry := record{ entry := record{
name: name, name: name,
url: "http://www.tripadvisor.com" + url, url: url,
distanceToStn: distanceToStn, distanceToStn: distanceToStn,
closestStn: closestStn, closestStn: closestStn,
accessCount: accessCount, accessCount: accessCount,