PageRenderTime 134ms CodeModel.GetById 37ms RepoModel.GetById 10ms app.codeStats 0ms

/model.go

https://bitbucket.org/matchmove/go-cqlorm
Go | 360 lines | 246 code | 79 blank | 35 comment | 58 complexity | 2ab78d1340e9402399e891486c84b61e MD5 | raw file
  1. package cqlorm
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "reflect"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "bitbucket.org/matchmove/go-resource/param"
  11. "bitbucket.org/matchmove/go-tools/secure"
  12. valid "bitbucket.org/matchmove/go-valid"
  13. "github.com/gocql/gocql"
  14. "gopkg.in/validator.v2"
  15. )
  16. const (
  17. // ISO8601Date ISO 8601 format with just the date
  18. ISO8601Date = "2006-01-02"
  19. // SQLDatetime YYYY-MM-DD HH:II:SS format with the date and time
  20. SQLDatetime = "2006-01-02 15:04:05"
  21. // TimestampFormat Timestamp Format
  22. TimestampFormat = "20060102150405"
  23. // MaxLimit ...
  24. MaxLimit = 50
  25. // MinOffset ...
  26. MinOffset = 0
  27. // SQLNullDate default null date
  28. SQLNullDate = "0000-00-00 00:00:00"
  29. // SQLNoDatabaseConnection No database connection
  30. SQLNoDatabaseConnection = "No database connection"
  31. // SQLInvalidOperator Invalid sql operator
  32. SQLInvalidOperator = "Invalid sql operator"
  33. //LimitRangeErrorCode limit must be a value 1 to 50
  34. LimitRangeErrorCode = "limit must be a value 1 to 50"
  35. //OffsetRangeErrorCode offset must be >= 0
  36. OffsetRangeErrorCode = "offset must be >= 0"
  37. // SQLNoRowsErrorCode sql: no rows in result set
  38. SQLNoRowsErrorCode = "sql: no rows in result set"
  39. // JSONTag ...
  40. JSONTag = "json"
  41. //FieldPlaceholder ...
  42. FieldPlaceholder = "?"
  43. //DatabaseTag ...
  44. DatabaseTag = "db"
  45. // EmptyUUID ...
  46. EmptyUUID = "00000000-0000-0000-0000-000000000000"
  47. )
  48. // OrderByIDDesc desc
  49. var OrderByIDDesc = []string{"-id"}
  50. // Model represents the core model
  51. type Model struct {
  52. Session *gocql.Session
  53. Table string `json:"-"`
  54. Conditions Conditions `json:"-"`
  55. L int `json:"-"`
  56. LastID string `json:"-"`
  57. SortOrder []string `json:"-"`
  58. Consistency gocql.Consistency
  59. }
  60. // Record ...
  61. type Record map[string]interface{}
  62. // Records ...
  63. type Records []Record
  64. // UnixTimestamp return utc timestamp
  65. func UnixTimestamp() string {
  66. return fmt.Sprintf("%d", time.Now().Unix())
  67. }
  68. // UnixToMysqlTime return utc timestamp
  69. func UnixToMysqlTime(sec string, nsec string) string {
  70. iSec, _ := strconv.ParseInt(sec, 10, 64)
  71. iNsec, _ := strconv.ParseInt(nsec, 10, 64)
  72. return time.Unix(iSec, iNsec).Format(SQLDatetime)
  73. }
  74. func init() {
  75. InitValidators()
  76. }
  77. // InitValidators Initializes the validations using github.com/go-validator/validator
  78. func InitValidators() {
  79. validator.SetValidationFunc("pattern", valid.Pattern)
  80. validator.SetValidationFunc("sqlvalue", valid.SQLValue)
  81. validator.SetValidationFunc("required", valid.Required)
  82. validator.SetValidationFunc("greater", valid.GreaterThan)
  83. validator.SetValidationFunc("gender", valid.Gender)
  84. validator.SetValidationFunc("url", valid.URL)
  85. }
  86. // FindFirst find first instance of record
  87. func (me *Model) FindFirst(dst interface{}) (record Record, err error) {
  88. var (
  89. args []interface{}
  90. query string
  91. )
  92. me.Limit(1)
  93. record = make(Record)
  94. if query, args, err = me.SelectCQL(dst); err != nil {
  95. return
  96. }
  97. if err = me.Session.Query(query, args...).Consistency(me.Consistency).MapScan(record); err != nil {
  98. return
  99. }
  100. return
  101. }
  102. // Find find all match
  103. func (me *Model) Find(dst interface{}) (iterations *gocql.Iter, err error) {
  104. var (
  105. args []interface{}
  106. query string
  107. )
  108. if me.L == 0 {
  109. me.Limit(MaxLimit)
  110. }
  111. if query, args, err = me.SelectCQL(dst); err != nil {
  112. return
  113. }
  114. iterations = me.Session.Query(query, args...).Consistency(me.Consistency).Iter()
  115. return
  116. }
  117. // SQL ...
  118. func (me *Model) SQL(s string) string {
  119. return fmt.Sprintf(s, me.Table)
  120. }
  121. // DeleteCQL creates an sql delete statement
  122. func (me *Model) DeleteCQL(dst interface{}) (query string, args []interface{}, err error) {
  123. if query, args, err = me.PrepareStatement(dst); err != nil {
  124. return
  125. }
  126. query = me.SQL(`DELETE FROM %s ` + query)
  127. return
  128. }
  129. // SelectCQL creates an sql select statement
  130. func (me *Model) SelectCQL(dst interface{}) (query string, args []interface{}, err error) {
  131. if query, args, err = me.PrepareStatement(dst); err != nil {
  132. return
  133. }
  134. query = me.SQL(`SELECT * FROM %s ` + query)
  135. return
  136. }
  137. // UpdateCQL creates an sql update statement
  138. func (me *Model) UpdateCQL(dst interface{}) (query string) {
  139. var (
  140. set string
  141. )
  142. t := reflect.ValueOf(dst).Elem()
  143. for i := 0; i < t.NumField(); i++ {
  144. f := t.Type().Field(i)
  145. items := strings.Split(f.Tag.Get("db"), ",")
  146. if len(items) == 0 {
  147. continue
  148. }
  149. tag := items[0]
  150. if "" == tag || "-" == tag || tag == "id" || tag == "created_at" {
  151. continue
  152. }
  153. if len(set) == 0 {
  154. set = set + tag + " = ? "
  155. } else {
  156. set = set + "," + tag + " = ? "
  157. }
  158. }
  159. query = me.SQL(`UPDATE %s SET ` + set + "WHERE id = " + FieldPlaceholder)
  160. return
  161. }
  162. // InsertCQL creates an sql insert statement
  163. func (me *Model) InsertCQL(dst interface{}) (query string) {
  164. set := ""
  165. values := ""
  166. t := reflect.ValueOf(dst).Elem()
  167. for i := 0; i < t.NumField(); i++ {
  168. f := t.Type().Field(i)
  169. items := strings.Split(f.Tag.Get(DatabaseTag), ",")
  170. if len(items) == 0 {
  171. continue
  172. }
  173. tag := items[0]
  174. if "" == tag || "-" == tag {
  175. continue
  176. }
  177. set = set + tag
  178. values = values + FieldPlaceholder
  179. if i < t.NumField()-1 {
  180. set = set + ", "
  181. values = values + ", "
  182. } else {
  183. set = set + " "
  184. values = values + " "
  185. }
  186. }
  187. query = me.SQL(`INSERT INTO %s (` + set + `) VALUES (` + values + `)`)
  188. return
  189. }
  190. // Limit set the limit for the query
  191. func (me *Model) Limit(limit int) error {
  192. if limit < 1 || limit > 50 {
  193. return errors.New(LimitRangeErrorCode)
  194. }
  195. me.L = limit
  196. return nil
  197. }
  198. // Where set the conditions for the query
  199. func (me *Model) Where(field string, operator string, value interface{}) (err error) {
  200. me.Conditions = append(me.Conditions, Condition{
  201. Field: field,
  202. Operator: operator,
  203. Value: value,
  204. })
  205. if _, _, err = me.Filters(me.Conditions); err != nil {
  206. return
  207. }
  208. return
  209. }
  210. // Filters ...
  211. func (me *Model) Filters(filters Conditions) (filter []string, args []interface{}, err error) {
  212. var (
  213. f, v string
  214. )
  215. for _, cond := range filters {
  216. if err = cond.Validate(); err != nil {
  217. return
  218. }
  219. v = fmt.Sprint(cond.Value)
  220. if OperatorLike == cond.Operator {
  221. if v, err = param.ParseFilter(v, param.FilterRegexMatchAll); nil != err {
  222. return
  223. }
  224. }
  225. f = fmt.Sprintf("%s %s %s", cond.Field, cond.Operator, FieldPlaceholder)
  226. args = append(args, v)
  227. filter = append(filter, f)
  228. }
  229. return
  230. }
  231. // PrepareStatement initialize the query
  232. func (me *Model) PrepareStatement(model interface{}) (query string, args []interface{}, err error) {
  233. var (
  234. filters []string
  235. order, filter, limit string
  236. )
  237. if len(me.SortOrder) > 0 {
  238. order = "ORDER BY " + strings.Join(me.SortOrder, ", ")
  239. }
  240. if len(me.Conditions) > 0 {
  241. if filters, args, err = me.Filters(me.Conditions); err != nil {
  242. return
  243. }
  244. filter = "WHERE " + strings.Join(filters, " AND ")
  245. }
  246. if me.L != 0 {
  247. limit = fmt.Sprintf(`LIMIT %d`, me.L)
  248. }
  249. query = filter + ` ` + order + ` ` + limit
  250. return
  251. }
  252. // JSON ...
  253. func (me *Model) JSON(model interface{}) string {
  254. body, _ := json.Marshal(model)
  255. return string(body)
  256. }
  257. // GenerateERN ...
  258. func (me Model) GenerateERN(service string, resource string, args ...string) (ern string, err error) {
  259. var (
  260. prefix string
  261. )
  262. if len(service) != 3 {
  263. err = errors.New("Service must be 3 in lenght")
  264. }
  265. if len(resource) > 6 {
  266. err = errors.New("Resource must less than 6 in lenght")
  267. }
  268. if len(args) > 0 {
  269. programCode := args[0]
  270. if len(programCode) > 8 {
  271. err = errors.New("ProgramCode must less than 8 in lenght")
  272. }
  273. prefix = fmt.Sprintf("%s:%s:%s", service, resource, programCode)
  274. } else {
  275. prefix = fmt.Sprintf("%s:%s", service, resource)
  276. }
  277. hashLength := (31 - len(prefix))
  278. hash := fmt.Sprintf("%s%s", UnixTimestamp(), secure.RandomString(hashLength-len(UnixTimestamp()), []rune(secure.RuneAlNumCS)))
  279. ern = strings.ToUpper(fmt.Sprintf("%s:%s", prefix, hash))
  280. return
  281. }