func.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. /*
  11. Package interpreter contains the EQL interpreter.
  12. */
  13. package interpreter
  14. import (
  15. "errors"
  16. "fmt"
  17. "strconv"
  18. "strings"
  19. "time"
  20. "devt.de/krotik/common/datautil"
  21. "devt.de/krotik/common/errorutil"
  22. "devt.de/krotik/eliasdb/eql/parser"
  23. "devt.de/krotik/eliasdb/graph/data"
  24. )
  25. // Where related functions
  26. // =======================
  27. /*
  28. FuncWhere represents a where related function.
  29. */
  30. type FuncWhere func(astNode *parser.ASTNode, rtp *eqlRuntimeProvider,
  31. node data.Node, edge data.Edge) (interface{}, error)
  32. /*
  33. Runtime map for where related functions
  34. */
  35. var whereFunc = map[string]FuncWhere{
  36. "count": whereCount,
  37. "parseDate": whereParseDate,
  38. }
  39. /*
  40. whereCount counts reachable nodes via a given traversal.
  41. */
  42. func whereCount(astNode *parser.ASTNode, rtp *eqlRuntimeProvider,
  43. node data.Node, edge data.Edge) (interface{}, error) {
  44. // Check parameters
  45. np := len(astNode.Children)
  46. if np != 2 && np != 3 {
  47. return nil, rtp.newRuntimeError(ErrInvalidConstruct,
  48. "Count function requires 1 or 2 parameters: traversal spec, condition clause", astNode)
  49. }
  50. spec := astNode.Children[1].Token.Val
  51. // Only need to retrieve full node values if there is a where clause
  52. nodes, _, err := rtp.gm.TraverseMulti(rtp.part, node.Key(), node.Kind(), spec, np == 3)
  53. if np == 3 {
  54. var filteredNodes []data.Node
  55. // If a where clause was given parse it and evaluate it
  56. conditionString := astNode.Children[2].Token.Val
  57. ast, err := parser.ParseWithRuntime("count condition", "get _ where "+conditionString, &GetRuntimeProvider{rtp})
  58. if err != nil {
  59. return nil, rtp.newRuntimeError(ErrInvalidConstruct,
  60. fmt.Sprintf("Invalid condition clause in count function: %s", err), astNode)
  61. }
  62. cond := ast.Children[1] // This should always pick out just the where clause
  63. errorutil.AssertOk(cond.Runtime.Validate()) // Validation should alwasys succeed
  64. for _, n := range nodes {
  65. res, err := cond.Children[0].Runtime.(CondRuntime).CondEval(n, nil)
  66. if err != nil {
  67. return nil, rtp.newRuntimeError(ErrInvalidConstruct,
  68. fmt.Sprintf("Invalid condition clause in count function: %s", err), astNode)
  69. } else if b, ok := res.(bool); ok {
  70. if b {
  71. filteredNodes = append(filteredNodes, n)
  72. }
  73. } else {
  74. return nil, rtp.newRuntimeError(ErrInvalidConstruct,
  75. "Could not evaluate condition clause in count function", astNode)
  76. }
  77. }
  78. nodes = filteredNodes
  79. }
  80. return len(nodes), err
  81. }
  82. /*
  83. whereParseDate converts a date string into a unix time value.
  84. */
  85. func whereParseDate(astNode *parser.ASTNode, rtp *eqlRuntimeProvider,
  86. node data.Node, edge data.Edge) (interface{}, error) {
  87. var datestr interface{}
  88. var t time.Time
  89. var ret int64
  90. var err error
  91. // Define default layout
  92. layout := time.RFC3339
  93. // Check parameters
  94. if len(astNode.Children) < 2 {
  95. return nil, rtp.newRuntimeError(ErrInvalidConstruct,
  96. "parseDate function requires 1 parameter: date string", astNode)
  97. }
  98. if len(astNode.Children) > 2 {
  99. datestr, err = astNode.Children[2].Runtime.(CondRuntime).CondEval(node, edge)
  100. layout = fmt.Sprint(datestr)
  101. }
  102. if err == nil {
  103. // Convert the date string
  104. datestr, err = astNode.Children[1].Runtime.(CondRuntime).CondEval(node, edge)
  105. if err == nil {
  106. t, err = time.Parse(layout, fmt.Sprint(datestr))
  107. if err == nil {
  108. ret = t.Unix()
  109. }
  110. }
  111. }
  112. return ret, err
  113. }
  114. // Show related functions
  115. // ======================
  116. /*
  117. Runtime map for show related functions
  118. */
  119. var showFunc = map[string]FuncShowInst{
  120. "count": showCountInst,
  121. "objget": showObjgetInst,
  122. }
  123. /*
  124. FuncShow is the interface definition for show related functions
  125. */
  126. type FuncShow interface {
  127. /*
  128. name returns the name of the function.
  129. */
  130. name() string
  131. /*
  132. eval runs the function. Returns the result and a source for the result.
  133. The source should be a concrete node/edge key and kind or a query and
  134. should be returned in either of the following formats:
  135. n:<key>:<kind> for a node
  136. e:<key>:<kind> for an edge
  137. q:<query> for a query
  138. */
  139. eval(node data.Node, edge data.Edge) (interface{}, string, error)
  140. }
  141. /*
  142. FuncShowInst creates a function object. Returns which column data should be queried and
  143. how the colummn should be named.
  144. */
  145. type FuncShowInst func(astNode *parser.ASTNode, rtp *eqlRuntimeProvider) (FuncShow, string, string, error)
  146. // Show Count
  147. // ----------
  148. /*
  149. showCountInst creates a new showCount object.
  150. */
  151. func showCountInst(astNode *parser.ASTNode, rtp *eqlRuntimeProvider) (FuncShow, string, string, error) {
  152. var cond *parser.ASTNode
  153. // Check parameters
  154. np := len(astNode.Children)
  155. if np != 3 && np != 4 {
  156. return nil, "", "", errors.New("Count function requires 2 or 3 parameters: traversal step, traversal spec, condition clause")
  157. }
  158. pos := astNode.Children[1].Token.Val
  159. spec := astNode.Children[2].Token.Val
  160. if np == 4 {
  161. // If a condition clause was given parse it
  162. condString := astNode.Children[3].Token.Val
  163. ast, err := parser.ParseWithRuntime("count condition", "get _ where "+condString, &GetRuntimeProvider{rtp})
  164. if err != nil {
  165. return nil, "", "", fmt.Errorf("Invalid condition clause in count function: %s", err)
  166. }
  167. cond = ast.Children[1] // This should always pick out just the condition clause
  168. errorutil.AssertOk(cond.Runtime.Validate()) // Validation should alwasys succeed
  169. }
  170. return &showCount{rtp, astNode, spec, cond}, pos + ":n:key", "Count", nil
  171. }
  172. /*
  173. showCount is the number of reachable nodes via a given traversal spec.
  174. */
  175. type showCount struct {
  176. rtp *eqlRuntimeProvider
  177. astNode *parser.ASTNode
  178. spec string
  179. condition *parser.ASTNode
  180. }
  181. /*
  182. name returns the name of the function.
  183. */
  184. func (sc *showCount) name() string {
  185. return "count"
  186. }
  187. /*
  188. eval counts reachable nodes via a given traversal.
  189. */
  190. func (sc *showCount) eval(node data.Node, edge data.Edge) (interface{}, string, error) {
  191. condString := ""
  192. // Only need to retrieve full node values if there is a where clause
  193. nodes, _, err := sc.rtp.gm.TraverseMulti(sc.rtp.part, node.Key(), node.Kind(), sc.spec, sc.condition != nil)
  194. if err != nil {
  195. return nil, "", err
  196. }
  197. if sc.condition != nil {
  198. var filteredNodes []data.Node
  199. // If there is a condition clause filter the result
  200. condString, _ = parser.PrettyPrint(sc.condition)
  201. for _, n := range nodes {
  202. res, err := sc.condition.Children[0].Runtime.(CondRuntime).CondEval(n, nil)
  203. if err != nil {
  204. return nil, "", err
  205. } else if b, ok := res.(bool); ok {
  206. if b {
  207. filteredNodes = append(filteredNodes, n)
  208. }
  209. } else {
  210. return nil, "", sc.rtp.newRuntimeError(ErrInvalidConstruct,
  211. "Could not evaluate condition clause in count function", sc.astNode)
  212. }
  213. }
  214. nodes = filteredNodes
  215. }
  216. srcQuery := fmt.Sprintf("q:lookup %s %s traverse %s %s end show 2:n:%s, 2:n:%s, 2:n:%s",
  217. node.Kind(), strconv.Quote(node.Key()), sc.spec, condString, data.NodeKey, data.NodeKind, data.NodeName)
  218. return len(nodes), srcQuery, nil
  219. }
  220. // Show Objget
  221. // -----------
  222. /*
  223. showObjgetInst creates a new showObjget object.
  224. */
  225. func showObjgetInst(astNode *parser.ASTNode, rtp *eqlRuntimeProvider) (FuncShow, string, string, error) {
  226. // Check parameters
  227. if len(astNode.Children) != 4 {
  228. return nil, "", "",
  229. fmt.Errorf("Objget function requires 3 parameters: traversal step, attribute name, path to value")
  230. }
  231. pos := astNode.Children[1].Token.Val
  232. attr := astNode.Children[2].Token.Val
  233. path := astNode.Children[3].Token.Val
  234. return &showObjget{rtp, attr, strings.Split(path, ".")}, pos + ":n:" + attr,
  235. rtp.ni.AttributeDisplayString("", attr) + "." + path, nil
  236. }
  237. /*
  238. showObjget reaches into an object and extracts a value.
  239. */
  240. type showObjget struct {
  241. rtp *eqlRuntimeProvider
  242. attr string
  243. path []string
  244. }
  245. /*
  246. name returns the name of the function.
  247. */
  248. func (so *showObjget) name() string {
  249. return "objget"
  250. }
  251. /*
  252. eval reaches into an object and extracts a value.
  253. */
  254. func (so *showObjget) eval(node data.Node, edge data.Edge) (interface{}, string, error) {
  255. val := node.Attr(so.attr)
  256. if valMap, ok := val.(map[string]interface{}); ok {
  257. val, _ = datautil.GetNestedValue(valMap, so.path)
  258. }
  259. return val, "n:" + node.Kind() + ":" + node.Key(), nil
  260. }