Skip to content
Snippets Groups Projects
request.go 5.72 KiB
package request

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	"encoding/json"
	"io/ioutil"

	driver "github.com/arangodb/go-driver"
	"github.com/arangodb/go-driver/http"
)

// Service is a struct used to store this use case in
type Service struct {
}

// NewService creates a new instantion of this use case
func NewService() *Service {
	return &Service{}
}

// Document with Empty struct to retrieve all data from the DB Document
type Document map[string]interface{}

// GeneralFormat with Empty struct to retrieve all data from the DB Document
type GeneralFormat map[string][]Document

// ListContainer is a struct that keeps track of the nodes and edges that need to be returned
type ListContainer struct {
	nodeList []Document
	edgeList []Document
}

//attr interface{}

//map[1 , 2 , 3 map [ .. ]]

// SendAQLQuery send AQL string query to database and returns a JSON object in a general format
func (s *Service) SendAQLQuery(AQLQuery string) (*map[string][]Document, error) {
	var queryResult = make(map[string][]Document)
	conn, err := http.NewConnection(http.ConnectionConfig{
		Endpoints: []string{"https://aae8f5c054da.arangodb.cloud:8529"},
		TLSConfig: &tls.Config{InsecureSkipVerify: true},
	})
	if err != nil {
		log.Println("could not connect to database") // Handle error
		return nil, err
	}
	c, err := driver.NewClient(driver.ClientConfig{
		Connection:     conn,
		Authentication: driver.BasicAuthentication("root", "kXR0PFepIVm3EqgQ2MVc"),
	})
	if err != nil {
		log.Println("Could not log in to the ArangoDB") // Handle error
		return nil, err
	}

	ctx := context.Background()
	db, err := c.Database(ctx, "_system")
	if err != nil {
		// handle error
		log.Println(err)
		return nil, err
	}

	// CHANGED TO OTHER FORMAT
	//fmt.Println(AQLQuery)
	// query := `
	// 			LET n0 = (
	// 				FOR x IN airports
	// 				FILTER  x.country == 'USA'
	// 				RETURN x
	// 			)
	// 			FOR n in n0
	// 			RETURN n
	// 		`
	cursor, err := db.Query(ctx, AQLQuery, nil)
	if err != nil {
		log.Println("Invalid query") // handle error
		return nil, err
	}
	defer cursor.Close()

	lcontainer := ListContainer{}
	for {
		var doc map[string][]interface{}
		_, err := cursor.ReadDocument(ctx, &doc)
		if driver.IsNoMoreDocuments(err) {
			break
		} else if err != nil {
			// handle other errors
			return nil, err
		}
		//fmt.Printf("%s\n", doc)

		//GEDACHTEGANG TIJD:
		//Normaal een lijst van n0, n1. Nu kan er ook e0 bij zitten, die heeft een andere structuur
		//Dus nu een returnstruct maken met een nodelist en edgelist
		//Vervolgens door de keys van de doc (n0 e0 etc) loopen en een verschillende parser aanroepen die de
		//returnstruct vult. Daarna de returnstruct omzetten tot een nodelist (en maybe edgelist)

		//ret = parseDocToReturn() {
		//	for key in doc:
		//		if key starts with n: Nodeparsen
		//		if key starts with e: Edgeparsen
		// 	return listContainer {nodelist edgelist}
		//}
		//
		//result = parseContainerToString(ret)

		parseResult(doc, &lcontainer)
	}

	queryResult["nodes"] = lcontainer.nodeList
	queryResult["edges"] = lcontainer.edgeList

	//writeJSON(queryResult)
	//file, err := json.MarshalIndent(queryResult, "", " ")
	return &queryResult, nil
}

// parseResult takes the result of the query and translates this to two lists: a nodelist and an edgelist, stored in a listcontainer
func parseResult(doc map[string][]interface{}, lcontainer *ListContainer) {

	for k, v := range doc {
		switch letter := []byte(k)[0]; letter {
		case 'e':
			//fmt.Println(v)
			//Parsing of edges
			for _, j := range v {
				//fmt.Println(j)

				//fmt.Printf("\n%T\n", j)
				d := j.(map[string]interface{})
				//fmt.Printf("\n%T\n", d["vertices"])
				vert := d["vertices"].([]interface{})
				edg := d["edges"].([]interface{})

				lcontainer.nodeList = append(lcontainer.nodeList, parseNode(vert[0]))
				lcontainer.nodeList = append(lcontainer.nodeList, parseNode(vert[1]))
				lcontainer.edgeList = append(lcontainer.edgeList, parseEdge(edg[0]))
			}
		case 'n':
			//Parsing of nodes
			for _, j := range v {

				//fmt.Printf("\n%T\n", j)
				doc := j.(map[string]interface{})
				lcontainer.nodeList = append(lcontainer.nodeList, parseNode(doc))
			}
		default:
			//Error
			fmt.Println("Empty document")
		}
	}
}

// parseEdge parses the data of an edge to an output-friendly format
func parseEdge(d interface{}) Document {
	doc := d.(map[string]interface{})

	data := make(Document)
	data["_id"] = doc["_id"]
	delete(doc, "_id")
	data["_key"] = doc["_key"]
	delete(doc, "_key")
	data["_rev"] = doc["_rev"]
	delete(doc, "_rev")
	data["_from"] = doc["_from"]
	delete(doc, "_from")
	data["_to"] = doc["_to"]
	delete(doc, "_to")
	data["attributes"] = doc

	//delete(doc, "_key")
	//data.rev = fmt.Sprintf("%v", doc["_rev"])

	// delete(doc, "_rev")
	// data.attr = doc
	return data
}

// func formatToJSON(doc GeneralFormat) []Document {
// 	//b, err := json.Marshal(doc)
// 	//if err != nil {
// 	//handle error
// 	//}
// 	fmt.Println(doc)

// 	var nodeList []Document
// 	for _, v := range doc {
// 		for _, j := range v {
// 			nodeList = append(nodeList, parseNode(j))
// 		}
// 	}
// 	// fmt.Println(nodeList)
// 	return nodeList
// }

// writeJSON writes a json file for testing purposes
func writeJSON(queryResult map[string][]Document) {
	file, _ := json.MarshalIndent(queryResult, "", " ")

	_ = ioutil.WriteFile("result.json", file, 0644)
}

// parseNode parses the data of a node to an output-friendly format
func parseNode(d interface{}) Document {
	doc := d.(map[string]interface{})

	data := make(Document)
	data["_id"] = doc["_id"]
	delete(doc, "_id")
	data["_key"] = doc["_key"]
	delete(doc, "_key")
	data["_rev"] = doc["_rev"]
	delete(doc, "_rev")
	data["attributes"] = doc

	//delete(doc, "_key")
	//data.rev = fmt.Sprintf("%v", doc["_rev"])

	// delete(doc, "_rev")
	// data.attr = doc
	return data
}