diff --git a/aql/convertQuery.go b/aql/convertQuery.go index b9b8129e10e6e019e80fa8f95148de3f88cf2c56..e02227adf727ca8306dc07ff22ecc6e9379caaf0 100644 --- a/aql/convertQuery.go +++ b/aql/convertQuery.go @@ -25,19 +25,18 @@ ConvertQuery converts an IncomingQueryJSON object into AQL func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, error) { // TODO: MICHAEL WANT A SINGLE ENTITY TO RETURN A SUMMARY OF ATTRIBUTE VALUES (HISTOGRAM THINGIES) // Check to make sure all indexes exist - // The largest possible id for an entity - entityCount := len(JSONQuery.Entities) - 1 - // The largest possible id for a relation - relationCount := len(JSONQuery.Relations) - 1 + // The count of entities + entityCount := len(JSONQuery.Entities) + // The count of relations + relationCount := len(JSONQuery.Relations) // There are no entities or relations, our query is empty - if entityCount < 0 && relationCount < 0 { + if entityCount <= 0 && relationCount <= 0 { fmt.Println("Empty query sent, returning default response") return defaultReturn() } // There are no enities or there are no relations - if entityCount < 0 || relationCount < 0 { - fmt.Println("No relations or entities sent, returning default response") - return defaultReturn() + if entityCount <= 0 || relationCount <= 0 { + return nil, errors.New("no relations or entities sent") } potentialErrors := entity.ValidateStruct(*JSONQuery) @@ -49,7 +48,7 @@ func (s *Service) ConvertQuery(JSONQuery *entity.IncomingQueryJSON) (*string, er return nil, errors.New("JSONQuery invalid") } - entityMap, relationMap, _ := fixIndices(JSONQuery) + entityMap, relationMap, _ := entity.FixIndices(JSONQuery) var tree []entity.Tree var topNode entity.QueryEntityStruct diff --git a/aql/convertQuery_test.go b/aql/convertQuery_test.go index 5effaf767a7baae2f07d03b04bf8ab2cfa3cd195..4546451e185dbe09e07dec42561a351d869a8488 100644 --- a/aql/convertQuery_test.go +++ b/aql/convertQuery_test.go @@ -1531,19 +1531,9 @@ func TestNoRelationsField(t *testing.T) { var JSONQuery entity.IncomingQueryJSON json.Unmarshal(query, &JSONQuery) - convertedResult, err := service.ConvertQuery(&JSONQuery) - - // Assert that there is no error - assert.NoError(t, err) + _, err := service.ConvertQuery(&JSONQuery) - // Assert that the result and the expected result are the same - correctConvertedResult := `LET nodes = first(RETURN UNION_DISTINCT([],[])) - LET edges = first(RETURN UNION_DISTINCT([],[])) - RETURN {"vertices":nodes, "edges":edges }` - regExCleaner := regexp.MustCompile(`\s+`) - correctCleanedResult := regExCleaner.ReplaceAllString(string(correctConvertedResult), " ") - convertedCleanedResult := regExCleaner.ReplaceAllString(*convertedResult, " ") - assert.Equal(t, correctCleanedResult, convertedCleanedResult) + assert.Equal(t, errors.New("no relations or entities sent"), err) } /* @@ -1607,3 +1597,321 @@ func TestIncorrectRelationFrom(t *testing.T) { // Assert that there is an error assert.Equal(t, errors.New("JSONQuery invalid"), err) } + +/* +Tests two separated chains consisting of 1 relation each + t: *testing.T, makes go recognise this as a test +*/ +func TestSeparatedChainSingleRelationPerChain(t *testing.T) { + // Setup for test + // Create query conversion service + service := NewService() + + query := []byte(`{ + "databaseName": "TweedeKamer", + "return": { + "entities": [ + 0, + 1, + 2, + 3 + ], + "relations": [ + 0 + ] + }, + "entities": [ + { + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "parties", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + } + ], + "relations": [ + { + "ID": 0, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 1, + "constraints":[] + }, + { + "ID": 1, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 3, + "constraints":[] + } + ], + "groupBys": [], + "limit": 5000, + "modifiers": [] + }`) + + // Unmarshall the incoming message into an IncomingJSONQuery object + var JSONQuery entity.IncomingQueryJSON + json.Unmarshal(query, &JSONQuery) + + _, err := service.ConvertQuery(&JSONQuery) + + // Assert that there is an error + assert.Equal(t, errors.New("JSONQuery invalid"), err) +} + +/* +Tests two separated chains consisting of 1 relation each + t: *testing.T, makes go recognise this as a test +*/ +func TestSeparatedChainDoubleRelationPerChain(t *testing.T) { + // Setup for test + // Create query conversion service + service := NewService() + + query := []byte(`{ + "databaseName": "TweedeKamer", + "return": { + "entities": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "relations": [ + 0 + ] + }, + "entities": [ + { + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "parties", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + }, + { + "name": "resolutions", + "ID": 4, + "constraints": [] + }, + { + "name": "resolutions", + "ID": 5, + "constraints": [] + } + ], + "relations": [ + { + "ID": 0, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 1, + "constraints":[] + }, + { + "ID": 1, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 3, + "constraints":[] + }, + { + "ID": 2, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 4, + "constraints":[] + }, + { + "ID": 3, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 5, + "constraints":[] + } + ], + "groupBys": [], + "limit": 5000, + "modifiers": [] + }`) + + // Unmarshall the incoming message into an IncomingJSONQuery object + var JSONQuery entity.IncomingQueryJSON + json.Unmarshal(query, &JSONQuery) + + _, err := service.ConvertQuery(&JSONQuery) + + // Assert that there is an error + assert.Equal(t, errors.New("JSONQuery invalid"), err) +} + +/* +Tests two separated chains with unequal relations per chain + t: *testing.T, makes go recognise this as a test +*/ +func TestSeparatedChainUnequalRelationPerChain(t *testing.T) { + // Setup for test + // Create query conversion service + service := NewService() + + query := []byte(`{ + "databaseName": "TweedeKamer", + "return": { + "entities": [ + 0, + 1, + 2, + 3, + 4 + ], + "relations": [ + 0 + ] + }, + "entities": [ + { + "name": "parliament", + "ID": 0, + "constraints": [] + }, + { + "name": "parties", + "ID": 1, + "constraints": [] + }, + { + "name": "parliament", + "ID": 2, + "constraints": [] + }, + { + "name": "parties", + "ID": 3, + "constraints": [] + }, + { + "name": "resolutions", + "ID": 4, + "constraints": [] + } + ], + "relations": [ + { + "ID": 0, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 0, + "toType": "entity", + "toID": 1, + "constraints":[] + }, + { + "ID": 1, + "name": "member_of", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 3, + "constraints":[] + }, + { + "ID": 2, + "name": "submits", + "depth": { + "min": 1, + "max": 1 + }, + "fromType": "entity", + "fromID": 2, + "toType": "entity", + "toID": 4, + "constraints":[] + } + ], + "groupBys": [], + "limit": 5000, + "modifiers": [] + }`) + + // Unmarshall the incoming message into an IncomingJSONQuery object + var JSONQuery entity.IncomingQueryJSON + json.Unmarshal(query, &JSONQuery) + + _, err := service.ConvertQuery(&JSONQuery) + + // Assert that there is an error + assert.Equal(t, errors.New("JSONQuery invalid"), err) +} diff --git a/aql/hierarchy_test.go b/aql/hierarchy_test.go index 073825408a750f9fdd6b6b3593851380d1032a58..8f8a507d0d594a6fb6835fe9efb6a7250a82b744 100644 --- a/aql/hierarchy_test.go +++ b/aql/hierarchy_test.go @@ -140,7 +140,7 @@ func TestHierarchyBasic(t *testing.T) { var JSONQuery entity.IncomingQueryJSON json.Unmarshal(query, &JSONQuery) // Get the hierarchy and turn it into JSON so we can turn it into strings later - entityMap, relationMap, _ := fixIndices(&JSONQuery) + entityMap, relationMap, _ := entity.FixIndices(&JSONQuery) treeList, topNode := createHierarchy(&JSONQuery, entityMap, relationMap) jsonTopNode, err := json.Marshal(topNode) if err != nil { diff --git a/aql/fixIndices.go b/entity/fixIndices.go similarity index 66% rename from aql/fixIndices.go rename to entity/fixIndices.go index 1056cf4489acc6c97f0a091368228ccd7afb7f08..dc97192c46105a0b415fa8775aec60f2c3407049 100644 --- a/aql/fixIndices.go +++ b/entity/fixIndices.go @@ -1,10 +1,9 @@ -package aql +package entity -import ( - "git.science.uu.nl/graphpolaris/query-conversion/entity" -) - -func fixIndices(JSONQuery *entity.IncomingQueryJSON) (map[int]int, map[int]int, map[int]int) { +/* +A function that maps pill IDs to a range of 0..x +*/ +func FixIndices(JSONQuery *IncomingQueryJSON) (map[int]int, map[int]int, map[int]int) { entityMap := make(map[int]int) for i, e := range JSONQuery.Entities { entityMap[e.ID] = i diff --git a/entity/queryStructValidator.go b/entity/queryStructValidator.go index 2d44ce86effe0459952591ccd86a4f6f17107bbc..d25f2d8bc9713fb370c70e611b29c34e741ece09 100644 --- a/entity/queryStructValidator.go +++ b/entity/queryStructValidator.go @@ -22,6 +22,10 @@ func ValidateStruct(JSONQuery IncomingQueryJSON) []error { if len(JSONQuery.GroupBys) != 0 { ret = append(ret, getIllegalToFromInGroupBy(JSONQuery, minEntityID, maxEntityID, minRelationID, maxRelationID)...) } + if len(ret) == 0 { + ret = append(ret, separatedChainExists(JSONQuery)...) + } + return ret } @@ -145,3 +149,19 @@ func getMinAndMaxGroupByID(groupBys []QueryGroupByStruct) (int, int) { return min, max } + +// Get chains that are not connected to the main chain +func separatedChainExists(JSONQuery IncomingQueryJSON) []error { + ret := make([]error, 0) + entityCount := len(JSONQuery.Entities) + relationCount := len(JSONQuery.Relations) + // A relation always has 2 entities if it's on its own + // If there is a chain (e-r-e-r-e) then there will be one more entity than relation + // If there is two seperate chains you will always end up adding at least 2 entities more than relations + // (e-r-e-r-e) and (e-r-e) has 3 relations but 5 entities, thus we know they are separate + if relationCount != entityCount-1 { + err := errors.New("separated chain found") + ret = append(ret, err) + } + return ret +}