neo4j Cypher語句(CQL)練習

CQL雖然看起來挺容易懂,實際上仍是挺難寫的,跟SQL的直觀徹底不能比較,其複雜度的來源多是圖的結構引發的,而非自己語言設計的問題。固然,不能否認,neo4j中CQL的設計仍是略微的有一些不足。本次的練習是基於《Graph Databases(2013)》的第5章,確切說是從p105開始的3個例子。數據庫

這裏提到了3個現實的圖數據庫模型,分別是社交關係模型(Social Networks),權限控制模型(Access Control)和物流模型(Logistics)。具體的模型應用的背景,能夠去參考書中的描述,書能夠去官網下載電子版。網絡

社交關係模型的圖結構以下,ui

權限控制模型的圖結構以下,spa

物流模型的圖結構以下,設計

其中,物流模型中不一樣顏色的邊表示不一樣的時間段,時間段估計是用unix time_t類型記錄的一個長整數,以下圖unix

首先要先創建3個圖數據庫的基本數據,這裏使用CQL中的CREATE便可。rest

社交網絡模型的建圖語句以下code

CREATE 
// User
(uc:User {name : 'Charlie'}), 
(ub:User {name : 'Ben'}), 
(us:User {name : 'Sarah'}), 
(ua:User {name : 'Arnold'}), 
(ue:User {name : 'Emily'}), 
(ug:User {name : 'Gordon'}), 
(uk:User {name : 'Kate'}),
// Company
(ca:Company {name : 'Acme, Inc.'}), 
(cs:Company {name : 'Startup, Ltd.'}),
// Interest
(im:Interest {name : 'Medicine'}), 
(ic:Interest {name : 'Cars'}), 
(ir:Interest {name : 'REST'}), 
(ig:Interest {name : 'Graphs'}), 
(ij:Interest {name : 'Java'}), 
(it:Interest {name : 'Travel'}), 
(id:Interest {name : 'Design'}), 
(ia:Interest {name : 'Art'}), 
(im1:Interest {name : 'Music'}), 
(id1:Interest {name : 'Drama'}),
// Project
(pn:Project {name : 'Next Gen Platform'}), 
(pq:Project {name : 'Quantum Leap'}), 
(pp:Project {name : 'Phoenix'}),
// WORKS_FOR
(uc)-[:WORKS_FOR]->(ca),
(ub)-[:WORKS_FOR]->(ca),
(us)-[:WORKS_FOR]->(ca),
(ua)-[:WORKS_FOR]->(cs),
(ue)-[:WORKS_FOR]->(cs),
(ug)-[:WORKS_FOR]->(cs),
(uk)-[:WORKS_FOR]->(cs),
// WORKED_ON
(uc)-[:WORKED_ON]->(pn),
(ub)-[:WORKED_ON]->(pn),
(us)-[:WORKED_ON]->(pn),
(us)-[:WORKED_ON]->(pq),
(ua)-[:WORKED_ON]->(pp),
(ue)-[:WORKED_ON]->(pn),
(ue)-[:WORKED_ON]->(pq),
(uk)-[:WORKED_ON]->(pq),
(uk)-[:WORKED_ON]->(pp),
// INTERESTED_IN
(uc)-[:INTERESTED_IN]->(im),
(uc)-[:INTERESTED_IN]->(ic),
(uc)-[:INTERESTED_IN]->(ig),
(ub)-[:INTERESTED_IN]->(ir),
(ub)-[:INTERESTED_IN]->(ig),
(us)-[:INTERESTED_IN]->(ir),
(us)-[:INTERESTED_IN]->(ig),
(us)-[:INTERESTED_IN]->(ij),
(ua)-[:INTERESTED_IN]->(ir),
(ua)-[:INTERESTED_IN]->(ig),
(ua)-[:INTERESTED_IN]->(ij),
(ua)-[:INTERESTED_IN]->(it),
(ue)-[:INTERESTED_IN]->(id),
(ue)-[:INTERESTED_IN]->(ia),
(ug)-[:INTERESTED_IN]->(ig),
(ug)-[:INTERESTED_IN]->(im1),
(uk)-[:INTERESTED_IN]->(im1),
(uk)-[:INTERESTED_IN]->(id1)
;

