From 1261fecfb63a7069fdb28a519141d4a026fa9d12 Mon Sep 17 00:00:00 2001
From: "Geurtjens,D. (Douwe Geurtjens)" <d.geurtjens@students.uu.nl>
Date: Tue, 1 Feb 2022 08:09:25 +0000
Subject: [PATCH] Develop

---
 entity/IncomingTranslation.go                 |  17 ++
 entity/ReturnFormat.go                        |  29 +++
 main/main.go                                  |  75 ++++---
 main/sparql-mock-result.json                  | 103 +++++++++
 {arangodb => usecases/arangodb}/cover.html    |   0
 .../arangodb}/data/.gitignore                 |   0
 .../arangodb}/entity/document.go              |   3 +-
 .../arangodb}/executeQuery.go                 |  41 ++--
 .../arangodb}/executeQuery_test.go            |  53 +++++
 .../arangodb}/queryExecutor.go                |   0
 {arangodb => usecases/arangodb}/test.sh       |   0
 {neo4j => usecases/neo4j}/entity/types.go     |   0
 {neo4j => usecases/neo4j}/executeQuery.go     |   2 +-
 .../neo4j}/executeQuery_test.go               |   0
 {neo4j => usecases/neo4j}/queryExecutor.go    |   0
 usecases/sparql/entity/result.go              |  53 +++++
 usecases/sparql/executeQuery.go               | 197 ++++++++++++++++++
 usecases/sparql/executeQuery_test.go          |  46 ++++
 usecases/sparql/interface.go                  |  21 ++
 19 files changed, 588 insertions(+), 52 deletions(-)
 create mode 100644 entity/IncomingTranslation.go
 create mode 100644 entity/ReturnFormat.go
 create mode 100644 main/sparql-mock-result.json
 rename {arangodb => usecases/arangodb}/cover.html (100%)
 rename {arangodb => usecases/arangodb}/data/.gitignore (100%)
 rename {arangodb => usecases/arangodb}/entity/document.go (93%)
 rename {arangodb => usecases/arangodb}/executeQuery.go (74%)
 rename {arangodb => usecases/arangodb}/executeQuery_test.go (83%)
 rename {arangodb => usecases/arangodb}/queryExecutor.go (100%)
 rename {arangodb => usecases/arangodb}/test.sh (100%)
 mode change 100755 => 100644
 rename {neo4j => usecases/neo4j}/entity/types.go (100%)
 rename {neo4j => usecases/neo4j}/executeQuery.go (98%)
 rename {neo4j => usecases/neo4j}/executeQuery_test.go (100%)
 rename {neo4j => usecases/neo4j}/queryExecutor.go (100%)
 create mode 100644 usecases/sparql/entity/result.go
 create mode 100644 usecases/sparql/executeQuery.go
 create mode 100644 usecases/sparql/executeQuery_test.go
 create mode 100644 usecases/sparql/interface.go

diff --git a/entity/IncomingTranslation.go b/entity/IncomingTranslation.go
new file mode 100644
index 0000000..a45a1b7
--- /dev/null
+++ b/entity/IncomingTranslation.go
@@ -0,0 +1,17 @@
+/*
+This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+package entity
+
+/*
+IncomingTranslation contains the translated query and some possible metadata,
+ the metadata should have a clearly defined type for each usecase.
+ Each usecase should be responsible for unmarshalling to their own type
+ This really is a usecase for generics, but Go doesn't have them yet.
+*/
+type IncomingTranslation struct {
+	Translation string
+	MetaData    interface{}
+}
diff --git a/entity/ReturnFormat.go b/entity/ReturnFormat.go
new file mode 100644
index 0000000..0960c08
--- /dev/null
+++ b/entity/ReturnFormat.go
@@ -0,0 +1,29 @@
+/*
+This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+package entity
+
+/*
+Node is a JSON format for nodes
+*/
+type Node struct {
+	ID         string                 `json:"id"`
+	Attributes map[string]interface{} `json:"attributes"`
+}
+
+/*
+Edge is a JSON format for edges
+*/
+type Edge struct {
+	ID         string                 `json:"id"`
+	From       string                 `json:"from"`
+	To         string                 `json:"to"`
+	Attributes map[string]interface{} `json:"attributes"`
+}
+
+/*
+ResultFormat is a format for the result, the interface{} can be either an edge or a node
+*/
+type ResultFormat map[string][]interface{}
diff --git a/main/main.go b/main/main.go
index e183f8a..e68c6f0 100644
--- a/main/main.go
+++ b/main/main.go
@@ -6,48 +6,55 @@ This program has been developed by students from the bachelor Computer Science a
 package main
 
 import (
+	"fmt"
 	"log"
 
-	"git.science.uu.nl/graphpolaris/query-execution/arangodb"
+	"git.science.uu.nl/graphpolaris/query-execution/usecases/arangodb"
+	"git.science.uu.nl/graphpolaris/query-execution/usecases/sparql"
 )
 
 /* main is used to test the different queryExecutors */
