3.2.9. Maps

Cypher has solid support for maps.

The following graph is used for the examples below:

Figure 3.5. Graph
alt

3.2.9.1. Literal maps

From Cypher, you can also construct maps. Through REST you will get JSON objects; in Java they will be java.util.Map<String,Object>.

Query. 

RETURN { key: 'Value', listKey: [{ inner: 'Map1' }, { inner: 'Map2' }]}

Table 3.32. Result
{ key: 'Value', listKey: [{ inner: 'Map1' }, { inner: 'Map2' }]}

1 row

{listKey -> [{inner -> "Map1"},{inner -> "Map2"}], key -> "Value"}

Try this query live.  CREATE (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), (martin:Person {name: 'Martin Sheen'}), (wallstreet:Movie {title: 'Wall Street', year: 1987}), (reddawn:Movie {title: 'Red Dawn', year: 1984}), (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), (charlie)-[:ACTED_IN]->(wallstreet), (charlie)-[:ACTED_IN]->(reddawn), (charlie)-[:ACTED_IN]->(apocalypsenow), (martin)-[:ACTED_IN]->(wallstreet), (martin)-[:ACTED_IN]->(apocalypsenow) RETURN { key: 'Value', listKey: [{ inner: 'Map1' }, { inner: 'Map2' }]}

3.2.9.2. Map projection

Cypher supports a concept called "map projections". It allows for easily constructing map projections from nodes, relationships and other map values.

A map projection begins with the variable bound to the graph entity to be projected from, and contains a body of comma-separated map elements, enclosed by { and }.

map_variable {map_element, [, …​n]}

A map element projects one or more key-value pairs to the map projection. There exist four different types of map projection elements:

  • Property selector - Projects the property name as the key, and the value from the map_variable as the value for the projection.
  • Literal entry - This is a key-value pair, with the value being arbitrary expression key: <expression>.
  • Variable selector - Projects a variable, with the variable name as the key, and the value the variable is pointing to as the value of the projection. Its syntax is just the variable.
  • All-properties selector - projects all key-value pairs from the map_variable value.

Note that if the map_variable points to a null value, the whole map projection will evaluate to null.

Examples of map projections

Find 'Charlie Sheen' and return data about him and the movies he has acted in. This example shows an example of map projection with a literal entry, which in turn also uses map projection inside the aggregating collect().

Query. 

MATCH (actor:Person { name: 'Charlie Sheen' })-[:ACTED_IN]->(movie:Movie)
RETURN actor { .name, .realName, movies: collect(movie { .title, .year })}

Table 3.33. Result
actor

1 row

{name -> "Charlie Sheen", movies -> [{title -> "Apocalypse Now", year -> 1979},{title -> "Red Dawn", year -> 1984},{title -> "Wall Street", year -> 1987}], realName -> "Carlos Irwin Estévez"}

Try this query live.  CREATE (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), (martin:Person {name: 'Martin Sheen'}), (wallstreet:Movie {title: 'Wall Street', year: 1987}), (reddawn:Movie {title: 'Red Dawn', year: 1984}), (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), (charlie)-[:ACTED_IN]->(wallstreet), (charlie)-[:ACTED_IN]->(reddawn), (charlie)-[:ACTED_IN]->(apocalypsenow), (martin)-[:ACTED_IN]->(wallstreet), (martin)-[:ACTED_IN]->(apocalypsenow) MATCH (actor:Person {name: 'Charlie Sheen'})-[:ACTED_IN]->(movie:Movie) RETURN actor{ .name, .realName, movies: collect(movie{ .title, .year })}

Find all persons that have acted in movies, and show number for each. This example introduces an variable with the count, and uses a variable selector to project the value.

Query. 

MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie)
WITH actor, count(movie) AS nrOfMovies
RETURN actor { .name, nrOfMovies }

Table 3.34. Result
actor

2 rows

{name -> "Martin Sheen", nrOfMovies -> 2}

{name -> "Charlie Sheen", nrOfMovies -> 3}

Try this query live.  CREATE (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), (martin:Person {name: 'Martin Sheen'}), (wallstreet:Movie {title: 'Wall Street', year: 1987}), (reddawn:Movie {title: 'Red Dawn', year: 1984}), (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), (charlie)-[:ACTED_IN]->(wallstreet), (charlie)-[:ACTED_IN]->(reddawn), (charlie)-[:ACTED_IN]->(apocalypsenow), (martin)-[:ACTED_IN]->(wallstreet), (martin)-[:ACTED_IN]->(apocalypsenow) MATCH (actor:Person)-[:ACTED_IN]->(movie:Movie) WITH actor, count(movie) as nrOfMovies RETURN actor{ .name, nrOfMovies}

Again, focusing on 'Charlie Sheen', this time returning all properties from the node. Here we use an all-properties selector to project all the node properties, and additionally, explicitly project the property age. Since this property does not exist on the node, a null value is projected instead.

Query. 

MATCH (actor:Person { name: 'Charlie Sheen' })
RETURN actor { .*, .age }

Table 3.35. Result
actor

1 row

{name -> "Charlie Sheen", realName -> "Carlos Irwin Estévez", age -> <null>}

Try this query live.  CREATE (charlie:Person {name: 'Charlie Sheen', realName: 'Carlos Irwin Estévez'}), (martin:Person {name: 'Martin Sheen'}), (wallstreet:Movie {title: 'Wall Street', year: 1987}), (reddawn:Movie {title: 'Red Dawn', year: 1984}), (apocalypsenow:Movie {title: 'Apocalypse Now', year: 1979}), (charlie)-[:ACTED_IN]->(wallstreet), (charlie)-[:ACTED_IN]->(reddawn), (charlie)-[:ACTED_IN]->(apocalypsenow), (martin)-[:ACTED_IN]->(wallstreet), (martin)-[:ACTED_IN]->(apocalypsenow) MATCH (actor:Person {name: 'Charlie Sheen'}) RETURN actor{.*, .age}