Skip to content

Commit

Permalink
look up unique properties directly
Browse files Browse the repository at this point in the history
  • Loading branch information
danthe1st committed Sep 3, 2024
1 parent a4d1957 commit 3be1da8
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,30 @@ public interface AttributedGraph<N extends AttributedNode, E extends AttributedG

Collection<E> findOutgoingEdges(N node);
Collection<E> findIncomingEdges(N node);

/**
* Checks whether an attribute is unique for a specified node type.
*
* Returning {@code false} is also allowed if there is no concept of uniqueness, the uniqueness is uncertain or if {@link AttributedGraph#getNodeByUniqueAttribute(String, String, AttributeValue)} is not implemented
* @param key the key of the attribute to check
* @param nodeType the node type
* @return {@code true} if the attribute is unique, else {@code false}
* @see AttributedGraph#getNodeByUniqueAttribute(String, String, AttributeValue)
*/
default boolean isAttributeUniqueForNodeType(String key, String nodeType) {
return false;
}

/**
* Get a node by a unique attribute value as specified by {@link AttributedGraph#isAttributeUniqueForNodeType(String, String)}.
* @param nodeType the node type where the attribute is unique
* @param key the key of the unique attribute
* @param value the value of the unique attribute
* @return the node associated with the unique attribute value
* @throws UnsupportedOperationException if the attribute is not actually unique
* @see AttributedGraph#isAttributeUniqueForNodeType(String, String)
*/
default N getNodeByUniqueAttribute(String nodeType, String key, AttributeValue<?> value) {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.github.danthe1st.arebac.data.graph_pattern.GPNode;
import io.github.danthe1st.arebac.data.graph_pattern.GraphPattern;
import io.github.danthe1st.arebac.data.graph_pattern.constraints.AttributeRequirement;
import io.github.danthe1st.arebac.data.graph_pattern.constraints.AttributeRequirementOperator;
import io.github.danthe1st.arebac.data.graph_pattern.constraints.MutualExclusionConstraint;

/**
Expand Down Expand Up @@ -109,6 +110,12 @@ private void setupFixedVertices() throws NoResultException {
}
N graphNode = graph.findNodeById(value);

if(graphNode == null){
throw new NoResultException("Fixed node cannot be found");
}
candidates.put(patternNode, new HashSet<>(Set.of(graphNode)));
}else if(graph.isAttributeUniqueForNodeType(requirement.key(), patternNode.nodeType()) && requirement.operator() == AttributeRequirementOperator.EQUAL){
N graphNode = graph.getNodeByUniqueAttribute(patternNode.nodeType(), requirement.key(), requirement.value());
if(graphNode == null){
throw new NoResultException("Fixed node cannot be found");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
import java.util.Collection;
import java.util.stream.Stream;

import io.github.danthe1st.arebac.data.commongraph.attributed.AttributeValue;
import io.github.danthe1st.arebac.data.commongraph.attributed.AttributedGraph;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.schema.ConstraintDefinition;
import org.neo4j.graphdb.schema.ConstraintType;

public class Neo4jDB implements AttributedGraph<Neo4jNode, Neo4jEdge> {
private final Transaction tx;
Expand Down Expand Up @@ -36,4 +41,23 @@ private Collection<Neo4jEdge> findEdges(Neo4jNode node, Direction direction) {
}
}

@Override
public boolean isAttributeUniqueForNodeType(String key, String nodeType) {
for(ConstraintDefinition constraint : tx.schema().getConstraints(Label.label(nodeType))){
if(constraint.isConstraintType(ConstraintType.UNIQUENESS)){
for(String constraintKey : constraint.getPropertyKeys()){
if(constraintKey.equals(key)){
return true;
}
}
}
}
return false;
}

@Override
public Neo4jNode getNodeByUniqueAttribute(String nodeType, String key, AttributeValue<?> value) {
Node node = tx.findNode(Label.label(nodeType), key, value.value());
return new Neo4jNode(node);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ void testFindAnswersOfUser() {
try(Transaction tx = database.beginTx()){
Neo4jDB dbAsGraph = new Neo4jDB(tx);
Node someUserNode = tx.findNode(Label.label("User"), "uuid", 6309);
String someUserId = someUserNode.getElementId();
GraphPattern pattern = createFindAnswersPattern(someUserId);
GraphPattern pattern = createFindAnswersPattern(6309);
Set<List<Neo4jNode>> results = GPEval.evaluate(dbAsGraph, pattern);
assertNotEquals(0, results.size());
Set<List<Neo4jNode>> expectedAnswers = new HashSet<>();
Expand All @@ -82,7 +81,7 @@ void testFindAnswersOfUser() {
}
}

private GraphPattern createFindAnswersPattern(String requestorId) {
private GraphPattern createFindAnswersPattern(int userUUID) {
GPNode requestor = new GPNode("requestor", USER.name());
GPNode answer = new GPNode("answer", ANSWER.name());
return new GraphPattern(
Expand All @@ -91,7 +90,7 @@ private GraphPattern createFindAnswersPattern(String requestorId) {
List.of(new GPEdge(requestor, answer, null, PROVIDED.name()))
),
List.of(),
Map.of(requestor, List.of(new AttributeRequirement(ID_KEY, EQUAL, attribute(requestorId)))),
Map.of(requestor, List.of(new AttributeRequirement("uuid", EQUAL, attribute(userUUID)))),
Map.of(),
List.of(answer), Map.of("requestor", requestor, "answer", answer)
);
Expand Down Expand Up @@ -128,7 +127,7 @@ void testSimpleExampleWithInMemoryDB() {
)
);

Set<List<InMemoryGraphNode>> results = GPEval.evaluate(graph, createCommentsToSameQuestionInTagPattern(tag.id()));
Set<List<InMemoryGraphNode>> results = GPEval.evaluate(graph, createCommentsToSameQuestionInTagPattern(ID_KEY, tag.id()));
assertEquals(4, results.size());
assertEquals(
Set.of(
Expand Down Expand Up @@ -159,8 +158,7 @@ void testFindCommentsFromSameUsersToQuestionsInTag() {
expectedElementCount = testResult.stream().count();
}

Node tagNode = tx.findNode(TAG, "name", "neo4j");
GraphPattern pattern = createCommentsToSameQuestionInTagPattern(tagNode.getElementId());
GraphPattern pattern = createCommentsToSameQuestionInTagPattern("name", "neo4j");
Set<List<Neo4jNode>> results = assertTimeout(Duration.ofSeconds(30), () -> GPEval.evaluate(new Neo4jDB(tx), pattern));
assertNotEquals(0, results.size());
assertEquals(expectedElementCount, results.size());
Expand Down Expand Up @@ -204,7 +202,7 @@ private Node getOtherNodeOfSingleRelationship(Neo4jNode first, RelType relations
}
}

private GraphPattern createCommentsToSameQuestionInTagPattern(String tagId) {
private GraphPattern createCommentsToSameQuestionInTagPattern(String tagKey, String tagValue) {
GPNode tag = new GPNode("tag", TAG.name());

GPNode user1 = new GPNode("user1", USER.name());
Expand Down Expand Up @@ -244,14 +242,14 @@ private GraphPattern createCommentsToSameQuestionInTagPattern(String tagId) {
new MutualExclusionConstraint(question1, question2)
),
Map.of(
tag, List.of(new AttributeRequirement(ID_KEY, EQUAL, attribute(tagId)))
tag, List.of(new AttributeRequirement(tagKey, EQUAL, attribute(tagValue)))
),
Map.of(),
List.of(user1Comment1, user2Comment1, user1Comment2, user2Comment2),
Map.of("firstUser", user1, "secondUser", user2)
);
}

@Test
void testSelfAnswers() {
try(Transaction tx = database.beginTx()){
Expand All @@ -271,14 +269,14 @@ void testSelfAnswers() {
.map(List::of)
.collect(Collectors.toSet());
}
GraphPattern pattern = assertTimeout(Duration.ofSeconds(1), () -> createSelfAnswerPattern(tx.findNode(TAG, "name", "neo4j").getElementId()));
GraphPattern pattern = assertTimeout(Duration.ofSeconds(1), () -> createSelfAnswerPattern("neo4j"));
Set<List<Neo4jNode>> result = GPEval.evaluate(new Neo4jDB(tx), pattern);
assertNotEquals(0, result.size());
assertEquals(expectedResult, result);
}
}
private GraphPattern createSelfAnswerPattern(String elementId) {

private GraphPattern createSelfAnswerPattern(String tagName) {
GPNode tagNode = new GPNode("tag", TAG.name());
GPNode userNode = new GPNode("user", USER.name());
GPNode questionNode = new GPNode("question", QUESTION.name());
Expand All @@ -292,15 +290,15 @@ private GraphPattern createSelfAnswerPattern(String elementId) {
new GPEdge(userNode, answerNode, null, PROVIDED.name())
)
);

return new GraphPattern(
graph,
List.of(),
Map.of(tagNode, List.of(new AttributeRequirement(ID_KEY, EQUAL, attribute(elementId)))),
Map.of(tagNode, List.of(new AttributeRequirement("name", EQUAL, attribute(tagName)))),
Map.of(),
List.of(answerNode), Map.of()
);
}

}

0 comments on commit 3be1da8

Please sign in to comment.