CREATE INDEX ON :User(name);
CREATE INDEX ON :Company(name);
CREATE INDEX ON :Interest(name);
CREATE INDEX ON :Project(name);

權限控制模型的建圖語句以下orm

CREATE 
// Admin
(ab:Admin {name : 'Ben'}), 
(as1:Admin {name : 'Sarah'}), 
(al:Admin {name : 'Liz'}), 
(ap:Admin {name : 'Phil'}), 
// Group
(g1:Group {name : 'Group1'}), 
(g2:Group {name : 'Group2'}),
(g3:Group {name : 'Group3'}),
(g4:Group {name : 'Group4'}),
(g5:Group {name : 'Group5'}),
(g6:Group {name : 'Group6'}),
(g7:Group {name : 'Group7'}),
// Company
(ca:Company {name : 'Acme'}), 
(cs:Company {name : 'Spinoff'}), 
(cs1:Company {name : 'Startup'}), 
(cs2:Company {name : 'Skunk-workz'}), 
(cb:Company {name : 'Big Co'}), 
(ca1:Company {name : 'Aquired Ltd.'}), 
(cs3:Company {name : 'Subsid\'ry'}), 
(co:Company {name : 'One-Man Shop'}), 
(cd:Company {name : 'Dev Shop'}), 
// Employee
(ea:Employee {name : 'Arnold'}), 
(ec:Employee {name : 'Charlie'}), 
(ee:Employee {name : 'Emily'}),
(eg:Employee {name : 'Gordon'}),
(el:Employee {name : 'Lucy'}),
(ek:Employee {name : 'Kate'}),
(ea1:Employee {name : 'Alister'}),
(ee1:Employee {name : 'Eve'}),
(eg1:Employee {name : 'Gary'}),
(eb:Employee {name : 'Bill'}),
(em:Employee {name : 'Mary'}),
// Account
(n1:Account {name : 'Account1'}), 
(n2:Account {name : 'Account2'}), 
(n3:Account {name : 'Account3'}), 
(n4:Account {name : 'Account4'}), 
(n5:Account {name : 'Account5'}), 
(n6:Account {name : 'Account6'}), 
(n7:Account {name : 'Account7'}), 
(n8:Account {name : 'Account8'}), 
(n9:Account {name : 'Account9'}), 
(n10:Account {name : 'Account10'}), 
(n11:Account {name : 'Account11'}), 
(n12:Account {name : 'Account12'}), 
// MEMBER_OF
(ab)-[:MEMBER_OF]->(g1),
(ab)-[:MEMBER_OF]->(g3),
(as1)-[:MEMBER_OF]->(g2),
(as1)-[:MEMBER_OF]->(g3),
(al)-[:MEMBER_OF]->(g4),
(al)-[:MEMBER_OF]->(g5),
(al)-[:MEMBER_OF]->(g6),
(ap)-[:MEMBER_OF]->(g7),
// ALLOWED_INHERIT
(g1)-[:ALLOWED_INHERIT]->(ca),
(g3)-[:ALLOWED_INHERIT]->(cs1),
(g4)-[:ALLOWED_INHERIT]->(cb),
(g7)-[:ALLOWED_INHERIT]->(cs3),
// ALLOWED_DO_NOT_INHERIT
(g2)-[:ALLOWED_DO_NOT_INHERIT]->(ca),
(g6)-[:ALLOWED_DO_NOT_INHERIT]->(co),
// DENIED
(g2)-[:DENIED]->(cs2),
(g5)-[:DENIED]->(ca1),
// CHILD_OF
(cs)-[:CHILD_OF]->(ca),
(cs2)-[:CHILD_OF]->(cs1),
(ca1)-[:CHILD_OF]->(cb),
(cs3)-[:CHILD_OF]->(ca1),
(co)-[:CHILD_OF]->(cs3),
(cd)-[:CHILD_OF]->(cs3),
// WORKS_FOR
(ea)-[:WORKS_FOR]->(ca),
(ec)-[:WORKS_FOR]->(ca),
(ee)-[:WORKS_FOR]->(cs),
(eg)-[:WORKS_FOR]->(cs1),
(el)-[:WORKS_FOR]->(cs1),
(ek)-[:WORKS_FOR]->(cs2),
(ea1)-[:WORKS_FOR]->(cb),
(ee1)-[:WORKS_FOR]->(ca1),
(eg1)-[:WORKS_FOR]->(cs3),
(eb)-[:WORKS_FOR]->(co),
(em)-[:WORKS_FOR]->(cd),
// HAS_ACCOUNT
(ea)-[:HAS_ACCOUNT]->(n1),
(ea)-[:HAS_ACCOUNT]->(n2),
(ec)-[:HAS_ACCOUNT]->(n3),
(eg)-[:HAS_ACCOUNT]->(n4),
(el)-[:HAS_ACCOUNT]->(n5),
(ee)-[:HAS_ACCOUNT]->(n6),
(ek)-[:HAS_ACCOUNT]->(n7),
(ea1)-[:HAS_ACCOUNT]->(n8),
(ee1)-[:HAS_ACCOUNT]->(n9),
(eb)-[:HAS_ACCOUNT]->(n10),
(eg1)-[:HAS_ACCOUNT]->(n11),
(em)-[:HAS_ACCOUNT]->(n12)
; 

