/* * EliasDB * * Copyright 2016 Matthias Ladkau. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package graph import ( "bytes" "encoding/json" "fmt" "io" "devt.de/krotik/common/errorutil" "devt.de/krotik/eliasdb/graph/data" ) /* ExportPartition dumps the contents of a partition to an io.Writer in JSON format: { nodes : [ { : }, ... ] edges : [ { : }, ... ] } */ func ExportPartition(out io.Writer, part string, gm *Manager) error { // Use a map to unique found edge keys edgeKeys := make(map[string]string) writeData := func(data map[string]interface{}) { nk := 0 for k, v := range data { // JSON encode value - ignore values which cannot be JSON encoded jv, err := json.Marshal(v) // Encoding errors result in a null value if err != nil { jv = []byte("null") } // Write out the node attributes fmt.Fprintf(out, " \"%s\" : %s", k, jv) if nk < len(data)-1 { fmt.Fprint(out, ",") } fmt.Fprint(out, "\n") nk++ } } // Iterate over all available node kinds fmt.Fprint(out, `{ "nodes" : [ `) // Loop over all available kinds and build iterators if nodes // exist in the given partition var iters []*NodeKeyIterator var kinds []string for _, k := range gm.NodeKinds() { it, err := gm.NodeKeyIterator(part, k) if err != nil { return err } if it != nil { iters = append(iters, it) kinds = append(kinds, k) } } for ik, it := range iters { // Iterate over all node keys for i := 0; it.HasNext(); i++ { key := it.Next() if it.LastError != nil { return it.LastError } node, err := gm.FetchNode(part, key, kinds[ik]) if err != nil { return err } // Fetch all connected relationships and store their key and kind _, edges, err := gm.TraverseMulti(part, key, kinds[ik], ":::", false) if err != nil { return err } for _, edge := range edges { edgeKeys[edge.Kind()+edge.Key()] = edge.Kind() } // Write out JSON object fmt.Fprint(out, " {\n") writeData(node.Data()) if it.HasNext() || ik < len(iters)-1 { fmt.Fprint(out, " },\n") } else { fmt.Fprint(out, " }\n") } } } fmt.Fprint(out, ` ], "edges" : [ `) // Iterate over all available edge kinds ie := 0 for key, kind := range edgeKeys { key = key[len(kind):] edge, err := gm.FetchEdge(part, key, kind) if err != nil { return err } // Write out JSON object fmt.Fprint(out, " {\n") writeData(edge.Data()) if ie < len(edgeKeys)-1 { fmt.Fprint(out, " },\n") } else { fmt.Fprint(out, " }\n") } ie++ } fmt.Fprint(out, ` ] }`) return nil } /* SortDump sorts a string result which was produced by ExportPartition. Do not use this for very large results. Panics if the input data is not valid. */ func SortDump(in string) string { var nodes []data.Node var edges []data.Node dec := json.NewDecoder(bytes.NewBufferString(in)) gdata := make(map[string][]map[string]interface{}) errorutil.AssertOk(dec.Decode(&gdata)) nDataList := gdata["nodes"] for _, n := range nDataList { nodes = append(nodes, data.NewGraphNodeFromMap(n)) } data.NodeSort(nodes) for i, n := range nodes { nDataList[i] = n.Data() } eDataList := gdata["edges"] for _, n := range eDataList { edges = append(edges, data.NewGraphNodeFromMap(n)) } data.NodeSort(edges) for i, e := range edges { eDataList[i] = e.Data() } res, err := json.MarshalIndent(map[string]interface{}{ "nodes": nDataList, "edges": eDataList, }, "", " ") errorutil.AssertOk(err) return string(res) } /* ImportPartition imports the JSON contents of an io.Reader into a given partition. The following format is expected: { nodes : [ { : }, ... ] edges : [ { : }, ... ] } */ func ImportPartition(in io.Reader, part string, gm *Manager) error { dec := json.NewDecoder(in) gdata := make(map[string][]map[string]interface{}) if err := dec.Decode(&gdata); err != nil { return fmt.Errorf("Could not decode file content as object with list of nodes and edges: %s", err.Error()) } nDataList := gdata["nodes"] eDataList := gdata["edges"] // Create a transaction trans := NewGraphTrans(gm) // Store nodes in transaction for _, ndata := range nDataList { node := data.NewGraphNodeFromMap(ndata) if err := trans.StoreNode(part, node); err != nil { return err } } // Store edges in transaction for _, edata := range eDataList { edge := data.NewGraphEdgeFromNode(data.NewGraphNodeFromMap(edata)) if err := trans.StoreEdge(part, edge); err != nil { return err } } // Commit transaction return trans.Commit() }