-func main() {
+func main2() {
 	queryservice := arangodb.NewService()
 
-	js := `WITH bank
-	LET n0 = (
-		FOR x IN person
-		RETURN x
+	js := `LET result = (
+		FOR e_29 IN parliament
+		LET e30 = (
+			FOR e_30 IN parties
+			FOR r28 IN member_of
+			FILTER r28._from == e_29._id AND r28._to == e_30._id
+			FILTER length(e_30) != 0 AND length(r28) != 0
+			RETURN {"nodes":union_distinct([e_30], []), "rel": union_distinct([r28], []), "mod": union_distinct([], [])}
+		)
+		FILTER length(e30) != 0 AND length(e_29) != 0
+		RETURN {"nodes":union_distinct(flatten(e30[**].nodes), [e_29]), "rel": union_distinct(flatten(e30[**].rel), []), "mod": union_distinct(flatten(e30[**].mod), [])}
 	)
-	LET r0 = (
-		FOR x IN n0
-		FOR v, e, p IN 1..1 OUTBOUND x transfers
-		OPTIONS { uniqueEdges: "path" }
-		LIMIT 5000
-	RETURN DISTINCT p )
-
-	LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))
-	LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))
-	RETURN {"vertices":nodes, "edges":edges }`
-
-	// tweedeKamer := `WITH parties
-	// LET n0 = (
-	// 	FOR x IN parliament
-	// 	RETURN x
-	// )
-	// LET r0 = (
-	// 	FOR x IN n0
-	// 	FOR v, e, p IN 1..1 OUTBOUND x member_of
-	// 	OPTIONS { uniqueEdges: "path" }
-	// 	LIMIT 5000
-	// RETURN DISTINCT p )
-
-	// LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))
-	// LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))
-	// RETURN {"vertices":nodes, "edges":edges }`
+	LET nodes = union_distinct(flatten(result[**].nodes),[])
+	LET edges = union_distinct(flatten(result[**].rel),[])
+	RETURN {"vertices":nodes,"edges":edges}`
 
-	result, err := queryservice.ExecuteQuery(js, "root", "DikkeDraak", "http://167.71.8.197", 8529, "UsecaseFraud")
+	result, err := queryservice.ExecuteQuery(js, "root", "DikkeDraak", "https://datastrophe.science.uu.nl/", 8529, "TweedeKamer")
 	log.Println(err)
-	log.Println(result)
+	log.Println(string(*result))
+}
+
+func main() {
+	queryservice := sparql.NewService()
+	trips := `{
+		"triples": {
+			"r0": {
+				"from": "e0",
+				"to": "e1"
+			},
+			"r1": {
+				"from": "e2",
+				"to": "e1"
+			}
+		}
+	}`
+	res, err := queryservice.ExecuteQuery("lol", trips, "sparql-mock-result.json")
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(string(*res))
 }
diff --git a/main/sparql-mock-result.json b/main/sparql-mock-result.json
new file mode 100644
index 0000000..f95007e
--- /dev/null
+++ b/main/sparql-mock-result.json
@@ -0,0 +1,103 @@
+{
+    "head": {
+        "vars": [
+            "e0",
+            "e1",
+            "r0",
+            "e2",
+            "r1"
+        ]
+    },
+    "results": {
+        "bindings": [
+            {
+                "e0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Sierra_Swan"
+                },
+                "e1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/The_Black_Eyed_Peas"
+                },
+                "r0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                },
+                "e2": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Apl.de.ap"
+                },
+                "r1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                }
+            },
+            {
+                "e0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Sierra_Swan"
+                },
+                "e1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/The_Black_Eyed_Peas"
+                },
+                "r0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                },
+                "e2": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Fergie_(singer)"
+                },
+                "r1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                }
+            },
+            {
+                "e0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Sierra_Swan"
+                },
+                "e1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/The_Black_Eyed_Peas"
+                },
+                "r0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                },
+                "e2": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Sierra_Swan"
+                },
+                "r1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                }
+            },
+            {
+                "e0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Sierra_Swan"
+                },
+                "e1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/The_Black_Eyed_Peas"
+                },
+                "r0": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                },
+                "e2": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/Taboo_(rapper)"
+                },
+                "r1": {
+                    "type": "uri",
+                    "value": "http://api.stardog.com/memberOf"
+                }
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/arangodb/cover.html b/usecases/arangodb/cover.html
similarity index 100%
rename from arangodb/cover.html
rename to usecases/arangodb/cover.html
diff --git a/arangodb/data/.gitignore b/usecases/arangodb/data/.gitignore
similarity index 100%
rename from arangodb/data/.gitignore
rename to usecases/arangodb/data/.gitignore
diff --git a/arangodb/entity/document.go b/usecases/arangodb/entity/document.go
similarity index 93%
rename from arangodb/entity/document.go
rename to usecases/arangodb/entity/document.go
index b6106cb..cdb30af 100644
--- a/arangodb/entity/document.go
+++ b/usecases/arangodb/entity/document.go
@@ -3,7 +3,7 @@ This program has been developed by students from the bachelor Computer Science a
 © Copyright Utrecht University (Department of Information and Computing Sciences)
 */
 
-package entity
+package entityarangodb
 
 /*
 Document with Empty struct to retrieve all data from the DB Document
@@ -21,4 +21,5 @@ ListContainer is a struct that keeps track of the nodes and edges that need to b
 type ListContainer struct {
 	NodeList []Document
 	EdgeList []Document
+	RowList  []Document
 }
diff --git a/arangodb/executeQuery.go b/usecases/arangodb/executeQuery.go
similarity index 74%
rename from arangodb/executeQuery.go
rename to usecases/arangodb/executeQuery.go
index 72bb845..d2a9959 100644
--- a/arangodb/executeQuery.go
+++ b/usecases/arangodb/executeQuery.go
@@ -11,7 +11,7 @@ import (
 	"encoding/json"
 	"fmt"
 
-	"git.science.uu.nl/graphpolaris/query-execution/arangodb/entity"
+	entityarangodb "git.science.uu.nl/graphpolaris/query-execution/usecases/arangodb/entity"
 	driver "github.com/arangodb/go-driver"
 	"github.com/arangodb/go-driver/http"
 )
@@ -29,7 +29,7 @@ ExecuteQuery executes the given query on an ArangoDB instance
 func (s *Service) ExecuteQuery(query string, databaseUsername string, databasePassword string, databaseHost string, databasePort int, internalDatabase string) (*[]byte, error) {
 
 	// Create data structure to store query result in
-	var queryResult = make(map[string][]entity.Document)
+	var queryResult = make(map[string][]entityarangodb.Document)
 
 	// Open connection to ArangoDB
 	conn, err := http.NewConnection(http.ConnectionConfig{
@@ -62,9 +62,10 @@ func (s *Service) ExecuteQuery(query string, databaseUsername string, databasePa
 	defer cursor.Close()
 
 	// Loop through the resulting documents
-	listContainer := entity.ListContainer{}
-	listContainer.EdgeList = make([]entity.Document, 0)
-	listContainer.NodeList = make([]entity.Document, 0)
+	listContainer := entityarangodb.ListContainer{}
+	listContainer.EdgeList = make([]entityarangodb.Document, 0)
+	listContainer.NodeList = make([]entityarangodb.Document, 0)
+	listContainer.RowList = make([]entityarangodb.Document, 0)
 	num := -1.0
 	isNum := false
 
@@ -99,7 +100,7 @@ func (s *Service) ExecuteQuery(query string, databaseUsername string, databasePa
 		// Return nodes and edges
 		queryResult["nodes"] = listContainer.NodeList
 		queryResult["edges"] = listContainer.EdgeList
-
+		queryResult["rows"] = listContainer.RowList
 		jsonQ, err := json.Marshal(queryResult)
 		return &jsonQ, err
 	}
@@ -111,11 +112,19 @@ func (s *Service) ExecuteQuery(query string, databaseUsername string, databasePa
 /*
 parseResult takes the result of the query and translates this to two lists: a nodelist and an edgelist, stored in a listcontainer
 	doc: map[string]interface{}, the document with the nodes and vertices that came back from the database
-	listContainer: *entity.ListContainer, a struct containing the nodelist and edgelist that will be returned to the frontend
+	listContainer: *entityarangodb.ListContainer, a struct containing the nodelist and edgelist that will be returned to the frontend
 */
-func parseResult(doc map[string]interface{}, listContainer *entity.ListContainer) {
-	vertices := doc["vertices"].([]interface{})
-	edges := doc["edges"].([]interface{})
+func parseResult(doc map[string]interface{}, listContainer *entityarangodb.ListContainer) {
+	var vertices []interface{}
+	var edges []interface{}
+	_, vertex := doc["vertices"]
+	_, edge := doc["edges"]
+	if vertex && edge { // Node link
+		vertices = doc["vertices"].([]interface{})
+		edges = doc["edges"].([]interface{})
+	} else { // Table
+		(*listContainer).RowList = append(((*listContainer).RowList), doc)
+	}
 
 	for _, vertex := range vertices {
 		if vertex != nil {
@@ -137,10 +146,10 @@ func parseResult(doc map[string]interface{}, listContainer *entity.ListContainer
 /*
 parseEdge parses the data of an edge to an output-friendly format
 	doc: map[string]interface{}, a single edge returned from the database
-	Returns: entity.Document, a document with almost the same structure as before, but the attributes are grouped
+	Returns: entityarangodb.Document, a document with almost the same structure as before, but the attributes are grouped
 */
-func parseEdge(doc map[string]interface{}) entity.Document {
-	data := make(entity.Document)
+func parseEdge(doc map[string]interface{}) entityarangodb.Document {
+	data := make(entityarangodb.Document)
 	data["id"] = doc["_id"]
 	delete(doc, "_id")
 	delete(doc, "_key")
@@ -157,10 +166,10 @@ func parseEdge(doc map[string]interface{}) entity.Document {
 /*
 parseEdge parses the data of a node to an output-friendly format
 	doc: map[string]interface{}, a single entry of an node
-	Returns: entity.Document, a document with almost the same structure as before, but the attributes are grouped
+	Returns: entityarangodb.Document, a document with almost the same structure as before, but the attributes are grouped
 */
-func parseNode(doc map[string]interface{}) entity.Document {
-	data := make(entity.Document)
+func parseNode(doc map[string]interface{}) entityarangodb.Document {
+	data := make(entityarangodb.Document)
 	data["id"] = doc["_id"]
 	delete(doc, "_id")
 	delete(doc, "_key")
diff --git a/arangodb/executeQuery_test.go b/usecases/arangodb/executeQuery_test.go
similarity index 83%
rename from arangodb/executeQuery_test.go
rename to usecases/arangodb/executeQuery_test.go
index a48c5d0..a66e994 100644
--- a/arangodb/executeQuery_test.go
+++ b/usecases/arangodb/executeQuery_test.go
@@ -123,6 +123,59 @@ func TestValidQueries(t *testing.T) {
 			RETURN {"vertices":nodes, "edges":edges }`,
 			out: `{"edges":[{"_from":"airports/JFK","_id":"flights/1839","_key":"1839","_to":"airports/SFO","attributes":{"ArrTime":327,"ArrTimeUTC":"2008-01-01T11:27:00.000Z","Day":1,"DayOfWeek":2,"DepTime":11,"DepTimeUTC":"2008-01-01T05:11:00.000Z","Distance":2586,"FlightNum":9,"Month":1,"TailNum":"N555UA","UniqueCarrier":"UA","Year":2008}}],"nodes":[{"_id":"airports/SFO","_key":"SFO","attributes":{"city":"San Francisco","country":"USA","lat":37.61900194,"long":-122.3748433,"name":"San Francisco International","state":"CA","vip":true}},{"_id":"airports/JFK","_key":"JFK","attributes":{"city":"New York","country":"USA","lat":40.63975111,"long":-73.77892556,"name":"John F Kennedy Intl","state":"NY","vip":true}}]}`,
 		},
