Commit e203bf44 authored by zauberstuhl's avatar zauberstuhl

Add Helper method SelectStructFields

this will improve api performance in
respect of slow network connectivity e.g. mobile devices
parent a8e1f8e4
...@@ -22,8 +22,8 @@ import ( ...@@ -22,8 +22,8 @@ import (
"github.com/revel/revel" "github.com/revel/revel"
"regexp" "regexp"
"errors" "errors"
"strings"
"strconv" "strconv"
apiHelpers "gopkg.in/ganggo/api.v0/app/helpers"
"gopkg.in/ganggo/ganggo.v0/app/helpers" "gopkg.in/ganggo/ganggo.v0/app/helpers"
"gopkg.in/ganggo/ganggo.v0/app/models" "gopkg.in/ganggo/ganggo.v0/app/models"
"gopkg.in/ganggo/ganggo.v0/app/jobs" "gopkg.in/ganggo/ganggo.v0/app/jobs"
...@@ -58,11 +58,8 @@ func (p ApiPost) Index() revel.Result { ...@@ -58,11 +58,8 @@ func (p ApiPost) Index() revel.Result {
return p.RenderApiError(err) return p.RenderApiError(err)
} }
f := strings.Split(fields, ",") return p.RenderJSON(
apiHelpers.SelectStructFields(posts, fields))
p.Log.Debug("optimized queries", "fields", f)
return p.RenderJSON(posts.SelectFields(f...))
} }
func (p ApiPost) Create() revel.Result { func (p ApiPost) Create() revel.Result {
...@@ -152,6 +149,9 @@ func (p ApiPost) Create() revel.Result { ...@@ -152,6 +149,9 @@ func (p ApiPost) Create() revel.Result {
func (p ApiPost) Show(guid string) revel.Result { func (p ApiPost) Show(guid string) revel.Result {
var post models.Post var post models.Post
var fields string
p.Params.Bind(&fields, "fields")
user, err := models.CurrentUser(p.Params, p.Session) user, err := models.CurrentUser(p.Params, p.Session)
if err != nil { if err != nil {
...@@ -170,7 +170,8 @@ func (p ApiPost) Show(guid string) revel.Result { ...@@ -170,7 +170,8 @@ func (p ApiPost) Show(guid string) revel.Result {
return p.RenderApiError(err) return p.RenderApiError(err)
} }
return p.RenderJSON(post) return p.RenderJSON(
apiHelpers.SelectStructFields(post, fields))
} }
func (p ApiPost) Delete() revel.Result { func (p ApiPost) Delete() revel.Result {
......
package helpers
//
// GangGo API Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import (
"reflect"
"github.com/fatih/structs"
"regexp"
"strconv"
)
// select only a few fields instead of the whole struct
// this will optimize mobile app performance if on slow networks
func SelectStructFields(n interface{}, fieldParam string) interface{} {
kind := reflect.TypeOf(n).Kind()
if kind == reflect.Slice {
val := reflect.ValueOf(n)
m := make(map[int]interface{}, val.Len())
for i := 0; i < val.Len(); i++ {
model := val.Index(i).Interface()
m[i] = reduceStructField(structs.Map(model), fieldParam)
}
return m
} else if kind == reflect.Struct {
return reduceStructField(structs.Map(n), fieldParam)
}
panic("Cannot type cast to slice or struct")
}
// Iterate through the structure and remove unnecessary fields
func reduceStructField(m map[string]interface{}, fieldParam string) map[string]interface{} {
f, s := parseFieldParam(fieldParam)
if len(f) <= 0 && len(s) <= 0{
return m
}
newMap := make(map[string]interface{}, len(f) + len(s))
for _, field := range f {
if _, ok := m[field]; ok {
newMap[field] = m[field]
}
}
for _, subField := range s {
field := subField[1]
if _, ok := m[field]; ok {
kind := reflect.TypeOf(m[field]).Kind()
if kind == reflect.Map {
model := m[field].(map[string]interface{})
newMap[field] = reduceStructField(model, subField[3])
} else if kind == reflect.Slice {
models := m[field].([]interface{})
newModelMap := make(map[string]interface{}, len(models))
for i, m := range models {
model := m.(map[string]interface{})
newModelMap[strconv.Itoa(i)] = reduceStructField(model, subField[3])
}
newMap[field] = newModelMap
}
}
}
return newMap
}
// This will parse the field parameter and returns two arrays.
// The first one will contain 1th level information the second one
// will contain struct of struct information:
//
// fields=ID,Person(ID:Author)
//
// will be parsed into
// []string=ID
// and
// [][]string=Person(ID:Author)
//
func parseFieldParam(fieldParam string) ([]string, [][]string) {
re := regexp.MustCompile(`(\w+?)(\(([\w:\(\)]+)\),{0,1})`)
s := re.FindAllStringSubmatch(fieldParam, -1)
fieldParam = re.ReplaceAllString(fieldParam, "")
re = regexp.MustCompile(`[^,:]+`)
f := re.FindAllString(fieldParam, -1)
return f, s
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment