2

I am currently using Neo4j TimeTree REST API and is there any way to navigate to the time before and after a given timestamp? My resolution is Second and I just realize that if the minute has changed, then there is no 'NEXT' relationship bridging the previous Second in previous Minute to the current Second. This makes the cypher query quite complicated and I just don't want to reinvent the wheel again if it's already available. Thanks in advance and your response would be really appreciated!

EDIT

I've got to reproduce the missing NEXT relationship issue again, as you can see in the picture below. This starts to happen from the third time I add a new Second time instant.

enter image description here

I actually create a NodeEntity to operate with the Second nodes. The class is like below.

@NodeEntity(label = "Second")
public class TimeTreeSecond {
    @GraphId
    private Long id;

    private Integer value;

    @Relationship(type = "CREATED_ON", direction = Relationship.INCOMING)
    private FilterVersionChange relatedFilterVersionChange;

    @Relationship(type = "NEXT", direction = Relationship.OUTGOING)
    private TimeTreeSecond nextTimeTreeSecond;

    @Relationship(type = "NEXT", direction = Relationship.INCOMING)
    private TimeTreeSecond prevTimeTreeSecond;

    public TimeTreeSecond() {
    }

    public Long getId() {
        return id;
    }

    public void next(TimeTreeSecond nextTimeTreeSecond) {
        this.nextTimeTreeSecond = nextTimeTreeSecond;
    }

    public FilterVersionChange getRelatedFilterVersionChange() {
        return relatedFilterVersionChange;
    }
}

The problem here is the Incoming NEXT relationship. When I omit that, everything works fine. Sometimes I even get this kind of exception in my console when I create the time instant repetitively with short delay.

Exception in thread "main" org.neo4j.ogm.session.result.ResultProcessingException: Could not initialise response
    at org.neo4j.ogm.session.response.GraphModelResponse.<init>(GraphModelResponse.java:38)
    at org.neo4j.ogm.session.request.SessionRequestHandler.execute(SessionRequestHandler.java:55)
    at org.neo4j.ogm.session.Neo4jSession.load(Neo4jSession.java:108)
    at org.neo4j.ogm.session.Neo4jSession.load(Neo4jSession.java:100)
    at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findOne(GraphRepositoryImpl.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:452)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:437)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:409)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy32.findOne(Unknown Source)
    at de.rwthaachen.service.core.FilterDefinitionServiceImpl.createNewFilterVersionChange(FilterDefinitionServiceImpl.java:100)
    at sampleapp.FilterLauncher.main(FilterLauncher.java:50)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: org.neo4j.ogm.session.result.ResultProcessingException: "errors":[{"code":"Neo.ClientError.Statement.InvalidType","message":"Expected a numeric value for empty iterator, but got null"}]}
    at org.neo4j.ogm.session.response.JsonResponse.parseErrors(JsonResponse.java:128)
    at org.neo4j.ogm.session.response.JsonResponse.parseColumns(JsonResponse.java:102)
    at org.neo4j.ogm.session.response.JsonResponse.initialiseScan(JsonResponse.java:46)
    at org.neo4j.ogm.session.response.GraphModelResponse.initialiseScan(GraphModelResponse.java:66)
    at org.neo4j.ogm.session.response.GraphModelResponse.<init>(GraphModelResponse.java:36)
    ... 27 more
2015-05-23 01:30:46,204  INFO ork.data.neo4j.config.Neo4jConfiguration:  62 - Intercepted exception

Below is one REST call example which I use to create the time instant nodes: http://localhost:7474/graphaware/timetree/1202/single/1432337658713?resolution=Second&timezone=Europe/Amsterdam

method that I use to create the data :

