Skip to content
Snippets Groups Projects
healthChecks.go 8.49 KiB
Newer Older
  • Learn to ignore specific revisions
  • Joris's avatar
    Joris committed
    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
    }