+		{
+			name: `group by query with table return`,
+			in: `LET tree_0 = (
+				FOR e_9 IN parliament
+				LET e10 = (
+					FOR e_10 IN commissions
+					FOR r8 IN part_of
+					FILTER r8._from == e_9._id AND r8._to == e_10._id
+					FILTER length(e_10) != 0 AND length(r8) != 0
+					RETURN {"e10": union_distinct([e_10], []), "r8": union_distinct([r8], [])}
+				)
+				FILTER length(e10) != 0 AND length(e_9) != 0
+				RETURN {"e9": union_distinct([e_9], []), "e10": union_distinct(flatten(e10[**].e10), []), "r8": union_distinct(flatten(e10[**].r8), [])}
+			)
+			LET gt13 = (
+				FOR x IN part_of
+				LET variable_0 = (
+					LET tmp = union_distinct(flatten(tree_0[**].e10), [])
+						FOR y IN tmp
+						FILTER y._id == x._to
+						RETURN y._id
+				) 
+				LET variable_1 = variable_0[0] 
+				LET variable_2 = (
+					LET tmp = union_distinct(flatten(tree_0[**].e9), [])
+						FOR y IN tmp
+						FILTER y._id == x._from
+						RETURN y.age
+				) 
+				LET variable_3 = variable_2[0] 
+				FILTER variable_1 != NULL AND variable_3 != NULL
+				RETURN {
+					"group": variable_1, 
+					"by": variable_3
+				}
+			)
+			LET g13 = (
+				FOR x IN gt13
+				COLLECT c = x.group INTO groups = x.by
+				LET variable_0 = AVG(groups) 
+				FILTER variable_0 > 45  
+				RETURN {
+				_id: c,
+				modifier: variable_0
+				}
+			)
+			FOR x in g13
+			RETURN {group: x.modifier, by: x._id}`,
+			out: `{"edges":[],"nodes":[],"rows":[{"by":"commissions/15","group":48.53846153846154},{"by":"commissions/20","group":55.333333333333336},
+			{"by":"commissions/25","group":45.2},{"by":"commissions/26","group":46.25},{"by":"commissions/27","group":45.25},{"by":"commissions/28","group":55.4},
+			{"by":"commissions/29","group":45.36363636363637},{"by":"commissions/3","group":46.4},{"by":"commissions/33","group":45.25},{"by":"commissions/35","group":47},
+			{"by":"commissions/36","group":53.75},{"by":"commissions/4","group":46.15384615384615}]}`,
+		},
 	}
 
 	for _, tt := range queryTests {
diff --git a/arangodb/queryExecutor.go b/usecases/arangodb/queryExecutor.go
similarity index 100%
rename from arangodb/queryExecutor.go
rename to usecases/arangodb/queryExecutor.go
diff --git a/arangodb/test.sh b/usecases/arangodb/test.sh
old mode 100755
new mode 100644
similarity index 100%
rename from arangodb/test.sh
rename to usecases/arangodb/test.sh
diff --git a/neo4j/entity/types.go b/usecases/neo4j/entity/types.go
similarity index 100%
rename from neo4j/entity/types.go
rename to usecases/neo4j/entity/types.go
diff --git a/neo4j/executeQuery.go b/usecases/neo4j/executeQuery.go
similarity index 98%
rename from neo4j/executeQuery.go
rename to usecases/neo4j/executeQuery.go
index 4bca84c..f52e844 100644
--- a/neo4j/executeQuery.go
+++ b/usecases/neo4j/executeQuery.go
@@ -11,7 +11,7 @@ import (
 	"fmt"
 	"strings"
 
-	entityneo4j "git.science.uu.nl/graphpolaris/query-execution/neo4j/entity"
+	entityneo4j "git.science.uu.nl/graphpolaris/query-execution/usecases/neo4j/entity"
 	"github.com/neo4j/neo4j-go-driver/v4/neo4j"
 	"github.com/neo4j/neo4j-go-driver/v4/neo4j/db"
 	"github.com/neo4j/neo4j-go-driver/v4/neo4j/dbtype"
diff --git a/neo4j/executeQuery_test.go b/usecases/neo4j/executeQuery_test.go
similarity index 100%
rename from neo4j/executeQuery_test.go
rename to usecases/neo4j/executeQuery_test.go
diff --git a/neo4j/queryExecutor.go b/usecases/neo4j/queryExecutor.go
similarity index 100%
rename from neo4j/queryExecutor.go
rename to usecases/neo4j/queryExecutor.go
diff --git a/usecases/sparql/entity/result.go b/usecases/sparql/entity/result.go
new file mode 100644
index 0000000..9e0a370
--- /dev/null
+++ b/usecases/sparql/entity/result.go
@@ -0,0 +1,53 @@
+/*
+This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+package entitysparql
+
+/*
+Defines the result type of the JSON received as a response when sending a query to a SPARQL dataset
+standard by https://www.w3.org/TR/2013/REC-sparql11-results-json-20130321/
+*/
+type Result struct {
+	Head    Select
+	Results map[string][]map[string]Binding
+}
+
+type Select struct {
+	Vars []string
+}
+
+type Bindings struct {
+	Values interface{}
+}
+
+type Binding struct {
+	Type  string
+	Value string
+}
+
+/*
+Maps a relation (e.g. r1) to the two entities it's connceted to (e.g. e0,e1)
+This is our neccesary metadata for executing a query and converting the output into our standard format
+*/
+type MetaData struct {
+	Triples map[string]Tuple
+}
+
+/*
+Go has no tuples, so we make one ourselves specifically for SPARQL subject-predicate-object relation types
+*/
+type Tuple struct {
+	From string
+	To   string
+}
+
+/*
+This is only used for duplicate checking in a roundabout manner, since entity.Edge cannot be used as a key for a map, since it contains interface{}
+*/
+type DuplicateCheckingTriple struct {
+	Rel  string
+	From string
+	To   string
+}
diff --git a/usecases/sparql/executeQuery.go b/usecases/sparql/executeQuery.go
new file mode 100644
index 0000000..8414a54
--- /dev/null
+++ b/usecases/sparql/executeQuery.go
@@ -0,0 +1,197 @@
+/*
+This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+package sparql
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"strings"
+
+	"git.science.uu.nl/graphpolaris/query-execution/entity"
+	entitysparql "git.science.uu.nl/graphpolaris/query-execution/usecases/sparql/entity"
+)
+
+/*
+ExecuteQuery executes the given query on an ArangoDB instance
+Currently DOES NOT actually implement the Executor interface, since it requires a different set of parameters
+Since this code is essentially a proof-of-concept/template-to-be-extended,
+ there is no reason to mess with the current, and working, interface definition and its implementations
+	query: string, the query to be executed
+	endpoint: string, the public SPARQL endpoint to query
+	incomingTriplesJSON: string, the triples used in the query corresponding to their select identifiers
+	Return: (*[]byte, error), returns the result of the query and a potential error
+*/
+func (s *Service) ExecuteQuery(query string, incomingTriplesJSON string, endpoint string) (*[]byte, error) {
+
+	// TODO: Actually query a dataset rather than doing it from this mocked JSON file of data
+	// You can probably curl the endpoint, sample code is provided in the "curl" function
+
+	// Create data structure to store query result in
+	// This means after converting the database response to our standard format
+	nodeList := make([]entity.Node, 0)
+	edgeList := make([]entity.Edge, 0)
+	queryResult := make(map[string]interface{})
+	// Unmarshal metaData into struct, query service can also pass an object instead of string I think?
+	// TODO: Make query service send the new IncomingTranslation object rahter than strings
+	metaData := entitysparql.MetaData{}
+	err := json.Unmarshal([]byte(incomingTriplesJSON), &metaData)
+	if err != nil {
+		fmt.Println(err)
+	}
+	fmt.Println(metaData)
+
+	// Just a dummy function that reads and unmarshals our mock data
+	// It goes into a entitysparql.Result format
+	result := openAndRead(endpoint)
+	// The ?e and ?r items get put into their respective lists
+	selectedEntities, selectedRelations, err := getSelects(&result)
+	if err != nil {
+		fmt.Println(err)
+	}
+	// Maps to avoid duplicates
+	nodeDoneMap := make(map[string]bool)
+	relDoneMap := make(map[entitysparql.DuplicateCheckingTriple]bool)
+	for i, binding := range result.Results["bindings"] {
+		// First just make the nodes
+		for _, e := range selectedEntities {
+			// Avoid putting in duplicate nodes
+			if _, ok := nodeDoneMap[binding[e].Value]; !ok {
+				node := entity.Node{}
+				attributes := make(map[string]interface{})
+				attributes[binding[e].Type] = binding[e].Value
+				node.ID = binding[e].Value
+				node.Attributes = attributes
+				nodeList = append(nodeList, node)
+				nodeDoneMap[binding[e].Value] = true
+			}
+		}
+		// Then do relations
+		for j, r := range selectedRelations {
+			// Funny way of turning two digits into a unique number
+			id := fmt.Sprintf("%v%v", i, j)
+			if err != nil {
+				return nil, err
+			}
+			edge := entity.Edge{}
+			attributes := make(map[string]interface{})
+			// This is basically giving the edge the "uri" attribute
+			attributes[binding[r].Type] = binding[r].Value
+			edge.ID = id
+			edge.Attributes = attributes
+			// metaData.Triples is our map that allows us to find the entities a relation is connected to
+			// That way we can get access to the ID (key[].Value) that we have to connect to
+			for _, potentialE := range selectedEntities {
+				if potentialE == metaData.Triples[r].From {
+					edge.From = binding[potentialE].Value
+
+				} else if potentialE == metaData.Triples[r].To {
+					edge.To = binding[potentialE].Value
+				}
+			}
+			// We can't use a map[edge]bool so we create this quick triple to check if a specific relation is already present
+			// If it is, we can skip it
+			// Not very clean code, but since go has no generics this will have to do
+			// If we had generics then attributes wouldn't be interface{} and it would be possible to use it as a key for a map
+			tempTriple := entitysparql.DuplicateCheckingTriple{
+				From: edge.From,
+				To:   edge.To,
+				Rel:  binding[r].Value,
+			}
+			// Only add the result if we don't already have it
+			if _, ok := relDoneMap[tempTriple]; !ok {
+				edgeList = append(edgeList, edge)
+				relDoneMap[tempTriple] = true
+			}
+
+		}
+	}
+	// Doesn't add rows for GroupBy at the moment
+	queryResult["nodes"] = nodeList
+	queryResult["edges"] = edgeList
+	jsonQ, err := json.Marshal(queryResult)
+	return &jsonQ, err
+
+}
+
+/*
+curl sends a http POST request to a SPARQL endpoint and decodes the JSON response into a struct
+	query: string, the query to be executed
+	endpoint: string, the public SPARQL endpoint to query
+	target: interface{}, the struct that should be filled with JSON data, this parameter is mutated during the function
+	Return: error, returns either a http or JSON related error
+*/
+func curl(query string, endpoint string, target interface{}) error {
+	// you can run this curl command to query an endpoint
+	// curl -X POST http://35.233.212.30/blazegraph/namespace/kb/sparql --data "query=SELECT * { ?s ?p ?o } LIMIT 10" --data "format=json"
+	// or in more general format
+	// curl -X POST endpoint --data query --data "format=json"
+
+	params := url.Values{}
+	params.Add("query", query)
+	params.Add("format", `json`)
+	body := strings.NewReader(params.Encode())
+
+	req, err := http.NewRequest("POST", endpoint, body)
+	if err != nil {
+		// handle err
+	}
+	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		// handle err
+	}
+	defer resp.Body.Close()
+	return json.NewDecoder(resp.Body).Decode(target)
+
+}
+
+/*
+getSelects splits the items preceded by the SELECT statement into two lists, one for entities and one for relations
+	queryResult: *entitysparql.Result, contains all selected variables
+	Return: ([]string, []string, error), the first list contains all entity variable names, the second list contains all relation variable names, the error in case a variable name is not recognized
+*/
+func getSelects(queryResult *entitysparql.Result) ([]string, []string, error) {
+	selectedEntities := make([]string, 0)
+	selectedRelations := make([]string, 0)
+	for _, selectedItem := range queryResult.Head.Vars {
+		if rune(selectedItem[0]) == 'e' {
+			selectedEntities = append(selectedEntities, selectedItem)
+		} else if rune(selectedItem[0]) == 'r' {
+			selectedRelations = append(selectedRelations, selectedItem)
+		} else {
+			return selectedEntities, selectedRelations, errors.New("unrecognized var name")
+		}
+	}
+	return selectedEntities, selectedRelations, nil
+}
+
+/*
+openAndRead reads the mock data from a JSON file
+	Return: entitysparql.Result, the formatted results from the database
+*/
+func openAndRead(file string) entitysparql.Result {
+	jsonFile, err := os.Open(file)
+	// if we os.Open returns an error then handle it
+	if err != nil {
+		fmt.Println(err)
+	}
+	// defer the closing of our jsonFile so that we can parse it later on
+	defer jsonFile.Close()
+	// read our opened jsonFile as a byte array.
+	var result entitysparql.Result
+	byteValue, _ := ioutil.ReadAll(jsonFile)
+	err = json.Unmarshal(byteValue, &result)
+	if err != nil {
+		fmt.Println(err)
+	}
+	return result
+}
diff --git a/usecases/sparql/executeQuery_test.go b/usecases/sparql/executeQuery_test.go
new file mode 100644
index 0000000..407729e
--- /dev/null
+++ b/usecases/sparql/executeQuery_test.go
@@ -0,0 +1,46 @@
+/*
+This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+package sparql
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var s *Service
+
+/*
+Creates the service
+*/
+func createService() {
+	if s != nil {
+		return
+	}
+	s = NewService()
+}
+
+/*
+Tests if the executor properly runs using the mock data
+	t: *testing.T, makes go recognize this as a test
+*/
+func TestExecution(t *testing.T) {
+	createService()
+	triples := `{
+		"triples": {
+			"r0": {
+				"from": "e0",
+				"to": "e1"
+			},
+			"r1": {
+				"from": "e2",
+				"to": "e1"
+			}
+		}
+	}`
+	_, err := s.ExecuteQuery("mock-query", triples, "../../main/sparql-mock-result.json")
+	assert.Nil(t, err)
+}
diff --git a/usecases/sparql/interface.go b/usecases/sparql/interface.go
new file mode 100644
index 0000000..09dbc8f
--- /dev/null
+++ b/usecases/sparql/interface.go
@@ -0,0 +1,21 @@
+/*
+This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
+© Copyright Utrecht University (Department of Information and Computing Sciences)
+*/
+
+package sparql
+
+/*
+Service SHOULD implement the Executor interface
+It currently does NOT
+*/
+type Service struct {
+}
+
+/*
+NewService creates a new ArangoDB executor service
+	Return: *Service, the new service
+*/
+func NewService() *Service {
+	return &Service{}
+}
-- 
GitLab