Skip to content

17 Movies People Graph

JP Barbosa edited this page Apr 15, 2023 · 2 revisions

Movies People Graph

Common Movie Query Return

code ./packages/graph/src/movies/queries/_return.ts
export const _return = `
  RETURN DISTINCT movie {
    .*,
    id: id(movie),
    people: {
      actors: [ (person:Person)-[r:ACTED_IN]->(movie) | person { .*, id: id(person), roles: r.roles } ],
      directors: [ (person:Person)-[r:DIRECTED]->(movie) | person { .*, id: id(person) } ],
      producers: [ (person:Person)-[r:PRODUCED]->(movie) | person { .*, id: id(person) } ],
      writers: [ (person:Person)-[r:WROTE]->(movie) | person { .*, id: id(person) } ],
      reviewers: [ (person:Person)-[r:REVIEWED]->(movie) | person { .*, id: id(person) } ]
    }
  }
`;

Add People To getAll

code ./packages/graph/src/movies/queries/getAll.ts
import { _return } from './_return';

export const getAll = `
  WITH toLower($search) AS search
  MATCH (movie:Movie)
  OPTIONAL MATCH (person:Person)-[r]->(movie)
  WITH *
  WHERE
    search IS NULL
    OR search = ""
    OR toLower(movie.title) CONTAINS search
    OR toLower(person.name) CONTAINS search
  WITH movie
  ${_return}
  ORDER BY movie.title
`;

Add People To getById

code ./packages/graph/src/movies/queries/getById.ts
import { _return } from './_return';

export const getById = `
  MATCH (movie:Movie)
  WHERE id(movie) = $id
  ${_return}
`;

Add People To create

code ./packages/graph/src/movies/queries/create.ts
import { _return } from './_return';

const createRelationships = ({
  key,
  collection,
}: {
  key: string;
  collection: string;
}) => `
  WITH movie

  CALL {
    WITH movie
    UNWIND $movie.people.${collection} AS person
    MERGE (personNode:Person {
      name: person.name
    })
    SET
      personNode.born = person.born
    MERGE (personNode)-[r:${key}]->(movie)
    ${key === 'ACTED_IN' ? 'SET r.roles = person.roles' : ''}
    RETURN collect(personNode) AS ${collection}
  }
`;

export const create = `
  CREATE (movie:Movie)
  SET
    movie.title = $movie.title,
    movie.released = $movie.released,
    movie.tagline = $movie.tagline

  WITH movie

  ${createRelationships({ key: 'ACTED_IN', collection: 'actors' })}
  ${createRelationships({ key: 'DIRECTED', collection: 'directors' })}
  ${createRelationships({ key: 'PRODUCED', collection: 'producers' })}
  ${createRelationships({ key: 'WROTE', collection: 'writers' })}
  ${createRelationships({ key: 'REVIEWED', collection: 'reviewers' })}

  ${_return}
`;

Add People To update

code ./packages/graph/src/movies/queries/update.ts
import { _return } from './_return';

const updateRelationships = ({
  key,
  collection,
}: {
  key: string;
  collection: string;
}) => {
  return `
  WITH movie

  CALL {
    WITH movie
    UNWIND $movie.people.${collection} AS person
    MERGE (personNode:Person {
      name: person.name
    })
    SET
      personNode.born = person.born
    MERGE (personNode)-[r:${key}]->(movie)
    ${key === 'ACTED_IN' ? 'SET r.roles = person.roles' : ''}
    RETURN collect(ID(r)) AS relationshipIds
  }

  // Delete old relationships
  CALL {
    WITH movie, relationshipIds
    MATCH (personNode:Person)-[r:${key}]->(movie)
    WHERE NOT ID(r) IN relationshipIds
    DELETE r
    RETURN count(r) AS deleted
  }
  `;
};

export const update = `
  MATCH (movie:Movie)
  WHERE id(movie) = $id
  SET
    movie.title = $movie.title,
    movie.released = $movie.released,
    movie.tagline = $movie.tagline

  ${updateRelationships({ key: 'ACTED_IN', collection: 'actors' })}
  ${updateRelationships({ key: 'DIRECTED', collection: 'directors' })}
  ${updateRelationships({ key: 'PRODUCED', collection: 'producers' })}
  ${updateRelationships({ key: 'WROTE', collection: 'writers' })}
  ${updateRelationships({ key: 'REVIEWED', collection: 'reviewers' })}

  ${_return}
`;

Update tests

code ./packages/graph/src/movies/index.spec.ts
...

export const expectMoviesToBeEqual = (movie1: Movie, movie2: Movie) => {
  ...

  Object.keys(movie1Parsed.people).forEach((key) => {
    expect(movie1Parsed.people[key]).toIncludeSameMembers(
      movie2Parsed.people[key]
    );
  });
};

describe('graph.movies', () => {
  ...

  it('should create a movie', ...);

  it('should get the previously created movie', ...);

  it('should update movie tagline', ...);

  it('should add one actor and one reviewer to the movie', async () => {
    const [movie] = await graph.movies(session).getAll(sampleMovie.title);
    movie.people.actors.push({
      name: 'Clancy Brown',
      born: 1959,
      roles: ['Captain Andrew Ryan'],
    });
    const updatedMovie = await graph.movies(session).update(movie.id, movie);
    expectMoviesToBeEqual(updatedMovie, movie);
  });

  it('should remove one actor and one reviewer from the movie', async () => {
    const [movie] = await graph.movies(session).getAll(sampleMovie.title);
    movie.people.actors.pop();
    movie.people.reviewers.pop();
    const updatedMovie = await graph.movies(session).update(movie.id, movie);
    expectMoviesToBeEqual(updatedMovie, movie);
  });

  it('should delete the movie', ...);
});

Run tests

nx test graph
Determining test suites to run...
Neo4j container found at localhost:32768
 PASS   graph  packages/graph/src/people/index.spec.ts
 PASS   graph  packages/graph/src/movies/index.spec.ts

Test Suites: 2 passed, 2 total
Tests:       10 passed, 10 total
Snapshots:   0 total
Time:        4.252 s
Ran all test suites.

Commit

git add .
git commit -m "Movies People Graph"