Skip to content

Commit

Permalink
Merge pull request #10 from hatch-is/update-filter
Browse files Browse the repository at this point in the history
Update filter
  • Loading branch information
andreychuk authored Dec 22, 2016
2 parents aa8317d + 1da1de1 commit 2ff342c
Show file tree
Hide file tree
Showing 28 changed files with 7,617 additions and 12 deletions.
12 changes: 12 additions & 0 deletions Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

281 changes: 281 additions & 0 deletions filter/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
package filter

import (
"reflect"
"strconv"
"strings"
"time"
"unicode"

"github.com/pquerna/ffjson/ffjson"
"gopkg.in/mgo.v2/bson"
)

const (
id = "id"
_id = "_id"
desc = "desc"
//ID ...
ID = "ID"
)

//Filter describe fiter query
type Filter struct {
skip int
limit int
query bson.M
sort []string
search string
}

//BeforeFilter ...
type BeforeFilter struct {
Skip interface{}
Limit interface{}
Where map[string]interface{}
Sort map[string]string
Search string
}

func (bf *BeforeFilter) getSort() []string {
var sort []string
sort = make([]string, 0)
for key, value := range bf.Sort {
if desc == strings.ToLower(value) {
key = "-" + key
}
sort = append(sort, key)
}
return sort
}

func (bf *BeforeFilter) getLimit() int {
return convertParam(bf.Limit)
}

func (bf *BeforeFilter) getSkip() int {
return convertParam(bf.Skip)
}

//convertParam to convert skip/limit to int
//by default return 0
func convertParam(param interface{}) int {
var result int
switch param.(type) {
default:
return int(0)
case float64:
result = int(param.(float64))
case string:
t, err := strconv.Atoi(param.(string))
if err != nil {
result = int(0)
}
result = t
}
if result < 0 {
return int(0)
}
return result
}

//GetSkip ...
func (f *Filter) GetSkip() int {
return f.skip
}

//GetLimit ...
func (f *Filter) GetLimit() int {
return f.limit
}

//GetSearch ...
func (f *Filter) GetSearch() string {
return f.search
}

//GetSort ...
func (f *Filter) GetSort() []string {
return f.sort
}

//GetQuery ...
func (f *Filter) GetQuery() bson.M {
return f.query
}

//AddQuery ...
func (f *Filter) AddQuery(q bson.M) {
if f.query == nil {
f.query = q
} else {
f.query["$and"] = append(f.query["$and"].([]bson.M), q)
}
}

//GetFilterData ...
func GetFilterData(filter string, t interface{}) Filter {
var f Filter
if filter != "" {
var tmp BeforeFilter
ffjson.Unmarshal([]byte(filter), &tmp)
f.limit = tmp.getLimit()
f.skip = tmp.getSkip()
f.sort = tmp.getSort()
f.query = tmp.parseWhere(t)
}
return f
}

func (bf *BeforeFilter) parseWhere(t interface{}) bson.M {
var q bson.M
for key, values := range bf.Where {
if key == "and" || key == "or" {
key = "$" + key
q = parseAnd(key, values.([]interface{}), q, t)
} else {
q = parse(key, values, q, t)
}
}
return q
}

func parse(key string, values interface{}, q bson.M, t interface{}) bson.M {
if key == id {
key = _id
}
val := reflect.ValueOf(t)
var fieldType string
if val.FieldByName(convertName(key)).IsValid() == true {
fieldType = val.FieldByName(convertName(key)).Type().String()
if fieldType == "*time.Time" || fieldType == "time.Time" {
if q == nil {
q = bson.M{"$and": []bson.M{}}
} else {
if q["$and"] == nil {
q["$and"] = []bson.M{}
}
}
q = parseFilterDate("$and", key, values, q)
}
if fieldType == "bson.ObjectId" {
q = parseID(key, values, q)
}
}
return q
}

func addKeyToQuery(key string, q bson.M) bson.M {
if q == nil {
q = bson.M{key: []bson.M{}}
} else {
q[key] = []bson.M{}
}
return q
}

func parseID(key string, values interface{}, q bson.M) bson.M {
q = addKeyToQuery(key, q)
for field, data := range values.(map[string]interface{}) {
ids := convertStringIDtoObjectID(data)
if len(ids) > 0 {
if field == "in" || field == "nin" || field == "all" {
q[key] = bson.M{"$" + field: ids}
}
}
}
return q
}

func parseAndIDData(key string, field string, data interface{}, q bson.M) bson.M {
if field == id {
field = _id
}
slice := data.(map[string]interface{})
for k, value := range slice {
IDS := convertStringIDtoObjectID(value)
if len(IDS) > 0 {
if k == "in" || k == "nin" || k == "all" {
q[key] = append(q[key].([]bson.M), bson.M{field: bson.M{"$" + k: IDS}})
}
}
}
return q
}

