• neo4j Cypher语句(CQL)练习


    CQL虽然看起来挺容易懂,实际上还是挺难写的,跟SQL的直观完全不能比较,其复杂度的来源可能是图的结构引起的,而非本身语言设计的问题。当然,不可否认,neo4j中CQL的设计还是略微的有一些不足。本次的练习是基于《Graph Databases(2013)》的第5章,确切说是从p105开始的3个例子。

    这里提到了3个现实的图数据库模型,分别是社交关系模型(Social Networks),权限控制模型(Access Control)和物流模型(Logistics)。具体的模型应用的背景,可以去参考书中的描述,书可以去官网下载电子版。

    社交关系模型的图结构如下,

    权限控制模型的图结构如下,

    物流模型的图结构如下,

    其中,物流模型中不同颜色的边表示不同的时间段,时间段估计是用unix time_t类型记录的一个长整数,如下图

    首先要先建立3个图数据库的基本数据,这里使用CQL中的CREATE即可。

    社交网络模型的建图语句如下

    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);

    权限控制模型的建图语句如下

    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)
    ; 

    物流模型的建图语句如下,

    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不能访问到的账户有哪些,取并集即可。

  • 相关阅读:
    搜索
    c++ map与unordered_map区别及使用
    01BFS
    宇智波程序笔记55-Flutter 混合开发】嵌入原生View-iOS
    宇智波程序笔记54-解Bug之路-记一次线上请求偶尔变慢的排查
    宇智波程序笔记53-从红黑树的本质出发,彻底理解红黑树!
    宇智波程序笔记52-最受欢迎的微服务框架概览
    宇智波程序笔记51-JDK 15安装及新特性介绍
    宇智波程序笔记50-解Bug之路-记一次线上请求偶尔变慢的排查
    宇智波程序笔记49-link JDBC Connector:Flink 与数据库集成最佳实践
  • 原文地址:https://www.cnblogs.com/valleylord/p/3693306.html
Copyright © 2020-2023  润新知