You have learned how to create, read, and delete nodes in the Neo4j database (the C, R, and D in the popular CRUD acronym). The only part of the puzzle left is to update (U) nodes in the database.
Your challenge is to create a custom method in the repository interface using the @Query
annotation with a provided Cypher statement that writes an update to the database. Once you have created the method and query, you will need to test the application and verify the results.
Define a custom repository method
You will update a property in the database to increment the imdbVotes
field. While you could do this programmatically through the domain class, it would require either passing all of the property values in the Movie
class (recall there are several) or creating a projection (covered in upcoming lessons). A simpler approach, though, is to use a custom Cypher query to update a specific individual property.
To start, open your project’s MovieRepository
interface in the src/main/java/com/example/appspringdata
folder and add a new method called incrementImdbVotes()
that takes a movieId
String as a parameter and returns a Movie
. Add the @Query
annotation to the method and provide the following Cypher statement as the value:
MATCH (m:Movie {movieId: $movieId})
SET m.imdbVotes = coalesce(m.imdbVotes+1, 1)
RETURN m;
Your completed MovieRepository
interface should match the one provided below.
Click to reveal the completed MovieRepository
class code
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
interface MovieRepository extends Neo4jRepository<Movie, String> {
@Query("MATCH (m:Movie)<-[r:ACTED_IN]-(p:Person)" +
"RETURN m, collect(r), collect(p) LIMIT 20;")
Iterable<Movie> findMoviesSubset();
@Query("MATCH (m:Movie)<-[r:ACTED_IN]-(p:Person {name: $name})" +
"RETURN m, collect(r), collect(p);")
Iterable<Movie> findMoviesByPerson(String name);
@Query("MATCH (m:Movie {movieId: $movieId}) " +
"SET m.imdbVotes = coalesce(m.imdbVotes+1, 1) " +
"RETURN m;")
Movie incrementImdbVotes(String movieId);
}
Now implement that newly-created method in the MovieController
class by creating a new @PutMapping
with and endpoint of /movies/updateVotes
that takes a movieId
string as a request parameter and returns a Movie
object. Use the incrementImdbVotes()
method to save the value and return the result.
Click to reveal the completed MovieController
class code
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PutMapping;
@RestController
@RequestMapping("/movies")
public class MovieController {
private final MovieRepository movieRepo;
public MovieController(MovieRepository movieRepo) {
this.movieRepo = movieRepo;
}
@GetMapping()
Iterable<Movie> findAllMovies() {
return movieRepo.findMoviesSubset();
}
@GetMapping("/{movieId}")
Optional<Movie> findMovieById(@PathVariable String movieId) {
return movieRepo.findById(movieId);
}
@PostMapping("/save")
Movie save(@RequestBody Movie movie) {
return movieRepo.save(movie);
}
@DeleteMapping("/delete")
void delete(@RequestParam String movieId) {
movieRepo.deleteById(movieId);
System.out.println("Deleted movie with movieId: " + movieId);
}
@GetMapping("/person")
Iterable<Movie> findMoviesByPerson(@RequestParam String name) {
return movieRepo.findMoviesByPerson(name);
}
@PutMapping("/updateVotes")
Movie updateVotes(@RequestParam String movieId) {
return movieRepo.incrementImdbVotes(movieId);
}
}
Test the application
With those pieces in place, you can now test your changes. Run the application and execute the following command in the terminal to see the results.
curl -X PUT 'localhost:8080/movies/updateVotes?movieId=9876'
{
"movieId":"9876",
"title":"MyMovie",
"plot":null,
"poster":null,
"url":null,
"imdbId":null,
"tmdbId":null,
"released":null,
"year":null,
"runtime":null,
"budget":null,
"revenue":null,
"imdbVotes":1,
"imdbRating":null,
"languages":null,
"countries":null,
"actors":[]
}
The output from the command should display the updated imdbVotes
field. If you execute the command multiple times, you should see the value increment each time.
You can verify the results in the database by running the provided query in the right-hand tab.
Lesson Summary
In this challenge, you used your knowledge to create a custom method with a Cypher statement to write data to the database.
Next, you will learn about projections and how to use them to to retrieve or manipulate custom subsets of domain classes.