package org.gbif.checklistbank.nub.validation; import org.gbif.api.vocabulary.Kingdom; import org.gbif.checklistbank.neo.traverse.TreeWalker; import org.gbif.checklistbank.nub.NubDb; import java.util.Map; import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Does some basic verifications on the neo4j nub tree * This does not rely on name usages or metrics to exist yet! */ public class NubTreeValidation implements NubValidation { private static final Logger LOG = LoggerFactory.getLogger(NubTreeValidation.class); private final NubDb db; private boolean valid = true; public NubTreeValidation(NubDb db) { this.db = db; } private Result query(String cypher) { LOG.debug("Execute: {}", cypher); return db.dao().getNeo().execute(cypher); } private void notExists(String errorMessage, String cypher) { try (Transaction tx = db.beginTx()) { Result res = query(cypher); if (res.hasNext()) { valid = false; Map<String, Object> row = res.next(); LOG.error(errorMessage, row); } } } private void assertCount(String errorMessage, String cypher, long expected) { try (Transaction tx = db.beginTx()) { Result res = query(cypher); Map<String, Object> row = res.next(); long count = (long) row.values().iterator().next(); if (expected != count) { valid = false; LOG.error(errorMessage, count); } } } @Override public boolean validate() { // no self loops whatsoever notExists("Self loops in {}", "MATCH (n:TAXON) WHERE (n)--(n) RETURN n LIMIT 1"); // no parent_of rels in different directions on a given node notExists("Contradicting parent relations in {}", "MATCH (n:TAXON) WHERE (n)-[:PARENT_OF*..6]->(n) RETURN n LIMIT 1"); // just kingdom roots assertCount("Too many roots {}", "MATCH (n:ROOT) RETURN count(n)", Kingdom.values().length); // no basionyms without basionym relation notExists("Basionym without basionym relation {}", "MATCH (b:BASIONYM) WHERE not (b)-[:BASIONYM_OF]->() RETURN b LIMIT 1"); // no basionym exists without an accepted or parent node notExists("Orphaned basionym found {}", "MATCH (b:BASIONYM) WHERE not (b)-[:SYNONYM_OF]->() and not (b)<-[:PARENT_OF]-() RETURN b LIMIT 1"); // no synonyms without synonym relation notExists("Synonym without synonym relation {}", "MATCH (s:SYNONYM) WHERE not (s)-[:SYNONYM_OF]->() RETURN s LIMIT 1"); // verify accepted tree has ranks in proper order try { TreeRankValidation rankVal = new TreeRankValidation(); TreeWalker.walkAcceptedTree(db.dao().getNeo(), rankVal); } catch (IllegalStateException e) { LOG.error("TreeRankValidation failed with {}", e.getMessage()); valid = false; } return valid; } }