1. 介绍
介绍如何使用Neo4j的插件,目前支持neo4j 3.5.x以上版本
目前支持neo4j与hibernate混用模式,用户角色等还是使用关系型数据库存储,而需要使用neo4j特性的部分使用neo4j库.
2. 使用
gradle中增加依赖库。
implementation('org.yunchen.gb:gb-plugin-neo4j:1.4.0.0')
implementation('org.grails:grails-datastore-gorm-neo4j:8.1.0')
在application.yml文件中加入如下的配置
grails:
neo4j:
url: bolt://localhost:7687
username: neo4j
password: neo4j123456
3. neo4j 配置
3.1. server端
使用docker 运行neo4j server
docker pull neo4j:3.5.8 docker run --publish=7473:7473 --publish=7474:7474 --publish=7687:7687 neo4j
访问http://localhost:7474 ,使用neo4j/neo4j 登录服务器后,首次访问会提示修改密码,将密码改为 neo4j123456
4. 规约
4.1. domain设置
需要在domain类中设置静态属性mapWith
static mapWith = "neo4j"
例子:(映射Node)
import grails.neo4j.Node
...
@Entity
@Title(zh_CN = "个人信息表")
@JsonIgnoreProperties(["errors", "metaClass", "dirty", "attached", "dirtyPropertyNames","handler","target","session","entityPersisters","hibernateLazyInitializer","initialized","proxyKey","children"])
class Person implements Node<Person>{ (1)
static mapWith = "neo4j" (2)
String name;
String sex;
static hasMany = [friends: Person] (3)
static constraints = {
name(nullable: false,blank:false,size:0..100)
sex(nullable: false,blank:false,size:0..10)
}
static mapping = {
}
}
1 | 标记domain类实现Node这个trait |
2 | 标记domain类使用neo4j映射 |
3 | 标记这个有好友这个关系 |
4.2. 插入数据
如下在一个事务空间,插入node数据和关系
private void initPersonWithNeo4j(){
Person.withNewTransaction {
Person joe = Person.findByName('Joe')
if(!joe){
joe = new Person(name: "Joe",sex:'male').save(flush:true)
}
Person barney = Person.findByName('Barney')
if(!barney){
barney =new Person(name: "Barney",sex:'female').save(flush:true)
}
barney.addToFriends(joe)
Person fred = Person.findByName('Fred')
if(!fred){
fred = new Person(name: "Fred",sex:'male').save(flush:true)
}
fred.addToFriends(barney)
//查询node间的关系
Path<Person, Person> path = Person.findShortestPath(fred, joe, 15)
for(Path.Segment<Person, Person> segment in path) {
println segment.start().name
println segment.end().name
}
}
}
在neo4j可控制台输入如下命令删除所有数据 match (n) detach delete n |
4.3. 查询数据
在domain类上,扩展出findShortestPath等方法
//查询node间的关系
Path<Person, Person> path = Person.findShortestPath(fred, joe, 15)
for(Path.Segment<Person, Person> segment in path) {
println segment.start().name
println segment.end().name
}
5. 其他示例
映射 Releationship
5.1. Person
@Entity
@Title(zh_CN = "个人信息表")
@JsonIgnoreProperties(["errors", "metaClass", "dirty", "attached", "dirtyPropertyNames","handler","target","session","entityPersisters","hibernateLazyInitializer","initialized","proxyKey","children"])
class Person implements Node<Person>{
static mapWith = "neo4j"
String name
String sex
static hasMany = [friends: Person,appearances:CastMember]
static constraints = {
name(nullable: false,blank:false,size:0..100)
sex(nullable: false,blank:false,size:0..10)
}
static mapping = {
}
}
5.2. Movie
@Entity
@Title(zh_CN = "电影表")
@JsonIgnoreProperties(["errors", "metaClass", "dirty", "attached", "dirtyPropertyNames","handler","target","session","entityPersisters","hibernateLazyInitializer","initialized","proxyKey","children"])
class Movie implements Node<Movie> {
static mapWith = "neo4j"
String title
static hasMany = [cast:CastMember]
}
5.3. CastMember
@Entity
@Title(zh_CN = "演职员表")
@JsonIgnoreProperties(["errors", "metaClass", "dirty", "attached", "dirtyPropertyNames","handler","target","session","entityPersisters","hibernateLazyInitializer","initialized","proxyKey","children"])
class CastMember implements Relationship<Person, Movie> {
static mapWith = "neo4j"
List<String> roles = []
}
5.4. 查询操作
def person=Person.findByName('Keanu') if(!person){ person=new Person(name:'Keanu',sex:'male').save(flush:true) } def movie=Movie.findByTitle('The Matrix') if(!movie){ movie=new Movie(title:'The Matrix').save(flush:true) } def castMember = new CastMember( type: "ACTED_IN", // "DIRECTED", from: person, to: movie, roles: ["Neo"]) castMember['realName'] = "Thomas Anderson" castMember.save(flush:true) List keanuCastings = CastMember.where { from.name == "Keanu" }.list() keanuCastings.each { println it.from.name + " played " + it.roles + " in " + it.to.title }
默认项目在hibernate的transaction中,neo4j的写操作会报错"org.springframework.transaction.NoTransactionException: Cannot flush write operations without an active transaction!" 解决办法是在neo4j的事务下操作如: |
FilmPerson.withNewTransaction { ..... //do something with neo4j write operations }
6. 经典电影示例
neo4j 默认携带的电影示例
6.1. 初始化数据
默认neo4j提供cypher语句进行数据初始化,提供了对象的初始化语句如下:
Movie.withNewTransaction {
//TheMatrix
Movie matrix1= new Movie(title:'The Matrix', released:1999, tagline:'Welcome to the Real World').save(flush:true)
Person Keanu =new Person(name:'Keanu Reeves', born:1964).save(flush:true)
Person Carrie =new Person(name:'Carrie-Anne Moss', born:1967).save(flush:true)
Person Laurence =new Person(name:'Laurence Fishburne',born:1961).save(flush:true)
Person Hugo =new Person(name:'Hugo Weaving', born:1960).save(flush:true)
Person LillyW =new Person(name:'Lilly Wachowski', born:1967).save(flush:true)
Person LanaW =new Person(name:'Lana Wachowski', born:1965).save(flush:true)
Person JoelS =new Person(name:'Joel Silver', born:1952).save(flush:true)
new CastMember(from:Keanu, to: matrix1, roles: ["Neo"]).save(flush:true)
new CastMember(from:Carrie, to: matrix1, roles: ["Trinity"]).save(flush:true)
new CastMember(from:Laurence, to: matrix1, roles: ["Morpheus"]).save(flush:true)
new CastMember(from:Hugo, to: matrix1, roles: ["Agent Smith"]).save(flush:true)
new CastMember(from:LillyW, to: matrix1,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:LanaW, to: matrix1,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:JoelS, to: matrix1,type: CastMember.RoleType.PRODUCED).save(flush:true)
//neo4j cto 自己加戏
//Person Emil =new Person(name:"Emil Eifrem", born:1978).save(flush:true)
//new CastMember(from:Emil, to: matrix1, roles: ["Emil"]).save(flush:true)
//TheMatrixReloaded
Movie matrix2= new Movie(title:'The Matrix Reloaded', released:2003, tagline:'Free your mind').save(flush:true)
new CastMember(from:Keanu, to: matrix2, roles: ["Neo"]).save(flush:true)
new CastMember(from:Carrie, to: matrix2, roles: ["Trinity"]).save(flush:true)
new CastMember(from:Laurence, to: matrix2, roles: ["Morpheus"]).save(flush:true)
new CastMember(from:Hugo, to: matrix2, roles: ["Agent Smith"]).save(flush:true)
new CastMember(from:LillyW, to: matrix2,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:LanaW, to: matrix2,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:JoelS, to: matrix2,type: CastMember.RoleType.PRODUCED).save(flush:true)
//TheMatrixRevolutions
Movie matrix3= new Movie(title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end').save(flush:true)
new CastMember(from:Keanu, to: matrix3, roles: ["Neo"]).save(flush:true)
new CastMember(from:Carrie, to: matrix3, roles: ["Trinity"]).save(flush:true)
new CastMember(from:Laurence, to: matrix3, roles: ["Morpheus"]).save(flush:true)
new CastMember(from:Hugo, to: matrix3, roles: ["Agent Smith"]).save(flush:true)
new CastMember(from:LillyW, to: matrix3,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:LanaW, to: matrix3,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:JoelS, to: matrix3,type: CastMember.RoleType.PRODUCED).save(flush:true)
//theDevilsAdvocate
Movie theDevilsAdvocate= new Movie(title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways').save(flush:true)
Person Charlize =new Person(name:'Charlize Theron', born:1975).save(flush:true)
Person Al =new Person(name:'Al Pacino', born:1940).save(flush:true)
Person Taylor =new Person(name:'Taylor Hackford', born:1944).save(flush:true)
new CastMember(from:Keanu, to: theDevilsAdvocate, roles: ["Kevin Lomax" ]).save(flush:true)
new CastMember(from:Charlize, to: theDevilsAdvocate, roles: ["Mary Ann Lomax" ]).save(flush:true)
new CastMember(from:Al, to: theDevilsAdvocate, roles: ["John Milton" ]).save(flush:true)
new CastMember(from:Taylor, to: theDevilsAdvocate, type: CastMember.RoleType.DIRECTED).save(flush:true)
//AFewGoodMen
Movie AFewGoodMen= new Movie(title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth.").save(flush:true)
Person TomC =new Person(name:'Tom Cruise', born:1962).save(flush:true)
Person JackN =new Person(name:'Jack Nicholson', born:1937).save(flush:true)
Person DemiM =new Person(name:'Demi Moore', born:1962).save(flush:true)
Person KevinB =new Person(name:'Kevin Bacon', born:1958).save(flush:true)
Person KieferS =new Person(name:'Kiefer Sutherland', born:1966).save(flush:true)
Person NoahW =new Person(name:'Noah Wyle', born:1971).save(flush:true)
Person CubaG =new Person(name:'Cuba Gooding Jr.', born:1968).save(flush:true)
Person KevinP =new Person(name:'Kevin Pollak', born:1957).save(flush:true)
Person JTW =new Person(name:'J.T. Walsh', born:1943).save(flush:true)
Person JamesM =new Person(name:'James Marshall', born:1967).save(flush:true)
Person ChristopherG =new Person(name:'Christopher Guest', born:1948).save(flush:true)
Person RobR =new Person(name:'Rob Reiner', born:1947).save(flush:true)
Person AaronS =new Person(name:'Aaron Sorkin', born:1961).save(flush:true)
new CastMember(from:TomC, to: AFewGoodMen, roles: ["Lt. Daniel Kaffee" ]).save(flush:true)
new CastMember(from:JackN, to: AFewGoodMen, roles: ["Col. Nathan R. Jessup" ]).save(flush:true)
new CastMember(from:DemiM, to: AFewGoodMen, roles: ["Lt. Cdr. JoAnne Galloway" ]).save(flush:true)
new CastMember(from:KevinB, to: AFewGoodMen, roles: ["Capt. Jack Ross" ]).save(flush:true)
new CastMember(from:KieferS, to: AFewGoodMen, roles: ["Lt. Jonathan Kendrick" ]).save(flush:true)
new CastMember(from:NoahW, to: AFewGoodMen, roles: ["Cpl. Jeffrey Barnes" ]).save(flush:true)
new CastMember(from:CubaG, to: AFewGoodMen, roles: ["Cpl. Carl Hammaker" ]).save(flush:true)
new CastMember(from:KevinP, to: AFewGoodMen, roles: ["Lt. Sam Weinberg" ]).save(flush:true)
new CastMember(from:JTW, to: AFewGoodMen, roles: ["Lt. Col. Matthew Andrew Markinson" ]).save(flush:true)
new CastMember(from:JamesM, to: AFewGoodMen, roles: ["Pfc. Louden Downey" ]).save(flush:true)
new CastMember(from:ChristopherG, to: AFewGoodMen, roles: ["Dr. Stone" ]).save(flush:true)
new CastMember(from:AaronS, to: AFewGoodMen, roles: ["Man in Bar" ]).save(flush:true)
new CastMember(from:RobR, to: AFewGoodMen,type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:AaronS, to: AFewGoodMen, type: CastMember.RoleType.WROTE).save(flush:true)
//Top Gun
Movie TopGun= new Movie(title:"Top Gun", released:1986, tagline:"I feel the need, the need for speed.").save(flush:true)
Person KellyM =new Person(name:'Kelly McGillis', born:1957).save(flush:true)
Person ValK =new Person(name:'Val Kilmer', born:1959).save(flush:true)
Person AnthonyE =new Person(name:'Anthony Edwards', born:1962).save(flush:true)
Person TomS =new Person(name:'Tom Skerritt', born:1933).save(flush:true)
Person MegR =new Person(name:'Meg Ryan', born:1961).save(flush:true)
Person TonyS =new Person(name:'Tony Scott', born:1944).save(flush:true)
Person JimC =new Person(name:'Jim Cash', born:1941).save(flush:true)
new CastMember(from:TomC, to: TopGun, roles: ["Maverick" ]).save(flush:true)
new CastMember(from:KellyM, to: TopGun, roles: ["Charlie" ]).save(flush:true)
new CastMember(from:ValK, to: TopGun, roles: ["Iceman" ]).save(flush:true)
new CastMember(from:AnthonyE, to: TopGun, roles: ["Goose" ]).save(flush:true)
new CastMember(from:TomS, to: TopGun, roles: ["Viper" ]).save(flush:true)
new CastMember(from:MegR, to: TopGun, roles: ["Carole" ]).save(flush:true)
new CastMember(from:TonyS, to: TopGun, type:CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:JimC, to: TopGun, type:CastMember.RoleType.WROTE).save(flush:true)
//Jerry Maguire
Movie JerryMaguire= new Movie(title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.').save(flush:true)
Person ReneeZ =new Person(name:'Renee Zellweger', born:1969).save(flush:true)
Person KellyP =new Person(name:'Kelly Preston', born:1962).save(flush:true)
Person JerryO =new Person(name:"Jerry O'Connell", born:1974).save(flush:true)
Person JayM =new Person(name:'Jay Mohr', born:1970).save(flush:true)
Person BonnieH =new Person(name:'Bonnie Hunt', born:1961).save(flush:true)
Person ReginaK =new Person(name:'Regina King', born:1971).save(flush:true)
Person JonathanL =new Person(name:'Jonathan Lipnicki', born:1996).save(flush:true)
Person CameronC =new Person(name:'Cameron Crowe', born:1957).save(flush:true)
new CastMember(from:TomC, to: JerryMaguire, roles: ["Jerry Maguire" ]).save(flush:true)
new CastMember(from:CubaG, to: JerryMaguire, roles: ["Rod Tidwell" ]).save(flush:true)
new CastMember(from:ReneeZ, to: JerryMaguire, roles: ["Dorothy Boyd" ]).save(flush:true)
new CastMember(from:KellyP, to: JerryMaguire, roles: ["Avery Bishop" ]).save(flush:true)
new CastMember(from:JerryO, to: JerryMaguire, roles: ["Frank Cushman" ]).save(flush:true)
new CastMember(from:JayM, to: JerryMaguire, roles: ["Bob Sugar" ]).save(flush:true)
new CastMember(from:BonnieH, to: JerryMaguire, roles: ["Laurel Boyd" ]).save(flush:true)
new CastMember(from:ReginaK, to: JerryMaguire, roles: ["Marcee Tidwell" ]).save(flush:true)
new CastMember(from:JonathanL, to: JerryMaguire, roles: ["Ray Boyd" ]).save(flush:true)
new CastMember(from:CameronC, to: JerryMaguire, type: CastMember.RoleType.PRODUCED).save(flush:true)
new CastMember(from:CameronC, to: JerryMaguire, type: CastMember.RoleType.DIRECTED).save(flush:true)
new CastMember(from:CameronC, to: JerryMaguire, type: CastMember.RoleType.WROTE).save(flush:true)
//Stand By Me
Movie StandByMe= new Movie(title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of.").save(flush:true)
Person RiverP =new Person(name:'River Phoenix', born:1970).save(flush:true)
Person CoreyF =new Person(name:'Corey Feldman', born:1971).save(flush:true)
Person WilW =new Person(name:'Wil Wheaton', born:1972).save(flush:true)
Person JohnC =new Person(name:'John Cusack', born:1966).save(flush:true)
Person MarshallB =new Person(name:'Marshall Bell', born:1942).save(flush:true)
new CastMember(from:WilW, to: StandByMe, roles: ["Gordie Lachance" ]).save(flush:true)
new CastMember(from:RiverP, to: StandByMe, roles: ["Chris Chambers" ]).save(flush:true)
new CastMember(from:JerryO, to: StandByMe, roles: ["Vern Tessio" ]).save(flush:true)
new CastMember(from:CoreyF, to: StandByMe, roles: ["Teddy Duchamp" ]).save(flush:true)
new CastMember(from:JohnC, to: StandByMe, roles: ["Denny Lachance" ]).save(flush:true)
new CastMember(from:KieferS, to: StandByMe, roles: ["Ace Merrill" ]).save(flush:true)
new CastMember(from:MarshallB, to: StandByMe, roles: ["Mr. Lachance" ]).save(flush:true)
new CastMember(from:RobR, to: StandByMe, type: CastMember.RoleType.DIRECTED).save(flush:true)
//As Good as It Gets
Movie AsGoodAsItGets= new Movie(title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.').save(flush:true)
Person HelenH =new Person(name:'Helen Hunt', born:1963).save(flush:true)
Person GregK =new Person(name:'Greg Kinnear', born:1963).save(flush:true)
Person JamesB =new Person(name:'James L. Brooks', born:1940).save(flush:true)
new CastMember(from:JackN, to: AsGoodAsItGets, roles: ["Melvin Udall" ]).save(flush:true)
new CastMember(from:HelenH, to: AsGoodAsItGets, roles: ["Carol Connelly" ]).save(flush:true)
new CastMember(from:GregK, to: AsGoodAsItGets, roles: ["Simon Bishop" ]).save(flush:true)
new CastMember(from:CubaG, to: AsGoodAsItGets, roles: ["Frank Sachs" ]).save(flush:true)
new CastMember(from:JamesB, to: AsGoodAsItGets, type: CastMember.RoleType.DIRECTED).save(flush:true)
}
6.2. 页面
建立neo4j/index.html页面,使用D3进行数据展示
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="http://neo4j-contrib.github.io/developer-resources/language-guides/assets/css/main.css">
<title>Neo4j Movies</title>
</head>
<body>
<div id="graph">
</div>
<div role="navigation" class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="row">
<div class="col-sm-6 col-md-6">
<ul class="nav navbar-nav">
<li>
<form role="search" class="navbar-form" id="search">
<div class="form-group">
<input type="text" value="Matrix" placeholder="Search for Movie Title" class="form-control" name="search">
</div>
<button class="btn btn-default" type="submit">Search</button>
</form>
</li>
</ul>
</div>
<div class="navbar-header col-sm-6 col-md-6">
<div class="logo-well">
<a href="http://neo4j.com/developer-resources">
<img src="http://neo4j-contrib.github.io/developer-resources/language-guides/assets/img/logo-white.svg" alt="Neo4j World's Leading Graph Database" id="logo">
</a>
</div>
<div class="navbar-brand">
<div class="brand">Neo4j Movies</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-5">
<div class="panel panel-default">
<div class="panel-heading">Search Results</div>
<table id="results" class="table table-striped table-hover">
<thead>
<tr>
<th>Movie</th>
<th>Released</th>
<th>Tagline</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="col-md-7">
<div class="panel panel-default">
<div class="panel-heading" id="title">Details</div>
<div class="row">
<div class="col-sm-4 col-md-4">
<img src="" class="well" id="poster"/>
</div>
<div class="col-md-8 col-sm-8">
<h4>Crew</h4>
<ul id="crew">
</ul>
</div>
</div>
</div>
</div>
</div>
<style type="text/css">
.node { stroke: #222; stroke-width: 1.5px; }
.node.actor { fill: #888; }
.node.movie { fill: #BBB; }
.link { stroke: #999; stroke-opacity: .6; stroke-width: 1px; }
</style>
<script type="text/javascript" src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" type="text/javascript"></script>
<script type="text/javascript">
var contextPath = '[[${#httpServletRequest.contextPath}]]';
$(function () {
function showMovie(title) {
$.get(contextPath+"/movie/show?title=" + encodeURIComponent(title),
function (data) {
if (!data) return;
$("#title").text(data.title);
$("#poster").attr("src","http://neo4j-contrib.github.io/developer-resources/language-guides/assets/posters/"+encodeURIComponent(data.title)+".jpg");
var $list = $("#crew").empty();
data.cast.forEach(function (cast) {
$list.append($("<li>" + cast.name + " " +cast.job + (cast.job == "acted"?" as " + cast.role : "") + "</li>"));
});
}, "json");
return false;
}
function search() {
var query=$("#search").find("input[name=search]").val();
$.get(contextPath + "/movie/search?q=" + encodeURIComponent(query),
function (data) {
var t = $("table#results tbody").empty();
if (!data || data.length == 0) return;
data.forEach(function (row) {
var movie = row;
$("<tr><td class='movie'>" + movie.title + "</td><td>" + movie.released + "</td><td>" + movie.tagline + "</td></tr>").appendTo(t)
.click(function() { showMovie($(this).find("td.movie").text());})
});
showMovie(data[0].title);
}, "json");
return false;
}
$("#search").submit(search);
search();
})
</script>
<script type="text/javascript">
var width = 800, height = 800;
var force = d3.layout.force()
.charge(-200).linkDistance(30).size([width, height]);
var svg = d3.select("#graph").append("svg")
.attr("width", "100%").attr("height", "100%")
.attr("pointer-events", "all");
d3.json(contextPath+"/movie/graph", function(error, graph) {
if (error) return;
force.nodes(graph.nodes).links(graph.links).start();
var link = svg.selectAll(".link")
.data(graph.links).enter()
.append("line").attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes).enter()
.append("circle")
.attr("class", function (d) { return "node "+d.label })
.attr("r", 10)
.call(force.drag);
// html title attribute
node.append("title")
.text(function (d) { return d.title; })
// force feed algo ticks
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>
6.3. controller 类
建立Neo4jController类
import org.groovyboot.core.annotation.GbController
@GbController
class Neo4jController {
public void index(){
}
}
建立MovieController类
import org.groovyboot.core.PageParams
import org.groovyboot.core.annotation.GbRestController
import org.groovyboot.example.mongodemo.domain.movie.CastMember
import org.groovyboot.example.mongodemo.domain.movie.Movie
import org.groovyboot.example.mongodemo.service.movie.MovieService
import org.springframework.beans.factory.annotation.Autowired
@GbRestController
class MovieController {
@Autowired MovieService movieService
public Movie show(String title){
Map map=[:]
Movie.withSession {
Movie movie=movieService.find(title)
if(movie){
map.title=movie.title
map.released=movie.released
List list=[]
movie.cast.each{CastMember castMember->
Map one=[:]
one.job = castMember.type.tokenize("_")[0].toLowerCase()
one.name = castMember.from.name
one.role = castMember.roles.toString()
list << one
}
map.cast=list
}
}
return map;
}
public List search(String q,PageParams pageParams){
List result=[]
Movie.withSession {
List list=movieService.search(q,pageParams.max);
list.each {Movie movie->
Map map=[:]
map.title =movie.title
map.released =movie.released
map.tagline =movie.tagline
result << map
}
}
return result
}
public Map<String, Object> graph(PageParams pageParams) {
return movieService.graph(pageParams.max);
}
}
6.4. service 类
建立MovieService类
import org.groovyboot.example.mongodemo.domain.movie.Movie
import org.groovyboot.example.mongodemo.domain.movie.Person
import org.neo4j.driver.internal.InternalStatementResult
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Transactional
@Service
class MovieService {
public Movie find(String title){
return Movie.findByTitle(title)
}
public List<Movie> search(String q, int limit = 100) {
List<Movie> results
if (q) {
results = Movie.where {
title ==~ "%${q}%"
}.list(max:limit)
}else {
results = []
}
results
}
private List<Map<String, Iterable<String>>> findMovieTitlesAndCast(int limit){
return Movie.executeQuery("""MATCH (m:Movie)<-[:ACTED_IN]-(p:Person)
RETURN m.title as movie, collect(p.name) as cast
LIMIT ${limit}""")
}
public Map<String, Object> graph(int limit = 100) {
toD3Format(findMovieTitlesAndCast(limit))
}
private static Map<String, Object> toD3Format(List<Map<String, Iterable<String>>> result) {
List<Map<String,String>> nodes = []
List<Map<String,Object>> rels= []
int i = 0
for (entry in result) {
nodes << [title: entry.movie, label: 'movie']
int target=i
i++
for (String name : (Iterable<String>) entry.cast) {
def actor = [title: name, label: 'actor']
int source = nodes.indexOf(actor)
if (source == -1) {
nodes << actor
source = i++
}
rels << [source: source, target: target]
}
}
return [nodes: nodes, links: rels]
}
}