func convertStringIDtoObjectID(data interface{}) []bson.ObjectId {
var ids []bson.ObjectId
ids = make([]bson.ObjectId, 0)
switch data.(type) {
case []interface{}:
ids = prepareIDSArray(data.([]interface{}))
case string:
ids = prepareInIds(data.(string))
}
return ids
}

func prepareIDSArray(values []interface{}) []bson.ObjectId {
var ids []bson.ObjectId
ids = make([]bson.ObjectId, 0)
for _, id := range values {
ids = append(ids, bson.ObjectIdHex(id.(string)))
}
return ids
}

func parseAnd(key string, values []interface{}, q bson.M, t interface{}) bson.M {
val := reflect.ValueOf(t)
if q == nil {
q = bson.M{key: []bson.M{}}
} else {
q[key] = []bson.M{}
}
for _, value := range values {
for field, data := range value.(map[string]interface{}) {
fieldName := convertName(field)
var fieldType string
if val.FieldByName(fieldName).IsValid() == true {
fieldType = val.FieldByName(fieldName).Type().String()
if fieldType == "*time.Time" || fieldType == "time.Time" {
q = parseFilterDate(key, field, data, q)
}
if fieldType == "bson.ObjectId" {
q = parseAndIDData(key, field, data, q)
}
}
}
}
return q
}

func convertName(name string) string {
if name == id || name == _id {
return ID
}
newName := []rune(name)
newName[0] = unicode.ToUpper(newName[0])
return string(newName)
}

func parseFilterDate(key string, field string, data interface{}, q bson.M) bson.M {
for k, v := range data.(map[string]interface{}) {
if k == "lte" || k == "lt" || k == "gte" || k == "gt" {
t, err := time.Parse(time.RFC3339, v.(string))
if err == nil {
q[key] = append(q[key].([]bson.M), bson.M{field: bson.M{"$" + k: t}})
}
}
}
return q
}

func prepareInIds(sID string) []bson.ObjectId {
var ids []bson.ObjectId
ids = make([]bson.ObjectId, 0)
IDs := strings.Split(sID, ",")
for _, id := range IDs {
ids = append(ids, bson.ObjectIdHex(id))
}
return ids
}
16 changes: 7 additions & 9 deletions model/articles.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package model
import (
"encoding/json"
"io"
"knowledge-base/filter"
"knowledge-base/store"
"strconv"

Expand All @@ -17,18 +18,15 @@ type ArticlesModel struct{}
//Read get articles
func (artModel *ArticlesModel) Read(qFilter string) (result []store.Article, total int, left int, err error) {
artDb := store.ArticlesCollectionConnect()
result = make([]store.Article, 0)

query, isSort, err := artModel.convertFilter(qFilter)
if err != nil {
return
}
skip, limit := getSkipLimit(qFilter)
query["deleted"] = false
if isSort == false {
f := filter.GetFilterData(qFilter, store.Article{})

if f.GetSearch() == "" {
fields := bson.M{}
result, total, left, err = artDb.Read(query, fields, skip, limit)
result, total, left, err = artDb.Read(f.GetQuery(), fields, f.GetSkip(), f.GetLimit(), f.GetSort())
} else {
result, total, left, err = artDb.Search(query, skip, limit)
result, total, left, err = artDb.Search(f.GetQuery(), f.GetSkip(), f.GetLimit())
}

if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions store/articles.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,15 @@ func ArticlesCollectionConnect() *ArticlesCollection {
}

//Read return entries of Articles collections
func (art *ArticlesCollection) Read(query bson.M, fields bson.M, skip int, limit int) (result []Article, total int, left int, err error) {
func (art *ArticlesCollection) Read(query bson.M, fields bson.M, skip int, limit int, sort []string) (result []Article, total int, left int, err error) {
session, artCollection, err := art.conn.getSessionAndCollection(art.collection)
if err != nil {
return
}
defer session.Close()
query = checkUpdated(query)
result = make([]Article, 0)
err = artCollection.Find(query).Select(fields).Skip(skip).Limit(limit).All(&result)
err = artCollection.Find(query).Select(fields).Skip(skip).Limit(limit).Sort(sort...).All(&result)
if err != nil {
return
}
Expand Down Expand Up @@ -174,7 +175,7 @@ func (art *ArticlesCollection) Search(q bson.M, skip int, limit int) (result []A
return
}
defer session.Close()

q = checkUpdated(q)
fields := bson.M{
"score": bson.M{
"$meta": "textScore",
Expand Down
10 changes: 10 additions & 0 deletions store/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
//"gopkg.in/mgo.v2/bson"
"knowledge-base/conf"
)
Expand Down Expand Up @@ -59,3 +60,12 @@ func (c *MongoConnection) getSessionAndCollection(collectionName string) (sessio
}
return
}

func checkUpdated(q bson.M) bson.M {
if q == nil {
q = bson.M{"deleted": false}
} else if q["deleted"] == nil {
q["deleted"] = false
}
return q
}
Loading

0 comments on commit 2ff342c

Please sign in to comment.