node.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. package data
  11. import (
  12. "bytes"
  13. "encoding/json"
  14. "fmt"
  15. "sort"
  16. "strconv"
  17. )
  18. /*
  19. Node models nodes in the graph
  20. */
  21. type Node interface {
  22. /*
  23. Key returns a potentially non human-readable unique key for this node.
  24. */
  25. Key() string
  26. /*
  27. Name returns a human-readable name for this node.
  28. */
  29. Name() string
  30. /*
  31. Kind returns a human-readable kind for this node.
  32. */
  33. Kind() string
  34. /*
  35. Data returns the node data of this node.
  36. */
  37. Data() map[string]interface{}
  38. /*
  39. Attr returns an attribute of this node.
  40. */
  41. Attr(attr string) interface{}
  42. /*
  43. SetAttr sets an attribute of this node. Setting a nil
  44. value removes the attribute.
  45. */
  46. SetAttr(attr string, val interface{})
  47. /*
  48. IndexMap returns a representation of this node as a string map which
  49. can be used to provide a full-text search.
  50. */
  51. IndexMap() map[string]string
  52. /*
  53. String returns a string representation of this node.
  54. */
  55. String() string
  56. }
  57. /*
  58. NodeKey is the key attribute for a node
  59. */
  60. const NodeKey = "key"
  61. /*
  62. NodeName is the name attribute for a node
  63. */
  64. const NodeName = "name"
  65. /*
  66. NodeKind is the kind attribute for a node
  67. */
  68. const NodeKind = "kind"
  69. /*
  70. CopyNode returns a shallow copy of a given node.
  71. */
  72. func CopyNode(node Node) Node {
  73. ret := NewGraphNode()
  74. for k, v := range node.Data() {
  75. ret.SetAttr(k, v)
  76. }
  77. return ret
  78. }
  79. /*
  80. graphNode data structure.
  81. */
  82. type graphNode struct {
  83. data map[string]interface{} // Data which is held by this node
  84. }
  85. /*
  86. NewGraphNode creates a new Node instance.
  87. */
  88. func NewGraphNode() Node {
  89. return &graphNode{make(map[string]interface{})}
  90. }
  91. /*
  92. NewGraphNodeFromMap creates a new Node instance.
  93. */
  94. func NewGraphNodeFromMap(data map[string]interface{}) Node {
  95. return &graphNode{data}
  96. }
  97. /*
  98. Key returns a potentially non human-readable unique key for this node.
  99. */
  100. func (gn *graphNode) Key() string {
  101. return gn.stringAttr(NodeKey)
  102. }
  103. /*
  104. Kind returns a human-readable kind for this node.
  105. */
  106. func (gn *graphNode) Kind() string {
  107. return gn.stringAttr(NodeKind)
  108. }
  109. /*
  110. Data returns the node data of this node.
  111. */
  112. func (gn *graphNode) Data() map[string]interface{} {
  113. return gn.data
  114. }
  115. /*
  116. Name returns a human-readable name for this node.
  117. */
  118. func (gn *graphNode) Name() string {
  119. return gn.stringAttr(NodeName)
  120. }
  121. /*
  122. Attr returns an attribute of this node.
  123. */
  124. func (gn *graphNode) Attr(attr string) interface{} {
  125. val, _ := gn.data[attr]
  126. return val
  127. }
  128. /*
  129. SetAttr sets an attribute of this node. Setting a nil
  130. value removes the attribute.
  131. */
  132. func (gn *graphNode) SetAttr(attr string, val interface{}) {
  133. if val != nil {
  134. gn.data[attr] = val
  135. } else {
  136. delete(gn.data, attr)
  137. }
  138. }
  139. /*
  140. Return the value of an attribute as a string. Or an
  141. empty string if it can't be represented as a string.
  142. */
  143. func (gn *graphNode) stringAttr(attr string) string {
  144. val, found := gn.data[attr]
  145. if st, ok := val.(string); found && ok {
  146. return st
  147. } else if st, ok := val.(fmt.Stringer); found && ok {
  148. return st.String()
  149. } else if found {
  150. return fmt.Sprintf("%v", val)
  151. }
  152. return ""
  153. }
  154. /*
  155. IndexMap returns a representation of this node as a string map which
  156. can be used to provide a full-text search.
  157. */
  158. func (gn *graphNode) IndexMap() map[string]string {
  159. return createIndexMap(gn, func(attr string) bool {
  160. return attr == NodeKey || attr == NodeKind
  161. })
  162. }
  163. /*
  164. createIndexMap creates a representation of a node as a string map. A filter
  165. function can be specified to filters out specific attributes.
  166. */
  167. func createIndexMap(gn *graphNode, attFilter func(attr string) bool) map[string]string {
  168. var addMap func(prefix string, data map[string]interface{})
  169. ret := make(map[string]string)
  170. addMap = func(prefix string, data map[string]interface{}) {
  171. for key, val := range data {
  172. attr := prefix + key
  173. // Ignore attributes which are uninteresting for a full-text search
  174. if attFilter(attr) {
  175. continue
  176. }
  177. // Detect nested structures and recurse into them
  178. if valmap, ok := val.(map[string]interface{}); ok {
  179. addMap(prefix+key+".", valmap)
  180. }
  181. // See the type of val and print it accordingly - ignore byte slices
  182. if st, ok := val.(string); ok {
  183. // Value is actually a string - no change needed
  184. ret[attr] = st
  185. } else if st, ok := val.(fmt.Stringer); ok {
  186. // Value has a proper string representation - use that
  187. ret[attr] = st.String()
  188. } else if _, ok := val.([]byte); !ok {
  189. // For all other cases (except ignored byte slices) try first a
  190. // JSON representation
  191. jsonBytes, err := json.Marshal(val)
  192. jsonString := string(jsonBytes)
  193. if err == nil && jsonString != "{}" {
  194. ret[attr] = string(jsonString)
  195. } else {
  196. // Otherwise do best effort printing
  197. ret[attr] = fmt.Sprintf("%v", val)
  198. }
  199. }
  200. }
  201. }
  202. addMap("", gn.data)
  203. return ret
  204. }
  205. /*
  206. String returns a string representation of this node.
  207. */
  208. func (gn *graphNode) String() string {
  209. return dataToString("GraphNode", gn)
  210. }
  211. /*
  212. dataToString returns a string representation of a data item.
  213. */
  214. func dataToString(dataType string, gn *graphNode) string {
  215. var buf bytes.Buffer
  216. attrlist := make([]string, 0, len(gn.data))
  217. maxlen := 0
  218. for attr := range gn.data {
  219. attrlist = append(attrlist, attr)
  220. if alen := len(attr); alen > maxlen {
  221. maxlen = alen
  222. }
  223. }
  224. sort.StringSlice(attrlist).Sort()
  225. buf.WriteString(dataType + ":\n")
  226. buf.WriteString(fmt.Sprintf(" %"+
  227. strconv.Itoa(maxlen)+"v : %v\n", "key", gn.Key()))
  228. buf.WriteString(fmt.Sprintf(" %"+
  229. strconv.Itoa(maxlen)+"v : %v\n", "kind", gn.Kind()))
  230. for _, attr := range attrlist {
  231. if attr == NodeKey || attr == NodeKind {
  232. continue
  233. }
  234. buf.WriteString(fmt.Sprintf(" %"+
  235. strconv.Itoa(maxlen)+"v : %v\n", attr, gn.data[attr]))
  236. }
  237. return buf.String()
  238. }