func.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. // Convert the date string
  103. datestr, err = astNode.Children[1].Runtime.(CondRuntime).CondEval(node, edge)
  104. if err == nil {
  105. t, err = time.Parse(layout, fmt.Sprint(datestr))
  106. if err == nil {
  107. ret = t.Unix()
  108. }
  109. }
  110. return ret, err
  111. }
  112. // Show related functions
  113. // ======================
  114. /*
  115. Runtime map for show related functions
  116. */
  117. var showFunc = map[string]FuncShowInst{
  118. "count": showCountInst,
  119. "objget": showObjgetInst,
  120. }
  121. /*
  122. FuncShow is the interface definition for show related functions
  123. */
  124. type FuncShow interface {
  125. /*
  126. name returns the name of the function.
  127. */
  128. name() string
  129. /*
  130. eval runs the function. Returns the result and a source for the result.
  131. The source should be a concrete node/edge key and kind or a query and
  132. should be returned in either of the following formats:
  133. n:<key>:<kind> for a node
  134. e:<key>:<kind> for an edge
  135. q:<query> for a query
  136. */
  137. eval(node data.Node, edge data.Edge) (interface{}, string, error)
  138. }
  139. /*
  140. FuncShowInst creates a function object. Returns which column data should be queried and
  141. how the colummn should be named.
  142. */
  143. type FuncShowInst func(astNode *parser.ASTNode, rtp *eqlRuntimeProvider) (FuncShow, string, string, error)
  144. // Show Count
  145. // ----------
  146. /*
  147. showCountInst creates a new showCount object.
  148. */
  149. func showCountInst(astNode *parser.ASTNode, rtp *eqlRuntimeProvider) (FuncShow, string, string, error) {
  150. var cond *parser.ASTNode
  151. // Check parameters
  152. np := len(astNode.Children)
  153. if np != 3 && np != 4 {
  154. return nil, "", "", errors.New("Count function requires 2 or 3 parameters: traversal step, traversal spec, condition clause")
  155. }
  156. pos := astNode.Children[1].Token.Val
  157. spec := astNode.Children[2].Token.Val
  158. if np == 4 {
  159. // If a condition clause was given parse it
  160. condString := astNode.Children[3].Token.Val
  161. ast, err := parser.ParseWithRuntime("count condition", "get _ where "+condString, &GetRuntimeProvider{rtp})
  162. if err != nil {
  163. return nil, "", "", fmt.Errorf("Invalid condition clause in count function: %s", err)
  164. }
  165. cond = ast.Children[1] // This should always pick out just the condition clause
  166. errorutil.AssertOk(cond.Runtime.Validate()) // Validation should alwasys succeed
  167. }
  168. return &showCount{rtp, astNode, spec, cond}, pos + ":n:key", "Count", nil
  169. }
  170. /*
  171. showCount is the number of reachable nodes via a given traversal spec.
  172. */
  173. type showCount struct {
  174. rtp *eqlRuntimeProvider
  175. astNode *parser.ASTNode
  176. spec string
  177. condition *parser.ASTNode
  178. }
  179. /*
  180. name returns the name of the function.
  181. */
  182. func (sc *showCount) name() string {
  183. return "count"
  184. }
  185. /*
  186. eval counts reachable nodes via a given traversal.
  187. */
  188. func (sc *showCount) eval(node data.Node, edge data.Edge) (interface{}, string, error) {
  189. condString := ""
  190. // Only need to retrieve full node values if there is a where clause
  191. nodes, _, err := sc.rtp.gm.TraverseMulti(sc.rtp.part, node.Key(), node.Kind(), sc.spec, sc.condition != nil)
  192. if err != nil {
  193. return nil, "", err
  194. }
  195. if sc.condition != nil {
  196. var filteredNodes []data.Node
  197. // If there is a condition clause filter the result
  198. condString, _ = parser.PrettyPrint(sc.condition)
  199. for _, n := range nodes {
  200. res, err := sc.condition.Children[0].Runtime.(CondRuntime).CondEval(n, nil)
  201. if err != nil {
  202. return nil, "", err
  203. } else if b, ok := res.(bool); ok {
  204. if b {
  205. filteredNodes = append(filteredNodes, n)
  206. }
  207. } else {
  208. return nil, "", sc.rtp.newRuntimeError(ErrInvalidConstruct,
  209. "Could not evaluate condition clause in count function", sc.astNode)
  210. }
  211. }
  212. nodes = filteredNodes
  213. }
  214. srcQuery := fmt.Sprintf("q:lookup %s %s traverse %s %s end show 2:n:%s, 2:n:%s, 2:n:%s",
  215. node.Kind(), strconv.Quote(node.Key()), sc.spec, condString, data.NodeKey, data.NodeKind, data.NodeName)
  216. return len(nodes), srcQuery, nil
  217. }
  218. // Show Objget
  219. // -----------
  220. /*
  221. showObjgetInst creates a new showObjget object.
  222. */
  223. func showObjgetInst(astNode *parser.ASTNode, rtp *eqlRuntimeProvider) (FuncShow, string, string, error) {
  224. // Check parameters
  225. if len(astNode.Children) != 4 {
  226. return nil, "", "",
  227. fmt.Errorf("Objget function requires 3 parameters: traversal step, attribute name, path to value")
  228. }
  229. pos := astNode.Children[1].Token.Val
  230. attr := astNode.Children[2].Token.Val
  231. path := astNode.Children[3].Token.Val
  232. return &showObjget{rtp, attr, strings.Split(path, ".")}, pos + ":n:" + attr,
  233. rtp.ni.AttributeDisplayString("", attr) + "." + path, nil
  234. }
  235. /*
  236. showObjget reaches into an object and extracts a value.
  237. */
  238. type showObjget struct {
  239. rtp *eqlRuntimeProvider
  240. attr string
  241. path []string
  242. }
  243. /*
  244. name returns the name of the function.
  245. */
  246. func (so *showObjget) name() string {
  247. return "objget"
  248. }
  249. /*
  250. eval reaches into an object and extracts a value.
  251. */
  252. func (so *showObjget) eval(node data.Node, edge data.Edge) (interface{}, string, error) {
  253. val := node.Attr(so.attr)
  254. if valMap, ok := val.(map[string]interface{}); ok {
  255. val, _ = datautil.GetNestedValue(valMap, so.path)
  256. }
  257. return val, "n:" + node.Kind() + ":" + node.Key(), nil
  258. }