public FilterVersionChange createNewFilterVersionChange(String projectName,
                                                            String filterVersionName,
                                                            String filterVersionChangeDescription,
                                                            Set<FilterState> filterStates)
    {
        Long filterVersionNodeId = filterVersionRepository.findFilterVersionByName(projectName, filterVersionName);
        FilterVersion newFilterVersion = filterVersionRepository.findOne(filterVersionNodeId, 2);

        // Populate all the existing filters in the current project
        Map<String, Filter> existingFilters = new HashMap<String, Filter>();
        try
        {
            for(Filter filter : newFilterVersion.getProject().getFilters())
            {
                existingFilters.put(filter.getMatchingString(), filter);
            }
        }
        catch(Exception e) {}

        // Map the filter states to the populated filters, if any. Otherwise, create new filter for it.
        for(FilterState filterState : filterStates)
        {
            Filter filter = existingFilters.get(filterState.getMatchingString());
            if(filter == null)
            {
                filter = new Filter(filterState.getMatchingString(), filterState.getMatchingType(), newFilterVersion.getProject());
            }
            filterState.stateOf(filter);
        }

        Long now = System.currentTimeMillis();
        TimeTreeSecond timeInstantNode = timeTreeSecondRepository.findOne(timeTreeService.getFilterTimeInstantNodeId(projectName, now));
        FilterVersionChange filterVersionChange = new FilterVersionChange(filterVersionChangeDescription, now, filterStates, filterStates, newFilterVersion, timeInstantNode);
        FilterVersionChange addedFilterVersionChange = filterVersionChangeRepository.save(filterVersionChange);

        return addedFilterVersionChange;
    }
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Peter Sie
  • 175
  • 11
  • Would you be able to run this query in your browser and give the results : MATCH (n:Second {value:42}), (n2:Second {value:21}) MATCH (n)-[r:NEXT]-(n2) RETURN n, n2, type(r) thanks – Christophe Willemsen May 22 '15 at 23:08
  • @ChristopheWillemsen that query returns no rows. – Peter Sie May 22 '15 at 23:18
  • Thanks we will investigate further, can you provide the query that you use or the 3 timestamps used against the rest api in order to reproduce the issue – Christophe Willemsen May 22 '15 at 23:19
  • So you are creating your events from the related FilterVersionChange entity ? – Christophe Willemsen May 22 '15 at 23:25
  • @ChristopheWillemsen yes. So I query the time instant node with the rest api, then attach the FilterVersionChange to the newly created time instant node with Spring repository. – Peter Sie May 22 '15 at 23:37
  • Is there any possibility to use the JAVA API of timetree library from Spring Data Neo4j? I actually want to use the JAVA API, but I'm confused by the type differences with Spring Data Neo4j. – Peter Sie May 22 '15 at 23:44
  • Could you please post: the exact versions of Neo4j, GraphAware Framework, TimeTree, and SDN that you are using, and the sequence in which you execute whatever it is you execute to get to the point on your screenshot? By that I mean, for example: issue a rest call to X, then load an entity using this and this method in SDN, etc. Cheers! – Michal Bachman May 23 '15 at 00:25
  • And also the method that contains this line: FilterDefinitionServiceImpl.createNewFilterVersionChange(FilterDefinitionServiceImpl.java:100) – Luanne May 23 '15 at 04:46
  • @MichalBachman I program my application with Spring Data Neo4j 4.0.0.BUILD_SNAPSHOT, Neo4j Community 2.2.1 (zip installation), Neo4j TimeTree 2.2.1.30.21, and GraphAware Server Community 2.2.1.30. So the sequence is : I create the time instant node by using rest call, getting the node id of it, query that newly created time instant node using findOne function from the repository of TimeTreeSecond class that I wrote above, then attach my event node to that time instant node. and finally save the event node by using save function of the event node's repository. Hope it gives enough info. – Peter Sie May 23 '15 at 08:49
  • @Luanne I have edited my post and included the method you need. – Peter Sie May 23 '15 at 08:53
  • Thanks Peter, I should be able to look at this in the coming week – Luanne May 23 '15 at 17:06

2 Answers2

4

The Seconds are linked to each other via a NEXT relationship, even across minutes.

Hope this is what you meant enter image description here

Christophe Willemsen
  • 19,399
  • 2
  • 29
  • 36
Luanne
  • 19,145
  • 1
  • 39
  • 51
  • Oh I think I was too tired to see the lines of my graph. Because previously I was really sure there was no 'NEXT' relationship between seconds across minutes. But now when I wipe my db and try again, it's there. Sorry for this confusion. I can delete this thread if that's needed. – Peter Sie May 22 '15 at 14:00
  • another question, is it possible to remove a specific time instant from the tree with REST API? I see it available in JAVA API features but I can't find any instruction for REST API. Thanks! – Peter Sie May 22 '15 at 14:43
  • 1
    No, it isn't exposed via the REST api at the moment, but feel free to submit a PR :-) – Luanne May 22 '15 at 15:47
4

Leaving aside for a moment the specific use of TimeTree, I'd like to describe how to generally manage a doubly-linked list using SDN 4, specifically for the case where the underlying graph uses a single relationship type between nodes, e.g.

(post:Post)-[:NEXT]->(post:Post)

What you can't do

Due to limitations in the mapping framework, it is not possible to reliably declare the same relationship type twice in two different directions in your object model, i.e. this (currently) will not work:

class Post {
   @Relationship(type="NEXT", direction=Relationship.OUTGOING)
   Post next;

   @Relationship(type="NEXT", direction=Relationship.INCOMING)
   Post previous;
}

What you can do

Instead we can combine the @Transient annotation with the use of annotated setter methods to obtain the desired result:

class Post {
   Post next;

   @Transient Post previous;

   @Relationship(type="NEXT", direction=Relationship.OUTGOING)
   public void setNext(Post next) {
      this.next = next;
      if (next != null) {
          next.previous = this;
      }
   }
}

As a final point, if you then wanted to be able to navigate forwards and backwards through the entire list of Posts from any starting Post without having to continually refetch them from the database, you can set the fetch depth to -1 when you load the post, e.g:

findOne(post.getId(), -1);

Bear in mind that an infinite depth query will fetch every reachable object in the graph from the matched one, so use it with care!

Hope this is helpful

Vince
  • 2,181
  • 13
  • 16
  • This limitation is exactly as I thought, that it's from the SDN4. Thank you for your explanation and I will try your solution. I think I won't try to fetch with -1 option, it's too risky because the size of my graph is very big and all are connected. Hope the feature to navigate bidirectionally between the same type of node will be possible in the upcoming SDN. – Peter Sie May 24 '15 at 09:03