Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 fix for h-omer#8 #13

Merged
merged 15 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ We would appreciate your feedback about our Versioner Core, how to improve and f

## Buy us a coffee :coffee:

This project is developed during our free time, and our free time is mostly during evening/night! So coffee is really helpful during our sessions :sweat_smile:. If you want to help us with that, you can buy us some :coffee: thanks to [PayPal](https://www.paypal.me/mfalcier/2)!
This project is developed during our free time, and our free time is mostly during evening/night! So coffee is really helpful during our sessions :sweat_smile:. If you want to help us with that, you can buy us some :coffee: thanks to [PayPal](https://www.paypal.me/mfalcier/2)!
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apply plugin: 'java'

group = 'org.homer'
version = '2.0.0'
version = '2.0.2'

description = """Neo4j Procedures for Graph Versioning"""

Expand Down
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,11 @@ name | parameters | return values | description
[graph.versioner.diff](#diff) | **stateFrom**, **stateTo** | operation, label, oldValue, newValue | Get a list of differences that must be applied to stateFrom in order to convert it into stateTo.
[graph.versioner.diff.from.previous](#diff-from-previous) | **state** | operation, label, oldValue, newValue | Get a list of differences that must be applied to the previous status of the given one in order to become the given state.
[graph.versioner.diff.from.current](#diff-from-current) | **state** | operation, label, oldValue, newValue | Get a list of differences that must be applied to the given state in order to become the current entity state.
[graph.versioner.relationships.createTo](#relationships-createTo) | **entitySource**, **List<entityDestination>**, relationshipType, *{key:value,...}*, *date* | **relationship** | Creates a new state for the source entity connected to each of the R nodes of the destinations with a relationship of the given type.
[graph.versioner.relationships.createFrom](#relationships-createFrom) | **List<entitySource>**, **entityDestination**, relationshipType, *{key:value,...}*, *date* | **relationship** | Creates a new state for each of the source entities connected to the R nodes of the destination with a relationship of the given type.
[graph.versioner.relationship.create](#relationship-create) | **entitySource**, **entityDestination**, relationshipType, *{key:value,...}*, *date* | **relationship** | Creates a new state for the source entity connected to the R node of the destination with a relationship of the given type.
[graph.versioner.relationship.delete](#relationship-delete) | **entitySource**, **entityDestination**, relationshipType, *date* | **result** | Creates a new state for the source entity without a custom relationship of the given type.
[graph.versioner.relationships.delete](#relationships-delete) | **entitySource**, **List<entityDestination>**, relationshipType, *date* | **result** | Creates a new state for the source entity without a custom relationships for each of the destination nodes of the given type.

## init

Expand Down
25 changes: 18 additions & 7 deletions src/main/java/org/homer/versioner/core/Utility.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package org.homer.versioner.core;

import org.apache.commons.lang3.tuple.Pair;
import org.homer.versioner.core.exception.VersionerCoreException;
import org.homer.versioner.core.output.NodeOutput;
import org.homer.versioner.core.output.RelationshipOutput;
import org.homer.versioner.core.procedure.RelationshipProcedure;
import org.neo4j.graphdb.*;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

Expand Down Expand Up @@ -143,10 +144,10 @@ public static void addCurrentState(Node state, Node entity, LocalDateTime instan
* @param state a {@link Node} representing the State
* @return {@link Boolean} result
*/
public static Boolean checkRelationship(Node entity, Node state) {
public static void checkRelationship(Node entity, Node state) throws VersionerCoreException {
Spliterator<Relationship> stateRelIterator = state.getRelationships(RelationshipType.withName(Utility.HAS_STATE_TYPE), Direction.INCOMING).spliterator();

Boolean check = StreamSupport.stream(stateRelIterator, false).map(hasStateRel -> {
StreamSupport.stream(stateRelIterator, false).map(hasStateRel -> {
Node maybeEntity = hasStateRel.getStartNode();
if (maybeEntity.getId() != entity.getId()) {
throw new VersionerCoreException("Can't patch the given entity, because the given State is owned by another entity.");
Expand All @@ -156,7 +157,6 @@ public static Boolean checkRelationship(Node entity, Node state) {
throw new VersionerCoreException("Can't find any entity node relate to the given State.");
});

return check;
}

/**
Expand Down Expand Up @@ -195,9 +195,9 @@ public static LocalDateTime defaultToNow(LocalDateTime date) {
* @param node the {@link Node} to check
* @throws VersionerCoreException
*/
public static void isEntityOrThrowException(Node node) throws VersionerCoreException {
public static void isEntityOrThrowException(Node node) {

streamOfIterable(node.getRelationships(RelationshipType.withName("CURRENT"), Direction.OUTGOING)).findAny()
streamOfIterable(node.getRelationships(RelationshipType.withName(CURRENT_TYPE), Direction.OUTGOING)).findAny()
.map(ignored -> streamOfIterable(node.getRelationships(RelationshipType.withName("R"), Direction.INCOMING)).findAny())
.orElseThrow(() -> new VersionerCoreException("The given node is not a Versioner Core Entity"));
}
Expand All @@ -218,11 +218,22 @@ public static Optional<Relationship> getCurrentRelationship(Node entity) {
.findFirst();
}

public static Optional<Node> getCurrentState(Node entity) {
return streamOfIterable(entity.getRelationships(RelationshipType.withName(CURRENT_TYPE), Direction.OUTGOING)).map(relationship -> relationship.getEndNode()).findFirst();
}

public static LocalDateTime convertEpochToLocalDateTime(Long epochDateTime) {
return Instant.ofEpochMilli(epochDateTime).atZone(ZoneId.systemDefault()).toLocalDateTime();
}

public static Boolean isSystemType(String type) {
public static <A, B> List<org.apache.commons.lang3.tuple.Pair<A, B>> zip(List<A> listA, List<B> listB) {
return IntStream.range(0, listA.size())
.mapToObj(i -> Pair.of(listA.get(i), listB.get(i)))
.collect(Collectors.toList());

}

public static boolean isSystemType(String type) {
return SYSTEM_RELS.contains(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.logging.Log;

import java.lang.reflect.InvocationTargetException;
import java.util.Optional;

/**
Expand All @@ -13,7 +14,7 @@
*/
public abstract class CoreProcedureBuilder<T extends CoreProcedure> {

private Class<T> clazz;
private final Class<T> clazz;

private GraphDatabaseService db;
private Log log;
Expand Down Expand Up @@ -62,13 +63,12 @@ public CoreProcedureBuilder<T> withLog(Log log) {
* @return instance
*/
Optional<T> instantiate() {
T instance;
T instance = null;
try {
instance = clazz.newInstance();
instance = clazz.getDeclaredConstructor().newInstance();
instance.db = db;
instance.log = log;
} catch (InstantiationException | IllegalAccessException e) {
instance = null;
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e ) {
log.error(e.getMessage());
}
return Optional.ofNullable(instance);
Expand Down
78 changes: 40 additions & 38 deletions src/main/java/org/homer/versioner/core/procedure/Get.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.homer.versioner.core.Utility;
import org.homer.versioner.core.output.NodeOutput;
import org.homer.versioner.core.output.PathOutput;
import org.neo4j.cypher.internal.compiler.v2_3.commands.expressions.PathValueBuilder;
import org.neo4j.graphalgo.impl.util.PathImpl;
import org.neo4j.graphdb.*;
import org.neo4j.procedure.Description;
Expand Down Expand Up @@ -44,22 +45,23 @@ public Stream<NodeOutput> getCurrentState(
.map(Relationship::getEndNode).map(NodeOutput::new).orElse(null));
}


//fix for error Node[xyz] not connected to this relationship[xyz]
@Procedure(value = "graph.versioner.get.all", mode = DEFAULT)
@Description("graph.versioner.get.all(entity) - Get all the State nodes for the given Entity.")
public Stream<PathOutput> getAllState(
@Name("entity") Node entity) {

PathImpl.Builder builder = new PathImpl.Builder(entity)
.push(entity.getSingleRelationship(RelationshipType.withName(Utility.CURRENT_TYPE), Direction.OUTGOING));
builder = StreamSupport.stream(entity.getRelationships(RelationshipType.withName(Utility.HAS_STATE_TYPE), Direction.OUTGOING).spliterator(), false)
//.sorted((a, b) -> -1 * Long.compare((long)a.getProperty(START_DATE_PROP), (long)b.getProperty(START_DATE_PROP)))
.reduce(
builder,
(build, rel) -> Optional.ofNullable(rel.getEndNode().getSingleRelationship(RelationshipType.withName(Utility.PREVIOUS_TYPE), Direction.OUTGOING))
.map(build::push)
.orElse(build),
(a, b) -> a);
return Stream.of(new PathOutput(builder.build()));
PathValueBuilder builder = new PathValueBuilder();
builder.addNode(entity);
builder.addOutgoingRelationship(entity.getSingleRelationship(RelationshipType.withName(Utility.CURRENT_TYPE), Direction.OUTGOING));
StreamSupport.stream(entity.getRelationships(RelationshipType.withName(Utility.HAS_STATE_TYPE), Direction.OUTGOING).spliterator(), false)
.forEach(rel ->
Optional.ofNullable(rel.getEndNode().getSingleRelationship(RelationshipType.withName(Utility.PREVIOUS_TYPE), Direction.OUTGOING))
.map(builder::addOutgoingRelationship)
);

return Stream.of(new PathOutput(builder.result()));
}

@Procedure(value = "graph.versioner.get.by.label", mode = DEFAULT)
Expand All @@ -86,31 +88,31 @@ public Stream<NodeOutput> getStateByDate(
.map(NodeOutput::new);
}

@Procedure(value = "graph.versioner.get.nth.state", mode = DEFAULT)
@Description("graph.versioner.get.nth.state(entity, nth) - Get the nth State node for the given Entity.")
public Stream<NodeOutput> getNthState(
@Name("entity") Node entity,
@Name("nth") long nth) {

return getCurrentState(entity)
.findFirst()
.flatMap(currentState -> getNthStateFrom(currentState.node, nth))
.map(Utility::streamOfNodes)
.orElse(Stream.empty());
}

private Optional<Node> getNthStateFrom(Node state, long nth) {

return Stream.iterate(Optional.of(state), s -> s.flatMap(this::jumpToPreviousState))
.limit(nth + 1)
.reduce((a, b) -> b) //get only the last value (apply jumpToPreviousState n times
.orElse(Optional.empty());
}

private Optional<Node> jumpToPreviousState(Node state) {

return StreamSupport.stream(state.getRelationships(RelationshipType.withName(Utility.PREVIOUS_TYPE), Direction.OUTGOING).spliterator(), false)
.findFirst()
.map(Relationship::getEndNode);
}
@Procedure(value = "graph.versioner.get.nth.state", mode = DEFAULT)
@Description("graph.versioner.get.nth.state(entity, nth) - Get the nth State node for the given Entity.")
public Stream<NodeOutput> getNthState(
@Name("entity") Node entity,
@Name("nth") long nth) {

return getCurrentState(entity)
.findFirst()
.flatMap(currentState -> getNthStateFrom(currentState.node, nth))
.map(Utility::streamOfNodes)
.orElse(Stream.empty());
}

private Optional<Node> getNthStateFrom(Node state, long nth) {

return Stream.iterate(Optional.of(state), s -> s.flatMap(this::jumpToPreviousState))
.limit(nth + 1)
.reduce((a, b) -> b) //get only the last value (apply jumpToPreviousState n times
.orElse(Optional.empty());
}

private Optional<Node> jumpToPreviousState(Node state) {

return StreamSupport.stream(state.getRelationships(RelationshipType.withName(Utility.PREVIOUS_TYPE), Direction.OUTGOING).spliterator(), false)
.findFirst()
.map(Relationship::getEndNode);
}
}
Loading