package cypher import ( "errors" "fmt" "git.science.uu.nl/graphpolaris/query-conversion/entity" ) func performBasicHealthCheck(JSONQuery *entity.IncomingQueryJSON) (bool, error) { // Check to make sure all indexes exist // How many entities are there numEntities := len(JSONQuery.Entities) - 1 // How many relations there are numRelations := len(JSONQuery.Relations) - 1 // Make sure no entity should be returned that is outside the range of that list for _, e := range JSONQuery.Return.Entities { // If this entity references an entity that is outside the range if e > numEntities || e < 0 { return false, errors.New("non-existing entity referenced in return") } } // Make sure that no relation mentions a non-existing entity for _, r := range JSONQuery.Relations { if r.FromID > numEntities || r.ToID > numEntities { return false, errors.New("non-exisiting entity referenced in relation") } } // Make sure no non-existing relation is tried to be returned for _, r := range JSONQuery.Return.Relations { if r > numRelations || r < 0 { return false, errors.New("non-existing relation referenced in return") } } return true, nil } // checkForQueryCluster will detect (and separate?) if there are multiple queries in the query panel and will try to sepate the queries. // Maybe also delete floating pills that have no connection (but that is a different function) func checkForQueryCluster(JSONQuery *entity.IncomingQueryJSON) (*entity.IncomingQueryJSON, *entity.IncomingQueryJSON, bool) { // Notes naar moizelf: alle pills die dingen aan elkaar verbinden zijn relations en group bys, filter lijken 'op' een lijntje te leven, maar niet als schakel te dienen // Dit zou recursief kunnen: vind een cluster in de json, haal alles uit de json dat verbonden zit aan die cluster, voer de functie opnieuw uit op het restand cluster := make(map[string]bool) // aka een set (entities e0 e1 e2, relations r0 .., groub by g0 ..) // Dit is het startpunt van de cluster, vrij veel if elses ivm half afgemaakte queries // Lots of existance checks if len(JSONQuery.Relations) > 0 { rel := fmt.Sprintf("r%v", JSONQuery.Relations[0].ID) cluster[rel] = true if JSONQuery.Relations[0].ToID != -1 { // Take the first letter: entities with ID 0 -> e0 to := fmt.Sprintf("%v%v", JSONQuery.Relations[0].ToType[0], JSONQuery.Relations[0].ToID) cluster[to] = true } if JSONQuery.Relations[0].FromID != -1 { from := fmt.Sprintf("%v%v", JSONQuery.Relations[0].FromType[0], JSONQuery.Relations[0].FromID) cluster[from] = true } } else if len(JSONQuery.GroupBys) > 0 { gb := fmt.Sprintf("g%v", JSONQuery.GroupBys[0].ID) cluster[gb] = true // TODO: Wat te doen als de groupby niet goed is aangesloten, want dat crasht ie nogal atm group := fmt.Sprintf("%v%v", JSONQuery.GroupBys[0].GroupType[0], JSONQuery.GroupBys[0].GroupID) cluster[group] = true by := fmt.Sprintf("%v%v", JSONQuery.GroupBys[0].ByType[0], JSONQuery.GroupBys[0].ByID) cluster[by] = true } else if len(JSONQuery.Modifiers) > 0 { // I guess dat je ook een enkele entity met bepaalde constraints kan tellen ofzo? of kan averagen // TODO } // Relation toevoegen aan de map // Is er geen relation doe dan groupby // is die er ook niet dan rip for { stop := true // kijk langs alle relations en group bys of ie verbonden is aan de cluster en nog niet in de set zit // Is dat zo run m opnieuw en kijk of daar weer dingen aan verbonden zijn for _, rel := range JSONQuery.Relations { // check of de rel er al in zit, dan kan ie geskipped worden // zo nee kijk of een van de entities of group by's erin zit, dan is deze dus verbonden rela := fmt.Sprintf("r%v", rel.ID) if cluster[rela] { // If it is already in the cluster then we dont care continue } partOfCluster := false // Now comes the check to see if one of its endpoints is in the cluster, meaning everything is in the cluster if rel.ToID != -1 { to := fmt.Sprintf("%v%v", rel.ToType[0], rel.ToID) if cluster[to] { partOfCluster = true } } if rel.FromID != -1 { from := fmt.Sprintf("%v%v", rel.FromType[0], rel.FromID) cluster[from] = true if cluster[from] { partOfCluster = true } } if partOfCluster { if rel.ToID != -1 { to := fmt.Sprintf("%v%v", rel.ToType[0], rel.ToID) cluster[to] = true } if rel.FromID != -1 { from := fmt.Sprintf("%v%v", rel.FromType[0], rel.FromID) cluster[from] = true } stop = false } } // Now the same for Group by's for _, gb := range JSONQuery.GroupBys { gby := fmt.Sprintf("g%v", gb.ID) if cluster[gby] { continue } // It should have been checked that the connections of the group by are valid, since a group by must have all connections filled (in contrary of a relation) group := fmt.Sprintf("%v%v", gb.GroupType[0], gb.GroupID) by := fmt.Sprintf("%v%v", gb.ByType[0], gb.ByID) if cluster[group] || cluster[by] { cluster[gby] = true cluster[group] = true cluster[by] = true stop = false } } // ** then for modifiers? although modifiers havent changed yet, since their results must also be used in queries // Modifiers will change, so that is a problem for later if stop { // No new entities were added to the cluster, thus it is finished break } } // Now walk through the JSON and divide it into the cluster and rest restJSON := entity.IncomingQueryJSON{DatabaseName: JSONQuery.DatabaseName, Limit: JSONQuery.Limit} clusterJSON := entity.IncomingQueryJSON{DatabaseName: JSONQuery.DatabaseName, Limit: JSONQuery.Limit} isRest := false // Loop through entities for _, ent := range JSONQuery.Entities { name := fmt.Sprintf("e%v", ent.ID) if cluster[name] { clusterJSON.Entities = append(clusterJSON.Entities, ent) clusterJSON.Return.Entities = append(clusterJSON.Return.Entities, ent.ID) } else { restJSON.Entities = append(restJSON.Entities, ent) restJSON.Return.Entities = append(restJSON.Return.Entities, ent.ID) isRest = true } } // Loop through relations for _, rel := range JSONQuery.Relations { name := fmt.Sprintf("r%v", rel.ID) if cluster[name] { clusterJSON.Relations = append(clusterJSON.Relations, rel) clusterJSON.Return.Relations = append(clusterJSON.Return.Relations, rel.ID) } else { restJSON.Relations = append(restJSON.Relations, rel) restJSON.Return.Relations = append(restJSON.Return.Relations, rel.ID) isRest = true } } // Loop through groupby's for _, gb := range JSONQuery.GroupBys { name := fmt.Sprintf("g%v", gb.ID) if cluster[name] { clusterJSON.GroupBys = append(clusterJSON.GroupBys, gb) clusterJSON.Return.GroupBys = append(clusterJSON.Return.GroupBys, gb.ID) } else { restJSON.GroupBys = append(restJSON.GroupBys, gb) restJSON.Return.GroupBys = append(restJSON.Return.GroupBys, gb.ID) isRest = true } } // ** Loop through modifiers // Loop through filters // Filters were not done in the clustering, since they live on top of a connection, meaning they do not extend the cluster // This also means that if a From or a To is in the cluster, the other (and thus the filter) is in the cluster as well for _, filter := range JSONQuery.Filters { from := fmt.Sprintf("%v%v", filter.FromType[0], filter.FromID) if cluster[from] { clusterJSON.Filters = append(clusterJSON.Filters, filter) } else { restJSON.Filters = append(restJSON.Filters, filter) isRest = true } } return &clusterJSON, &restJSON, isRest // Nadat cluster is gevonden: maak twee nieuwe jsons aan: cluster en rest // Loop door de OG json en voeg alles aan of de cluster of de rest toe // Return cluster, rest en een bool die zegt of er een cluster is // Wss is het in 99% van de gevallen maar 1 cluster of een cluster met een verdwaalde node, maar toch } // ** MOGELIJK OBSOLETE, hangt af van de hierarchiefunctie /* checkQueryValidity performs checks to see if the query is valid. Returns a boolean indicating if the query is valid, the error will containt a custom message saying what is wrong with the query. It is obviously possible the query is still invalid, but that is for the database to find out. */ func checkQueryValidity(JSONQuery *entity.IncomingQueryJSON) (bool, error) { // The first test is to see if there are at least 2 returns, since only one return is not possible (we do not allow relation only queries) ret := JSONQuery.Return numOfReturns := len(ret.Entities) + len(ret.GroupBys) + len(ret.Relations) if numOfReturns < 2 { return false, errors.New("Insufficient return values") } return true, nil }