Where/how to prepare query results for use with visualisation

From the examples I have seen, is that they are mostly taking their data from a nodes array and a links array like this

      nodes: [...Array(N).keys()].map(i => ({ id: i })),
      links: [...Array(N).keys()]
        .filter(id => id)
        .map(id => ({
          source: id,
          target: Math.round(Math.random() * (id-1))
        }))

If I run such query

MATCH (a:Actor:Actor)-[r:ACTED_IN]->(m:Movie) 
WHERE m.year=1995
RETURN a as nodes,r as links,m
ORDER BY m.year ASC
LIMIT 10

I am getting more than 2 arrays back, to many properties etc.

Is there some sort of manual / example / best practice on how to prepare data for visualisation? Maybe special output functions?

What arrays and properties are you getting that are “to many”?

The output as is is going to be rows of actors related to the movie the acted in that was released in 1995. There could duplicate actors and movies across the rows because an actor could have been in multiple movies and a movie could have multiple actors. The data is flattened out to speak.

Would a hierarchical structure work better for you needs?

MATCH (m:Movie{year: 1995})
RETURN {
  movie: m{.*},
  actors: [(a:Actor)-[r:ACTED_IN]->(m) | {
     actor: a{.*},
     role: r{.*}
}]}
LIMIT 10

The above is just an example of outputting the data in a different format. Does this help with your visualization?

I have problem with the output, I was not even thinking about optimizing the query

this is currently my query

MATCH (a:Actor:Actor)-[r:ACTED_IN]->(m:Movie) 
WHERE m.year=2000 AND a.born IS NOT NULL 
RETURN a{id: id(a), labels: labels(a), .name, start: a.born , end: a.died, gender: CASE WHEN toInteger(rand()*2) = 1 THEN 'M' ELSE 'F' END} as nodes
      ,r{.role, source: id(startNode(r)), target: id(endNode(r)), age: duration.inDays(a.born, date(m.year+"-06-01")).days / 365 } as links
	  ,m{id: id(m), .title, start: date(m.year+"-01-01"), end: date(m.year+"-12-31") }
ORDER BY m.year ASC, a.start ASC
LIMIT 20

this generates arrays of elements nodes, links, m

[ { nodes: {}, links: {}, m {} },
{ nodes: {}, links: {}, m {} },
{ nodes: {}, links: {}, m {} },

I prefer this to be in just one nodes and one links
thus:

{"nodes":[ {}, {}, {} ],
"links:[ {},{},{} ]
}

The whole idea is that data is somewhat centred around the same timestamp, so you can stream this data.

How about this. I just did a little refactoring to embed the movie with the link, so I could group all them with the actor.

I removed the order by on movie year because they all start in 2000, due to your
WHER clause.

MATCH (a:Actor:Actor)-[r:ACTED_IN]->(m:Movie) 
WHERE m.year=2000 AND a.born IS NOT NULL 
WITH a, collect({
  link: r{.role, source: id(startNode(r)), target: id(endNode(r)), age: duration.inDays(a.born, date(m.year+"-06-01")).days / 365 },
  movie: m{id: id(m), .title, start: date(m.year+"-01-01"), end: date(m.year+"-12-31") }
}) as movies 
RETURN a{id: id(a), labels: labels(a), .name, start: a.born , end: a.died, gender: CASE WHEN toInteger(rand()*2) = 1 THEN 'M' ELSE 'F' END, movies: movies} as nodes
ORDER BY a.born ASC

Ok, I just saw your recent post. You could collect the nodes and links into separate lists. You could get duplicates in these lists, which you could remove by collecting with DISTINCT. This would mess up your relationship where the same element position in each list represents the same timestamp in your streaming paradigm.

MATCH (a:Actor:Actor)-[r:ACTED_IN]->(m:Movie) 
WHERE m.year=2000 AND a.born IS NOT NULL 
RETURN collect(a{id: id(a), labels: labels(a), .name, start: a.born , end: a.died, gender: CASE WHEN toInteger(rand()*2) = 1 THEN 'M' ELSE 'F' END}) as nodes, 
Collect(r{.role, source: id(startNode(r)), target: id(endNode(r)), age: duration.inDays(a.born, date(m.year+"-06-01")).days / 365 }) as links,
Collect(m{id: id(m), .title, start: date(m.year+"-01-01"), end: date(m.year+"-12-31") }) as movies
ORDER BY m.year ASC, a.born ASC
LIMIT 20