import_export.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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 graph
  11. import (
  12. "bytes"
  13. "encoding/json"
  14. "fmt"
  15. "io"
  16. "devt.de/krotik/common/errorutil"
  17. "devt.de/krotik/eliasdb/graph/data"
  18. )
  19. /*
  20. ExportPartition dumps the contents of a partition to an io.Writer in JSON format:
  21. {
  22. nodes : [ { <attr> : <value> }, ... ]
  23. edges : [ { <attr> : <value> }, ... ]
  24. }
  25. */
  26. func ExportPartition(out io.Writer, part string, gm *Manager) error {
  27. // Use a map to unique found edge keys
  28. edgeKeys := make(map[string]string)
  29. writeData := func(data map[string]interface{}) {
  30. nk := 0
  31. for k, v := range data {
  32. // JSON encode value - ignore values which cannot be JSON encoded
  33. jv, err := json.Marshal(v)
  34. // Encoding errors result in a null value
  35. if err != nil {
  36. jv = []byte("null")
  37. }
  38. // Write out the node attributes
  39. fmt.Fprintf(out, " \"%s\" : %s", k, jv)
  40. if nk < len(data)-1 {
  41. fmt.Fprint(out, ",")
  42. }
  43. fmt.Fprint(out, "\n")
  44. nk++
  45. }
  46. }
  47. // Iterate over all available node kinds
  48. fmt.Fprint(out, `{
  49. "nodes" : [
  50. `)
  51. // Loop over all available kinds and build iterators if nodes
  52. // exist in the given partition
  53. var iters []*NodeKeyIterator
  54. var kinds []string
  55. for _, k := range gm.NodeKinds() {
  56. it, err := gm.NodeKeyIterator(part, k)
  57. if err != nil {
  58. return err
  59. }
  60. if it != nil {
  61. iters = append(iters, it)
  62. kinds = append(kinds, k)
  63. }
  64. }
  65. for ik, it := range iters {
  66. // Iterate over all node keys
  67. for i := 0; it.HasNext(); i++ {
  68. key := it.Next()
  69. if it.LastError != nil {
  70. return it.LastError
  71. }
  72. node, err := gm.FetchNode(part, key, kinds[ik])
  73. if err != nil {
  74. return err
  75. }
  76. // Fetch all connected relationships and store their key and kind
  77. _, edges, err := gm.TraverseMulti(part, key, kinds[ik], ":::", false)
  78. if err != nil {
  79. return err
  80. }
  81. for _, edge := range edges {
  82. edgeKeys[edge.Kind()+edge.Key()] = edge.Kind()
  83. }
  84. // Write out JSON object
  85. fmt.Fprint(out, " {\n")
  86. writeData(node.Data())
  87. if it.HasNext() || ik < len(iters)-1 {
  88. fmt.Fprint(out, " },\n")
  89. } else {
  90. fmt.Fprint(out, " }\n")
  91. }
  92. }
  93. }
  94. fmt.Fprint(out, ` ],
  95. "edges" : [
  96. `)
  97. // Iterate over all available edge kinds
  98. ie := 0
  99. for key, kind := range edgeKeys {
  100. key = key[len(kind):]
  101. edge, err := gm.FetchEdge(part, key, kind)
  102. if err != nil {
  103. return err
  104. }
  105. // Write out JSON object
  106. fmt.Fprint(out, " {\n")
  107. writeData(edge.Data())
  108. if ie < len(edgeKeys)-1 {
  109. fmt.Fprint(out, " },\n")
  110. } else {
  111. fmt.Fprint(out, " }\n")
  112. }
  113. ie++
  114. }
  115. fmt.Fprint(out, ` ]
  116. }`)
  117. return nil
  118. }
  119. /*
  120. SortDump sorts a string result which was produced by ExportPartition.
  121. Do not use this for very large results. Panics if the input data is not valid.
  122. */
  123. func SortDump(in string) string {
  124. var nodes []data.Node
  125. var edges []data.Node
  126. dec := json.NewDecoder(bytes.NewBufferString(in))
  127. gdata := make(map[string][]map[string]interface{})
  128. errorutil.AssertOk(dec.Decode(&gdata))
  129. nDataList := gdata["nodes"]
  130. for _, n := range nDataList {
  131. nodes = append(nodes, data.NewGraphNodeFromMap(n))
  132. }
  133. data.NodeSort(nodes)
  134. for i, n := range nodes {
  135. nDataList[i] = n.Data()
  136. }
  137. eDataList := gdata["edges"]
  138. for _, n := range eDataList {
  139. edges = append(edges, data.NewGraphNodeFromMap(n))
  140. }
  141. data.NodeSort(edges)
  142. for i, e := range edges {
  143. eDataList[i] = e.Data()
  144. }
  145. res, err := json.MarshalIndent(map[string]interface{}{
  146. "nodes": nDataList,
  147. "edges": eDataList,
  148. }, "", " ")
  149. errorutil.AssertOk(err)
  150. return string(res)
  151. }
  152. /*
  153. ImportPartition imports the JSON contents of an io.Reader into a given partition.
  154. The following format is expected:
  155. {
  156. nodes : [ { <attr> : <value> }, ... ]
  157. edges : [ { <attr> : <value> }, ... ]
  158. }
  159. */
  160. func ImportPartition(in io.Reader, part string, gm *Manager) error {
  161. dec := json.NewDecoder(in)
  162. gdata := make(map[string][]map[string]interface{})
  163. if err := dec.Decode(&gdata); err != nil {
  164. return fmt.Errorf("Could not decode file content as object with list of nodes and edges: %s", err.Error())
  165. }
  166. nDataList := gdata["nodes"]
  167. eDataList := gdata["edges"]
  168. // Create a transaction
  169. trans := NewGraphTrans(gm)
  170. // Store nodes in transaction
  171. for _, ndata := range nDataList {
  172. node := data.NewGraphNodeFromMap(ndata)
  173. if err := trans.StoreNode(part, node); err != nil {
  174. return err
  175. }
  176. }
  177. // Store edges in transaction
  178. for _, edata := range eDataList {
  179. edge := data.NewGraphEdgeFromNode(data.NewGraphNodeFromMap(edata))
  180. if err := trans.StoreEdge(part, edge); err != nil {
  181. return err
  182. }
  183. }
  184. // Commit transaction
  185. return trans.Commit()
  186. }