物流模型的建圖語句以下,blog

CREATE 
// Parcel Center
(pc1:Center {name : 'Parcel Center-1'}), 
(pc2:Center {name : 'Parcel Center-2'}), 
// Delivery Base
(db1:Base {name : 'Delivery Base1'}), 
(db2:Base {name : 'Delivery Base2'}),
(db3:Base {name : 'Delivery Base3'}),
// Delivery Area
(da1:Area {name : 'Delivery Area1'}), 
(da2:Area {name : 'Delivery Area2'}),
(da3:Area {name : 'Delivery Area3'}),
(da4:Area {name : 'Delivery Area4'}), 
// Delivery Segment
(ds1:Segment {name : 'Delivery Segment1'}), 
(ds2:Segment {name : 'Delivery Segment2'}),
(ds3:Segment {name : 'Delivery Segment3'}),
(ds4:Segment {name : 'Delivery Segment4'}), 
(ds5:Segment {name : 'Delivery Segment5'}), 
(ds6:Segment {name : 'Delivery Segment6'}),
(ds7:Segment {name : 'Delivery Segment7'}),
(ds8:Segment {name : 'Delivery Segment8'}), 
// CONNECTED_TO
(pc1)-[:CONNECTED_TO {cost:3, start_date:1350255600000, end_date:1350860400000}]->(db1),
(pc1)-[:CONNECTED_TO {cost:2, start_date:1350860400000, end_date:1351465200000}]->(db1),
(pc1)-[:CONNECTED_TO {cost:6, start_date:1351465200000, end_date:1352070000000}]->(db1),

(pc1)-[:CONNECTED_TO {cost:3, start_date:1350255600000, end_date:1350860400000}]->(db2),
(pc1)-[:CONNECTED_TO {cost:2, start_date:1350860400000, end_date:1351465200000}]->(db2),
(pc1)-[:CONNECTED_TO {cost:6, start_date:1351465200000, end_date:1352070000000}]->(db2),

(pc1)-[:CONNECTED_TO {cost:6, start_date:1351465200000, end_date:1352070000000}]->(db3),

(pc2)-[:CONNECTED_TO {cost:5, start_date:1350860400000, end_date:1351465200000}]->(db1),

(pc2)-[:CONNECTED_TO {cost:3, start_date:1350255600000, end_date:1350860400000}]->(db2),
(pc2)-[:CONNECTED_TO {cost:2, start_date:1350860400000, end_date:1351465200000}]->(db2),
(pc2)-[:CONNECTED_TO {cost:6, start_date:1351465200000, end_date:1352070000000}]->(db2),

