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不能訪問到的帳戶有哪些,取並集便可。