(pc2)-[:CONNECTED_TO {cost:3, start_date:1350255600000, end_date:1350860400000}]->(db3),
(pc2)-[:CONNECTED_TO {cost:2, start_date:1350860400000, end_date:1351465200000}]->(db3),

// DELIVERY_ROUTE
(db1)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(da1),
(db1)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(da1),
(db1)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(da1),
(db1)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(da4),

(db2)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(da4),
(db2)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(da4),
(db2)-[:DELIVERY_ROUTE {cost:5, start_date:1350255600000, end_date:1350860400000}]->(da3),
(db2)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(da2),

(db3)-[:DELIVERY_ROUTE {cost:5, start_date:1350860400000, end_date:1351465200000}]->(da3),
(db3)-[:DELIVERY_ROUTE {cost:5, start_date:1351465200000, end_date:1352070000000}]->(da3),
(db3)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(da2),
(db3)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(da2),

(da1)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds1),
(da1)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds1),
(da1)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds2),
(da1)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds2),
(da1)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds7),
(da1)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds8),

(da4)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds1),
(da4)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds2),
(da4)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds7),
(da4)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds7),
(da4)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds8),
(da4)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds8),
(da4)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds5),

(da3)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds5),
(da3)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds5),
(da3)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds6),
(da3)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds6),
(da3)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds6),
(da3)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds3),
(da3)-[:DELIVERY_ROUTE {cost:6, start_date:1351465200000, end_date:1352070000000}]->(ds4),

(da2)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds3),
(da2)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds3),
(da2)-[:DELIVERY_ROUTE {cost:3, start_date:1350255600000, end_date:1350860400000}]->(ds4),
(da2)-[:DELIVERY_ROUTE {cost:2, start_date:1350860400000, end_date:1351465200000}]->(ds4)
; 

以上建圖語句中,只有社交網絡模型(第一個)使用了索引,其餘的沒有加進去。neo4j的索引有一些坑,我用的是2.0.2版本,其中提供了2種索引,一種叫作index,一種叫作legacy index。兩種的差別並非一個取代另外一個,而是有特定場景的。index能夠用於match語句中,可是start語句中只能夠支持legacy index,文檔中有一段很不起眼的話「In general, the START clause is only really needed when using legacy indexes」。還有,index能夠很方便的經過create index建立,可是legacy index就悲劇了,從文檔的例子來看,只能經過寫Java代碼來建立。創建index應該是屬於DDL語句,須要由DBA來操做,這樣看來,index應該是neo4j須要增強的地方,把legacy index的不少特性逐步的遷移過來。

例子1,在社交網絡模型場景中,列出全部對Java感興趣的人名

MATCH (n:User), (i:Interest)
WHERE (n)-[:INTERESTED_IN]-> (i)
AND   i.name = 'Java'
RETURN n.name;

結果是Sarah,Arnold。

例子2,在權限控制模型中,列出全部Sarah無權管理的用戶(人名)

MATCH paths=(admin:Admin)-[:MEMBER_OF]->()-[:DENIED]->()<-[:CHILD_OF*0..3]-(company)
      <-[:WORKS_FOR]-(employee)
WHERE admin.name = 'Sarah'
RETURN employee.name
UNION
MATCH (admin:Admin),paths=(company:Company)<-[:WORKS_FOR]-(employee:Employee)
where (NOT ((admin)-[:MEMBER_OF]->()-[:ALLOWED_INHERIT]->()
<-[:CHILD_OF*0..3]-(company)))
and   (NOT ((admin)-[:MEMBER_OF]->()-[:ALLOWED_DO_NOT_INHERIT]->(company)))
and   admin.name = 'Sarah'
RETURN distinct employee.name;

結果有7我的,不列舉了。這個CQL的寫法是,先列出Sarah被Denied的帳戶有哪些,而後還有Sarah不能訪問到的帳戶有哪些,取並集便可。

相關文章
相關標籤/搜索