neo4j

 

本文简介

本文简介

通过 desktop 创建一个新的项目 wilson,它会要求输入密码,并会自动创建一个 neo4j 数据库

neo4j

启动这个数据库之后,使用 python API 进行连接

连接方法:

# Import the neo4j dependency
from neo4j import GraphDatabase

# Create a new Driver instance
driver = GraphDatabase.driver("neo4j://localhost:7687",
    auth=("neo4j", "al*****0"))

# Verify the connection details
driver.verify_connectivity()

对于连接参数

connect

  • neo4j:未加密连接,neo4j+s:加密连接
  • blot:用于直接连接到单个 DBMS (在集群环境或独立环境中)。如果您只配置了一台用于数据科学或分析的服务器,那么这将非常有用。

增加一个驱动 Driver

  • neo4j 的 python 驱动就是 pip install neo4j 提供
  • 创建驱动实例
    • neo4j 的 GraphDatabase 创建驱动实例

      # Import the neo4j dependency
      from neo4j import GraphDatabase
      
      # Create a new Driver instance
      driver = GraphDatabase.driver("neo4j://localhost:7687",
          auth=("neo4j", "neo"))
          
      driver.verify_connectivity()
      

session 和 transaction 事务

  1. 创建一个会话
  with driver.session(database="people") as session:

一个会话可以有多个和数据库的 socket 连接

  1. 事务 transaction

事物是原子的、一致的、隔离和持久的。驱动公开了三种类型的事务:

  • 自动提交的事务 auto-commit
  • read
  • write
    1. auto-commit transaction
  • 一个单独的工作单元,可以针对 DBMS 立即执行并立即得到确认
  • 可通过 session 上的 run() 进行连接

    session.run(
        "MATCH (p:Person {name: $name}) RETURN p", # Query
        name="Tom Hanks" # Named parameters referenced
    )                    # in Cypher by prefixing with a $
    
        4. read transaction
    
  • session 有 execute_read() 方法

    session.execute_read(get_movies, title="Arthur")
    
    def get_movies(tx, title):
        return tx.run("""
                      MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
                      WHERE m.title = $title // (1)
                      RETURN p.name AS name
                      LIMIT 10
                      """, title=title)
    
        5. write transaction
    
  session.execute_write(create_person, name="Michael")

  def create_person(tx, name):
      return tx.run( "CREATE (p:Person {name: $name})", name=name)
  1. 手动创建事务 transaction
    • 通过 session 上的 start_action 创建事务对象
     with session.begin_transaction() as tx:
    
         - 手动创建的事务,要手动进行commit或roll_back回溯,而 `execute_read/write` 不用
    
     try:
         # Run a query
         tx.run(query, **params)
    
         # Commit the transaction
         tx.commit()
     except:
         # If something goes wrong in the try block,
         # then rollback the transaction
         tx.rollback()
    
  2. 关闭会话
  session.close()

事务处理结果

# Unit of work
def get_actors(tx, movie): # (1)
    result = tx.run("""
        MATCH (p:Person)-[:ACTED_IN]->(:Movie {title: $title})
        RETURN p
    """, title=movie)

    # Access the `p` value from each record
    return [ record["p"] for record in result ]

# Open a Session
with driver.session() as session:
    # Run the unit of work within a Read Transaction
    actors = session.execute_read(get_actors, movie="The Green Mile") # (2)

    for record in actors:
        print(record["p"])

    session.close()
  • get_actors 是在事务中执行的工作单元
  • actors 是一个迭代器,可以用 peek() 方法检查

    peek = result.peek()
    print(peek)
    
  • 获取结果中每条记录,可以用 result.keys()
  • 只需要一要记录,可以用:result.single()
  • 只需要提取单个值:result.value()

    def get_actors_values(tx, movie):
        result = tx.run("""
            MATCH (p:Person)-[r:ACTED_IN]->(m:Movie {title: $title})
            RETURN p.name AS name, m.title AS title, r.roles AS roles
        """, title=movie)
    
        return result.value("name", False)
        # Returns the `name` value, or False if unavailable
    
  • 提取多个条目,用 result.values()

    def get_actors_values(tx, movie):
        result = tx.run("""
            MATCH (p:Person)-[r:ACTED_IN]->(m:Movie {title: $title})
            RETURN p.name AS name, m.title AS title, r.roles AS roles
        """, title=movie)
    
        return result.values("name", "title", "roles")
    
  • 消耗 consume() 把结果剩余部分返回到 ResultSummary
    • “结果摘要”包含有关服务器、查询、执行时间和计数器对象的信息,计数器对象提供有关查询的统计信息。

      def get_actors_consume(tx, name):
          result = tx.run("""
              MERGE (p:Person {name: $name})
              RETURN p
          """, name=name)
      
          info = result.consume()
      
      # The time it took for the server to have the result available. (milliseconds)
      print(info.result_available_after)
      
      # The time it took for the server to consume the result. (milliseconds)
      print(info.result_consumed_after)
      print("{0} nodes created".format(info.counters.nodes_created))
      print("{0} properties set".format(info.counters.properties_set))
      

neo4j 的类型系统

node & relationships

result = tx.run("""
MATCH path = (person:Person)-[actedIn:ACTED_IN]->(movie:Movie {title: $title})
RETURN path, person, actedIn, movie
""", title=movie)

查询为每个 :Persion:Movie 节点返回一条记录,它们之间的关系是 :ACTED_IN

节点 Nodes

  • 从结果中检索出 movie 的值

    for record in result:
      node = record["movie"]
    
      print(node.id)              # (1)
      print(node.labels)          # (2)
      print(node.items())         # (3)
    
      # (4)
      print(node["name"])
      print(node.get("name", "N/A"))
    
  • 分配给 python 节点变量 node 的值是 neo4j 的 Node 类型的实例
  • id 对应 Node 的ID
  • labels 属性是一个冻结集,包含属于 Node 的标签数组:['Persion', 'Actor']
  • items() 方法,节点 node 的属性
  • get() 检索单个属性

关系

acted_in = record["actedIn"]

print(acted_in.id)         # (1)
print(acted_in.type)       # (2)
print(acted_in.items())    # (3)

# 4
print(acted_in["roles"])
print(acted_in.get("roles", "(Unknown)"))

print(acted_in.start_node) # (5)
print(acted_in.end_node)   # (6)
  • type:保存关系类型,如:ACTED_IN
  • start_node:表示关系开始处节点的内部 ID 的整数
  • end_node:关系结束时节点的内部 ID

路径 Paths

如果返回 node 和 relationship 的 path,它们将作为 Path 的实例被返回

path = record["path"]

print(path.start_node)  # (1)
print(path.end_node)    # (2)
print(len(path))  # (1)
print(path.relationships)  # (1)
  • len(path):路径中关系的计数
  • relationships:路径中关系对象数组

路径段 Path Segments

  • 路径被分割成代表路径中每个关系的段
  • 比如路径 (p:Person)-[:ACTED_IN]→(m:Movie)-[:IN_GENRE]→(g:Genre)
    • (p:Person)-[:ACTED_IN]→(m:Movie)
    • (p:Person)-[:ACTED_IN]→(m:Movie)
  • 路径中的关系可以用 iter() 来迭代

    for rel in iter(path):
        print(rel.type)
        print(rel.start_node)
        print(rel.end_node)
    

Cypher 基础内容

从 neo4j 读取数据

本内容的领域模式

domain

Cypher 是为图设计的查询语言。

  • 节点由()表示
  • :表示标签,如(:Person)
  • 关系由破折号表示:(:Person)--(:Movie)
  • 关系的方向由箭头表示:(:Person)-->(:Movie)
  • 关系的类型由方括号表示:[:ACTED_IN]
  • 属性于字典表示:{name: 'Tom Hanks'}
  • Cypher 模式

    (m:MOvie {title: ‘Cloud Atlas'})<-[:ACTED_IN]-(p:Person)
    
    • 两个节点类型是:MoviePerson
    • 定向 ACTED_IN 关系
    • 特定的 Movie 节点可以通过 title 属性筛选

Cypher 如何工作的

  • Cypher 通过匹配数据中的模式来工作
  • 使用 MATCH 检索数据
  • 从图中找一个 Person 节点,我们可以用 MATCH 配合一个带有标签 :Person 的节点来检索

    MATCH (:Person)
    
  • 想检索所有的 Person 节点,在 : 前分配一个变量,由它表示所有的 Person 节点

    MATCH (p:Person)
    RETURN p
    
  • 找到特定属性的节点,用 {} 的内联方法指定要匹配的属性值

    MATCH (p:Person {name: 'Tom Hanks'})
    RETURN p
    
  • 在 Cypher 中,可以用 . 访问节点的属性,比如只返回 name 属性值

    MATCH (p: Person {name: 'Tom Hanks'})
    RETURN p.born
    
  • Cypher 关键字不分大小写,但标签、属性键、变量区分
  • 使用 where 筛选

    match (p:Person)
    where p.name = 'Tom Hanks' or p.name = 'Rita Wilson'
    return p.name, p.born
    

找关系

  • 可以扩展匹配模式,遍历有 ACTED_IN 关系类型的任意节点,

      match (p:Person {name: 'Tom Hanks'})-[:ACTED_IN]->()
    
    • 数据模型可以找到指向的节点是 (m:Moive) 所以我们可以用

        match (p:Person {name: 'Tom Hanks'})-[:ACTED_IN]->(m)
        return m.title
      
    • 这就返回了 Tom Hanks 演的电影标题

过滤查询

  • 查询2008和2009年的电影

      match (p:Person)-[:ACTED_IN]->(m:Moive)
      where m.released=2009 or m.released=2008
      return p,m
    
  • 返回《黑客帝国》演员的名字

      match (p:Person)-[:ACTED_IN]->(m:Moive)
      where m.title='The Matrix'
      return p.name
    

    也可以

      match (p)-[:ACTED_IN]->(m)
      where p:Person and m:Moive and m.title='The Matrix'
      return p.name
    
  • 范围匹配

      match (p:Person)-[:ACTED_IN]->(m:Movie)
      where 2000 <= m.released<= 2009
      return p.name
    
  • 根据属性存在筛选

      match (p:Person)-[:ACTED_IN]->(m:Movie)
      where p.name='Jack Nocholson' and m.tagline is not null
      return p.name
    
  • 字符串筛选

      match (p:Person)-[:ACTED_IN]->(m:Movie)
      where p.name starts with 'Michael'
      return p.name
    
    • 可以是starts withends withcontains
    • 转小写toLower()toUpper()

        match (p:Person)-[:ACTED_IN]->(m:Movie)
        where toLower(p.name) starts with 'michael'
        return p.name
      
  • 根据图的模式过滤:找到写了电影但是没有导演电影的人

      match (p:Person)-[:WROTE]->(m:Moive)
      where not exists( (p)-[:DIRECTED]->(m) )
      return p.name, m.titile
    
  • 使用列表筛选,列表是同类型元素

      match (p:Person)-[:WROTE]->(m:Moive)
      where p.born in [1995, 1967, 1945]
      return p.name, m.titile
    
  • 把值和图中现有列表比较

      match (p:Person)-[r:ACTED_IN]->(m:Moive)
      where 'Neo' in r.roles and m.title='The Matrix'
      return p.name, m.titile
    

节点或关系有哪些属性?

  • 发现节点的属性

      match (p:Person)
      return p.name, keys(p)
    

    keys()函数返回节点属性的列表

  • 发现图中的所有属性

      call db.propertyKeys()
    

创建节点

  • 我们使用 MERGE 关键字在数据库中创建一个模式
    • 在 MERGE 关键字之后,我们指定要创建的模式。通常这将是一个单一的节点或两个节点之间的关系。
    • 创建一个代表 Michael Cain 的节点

      ```
      merge (p:Person {name: 'Michael Cain'})
      ``` - 请注意,在使用 MERGE 创建节点时,必须至少指定一个属性作为该节点的唯一主键
      
  • 我们还可以将多个 MERGE 子句链接在一个 Cypher 代码块中

      merge (p:Person {name: 'Kaite Holems'})
      merge (m:Movie {title: 'The Dark Kngiht'})
      return p, m
    
    • 创建两个节点,每个节点具有一个主键属性
  • 使用 create
    • 使用 CREATE 的好处是在添加节点之前不会查找主键。如果您确信数据是干净的,并且希望在导入期间获得更快的速度,则可以使用 CREATE
    • 我们使用 MERGE,因为它消除了节点的重复

创建关系

  • 两个节点间的关系必须有:类型和方向
    • 先找到两个已知的节点,再创建他们的关系

      ```
      match (p:Person {name: 'Michael Cain'})
      match (m:Movie {titile: 'The Dark Kngiht'})
      merge (p)-[:ACTED_IN]->(m)
      ```
      - 我们创建了两个节点间的 `ACTED_IN` 关系
      - 通过 match 确认关系存在
      
          ```
          match (p:Person {name: 'Michael Cain'})-[r:ACTED_IN]-(m:Movie {titile: 'The Dark Kngiht'})
          return p, m
          ```
      
          - 在 `match` 中可以不指定方向
          - 如果方向指错了,不会返回任何结果
      
  • 连续使用多个merge子句

      MERGE (p:Person {name: 'Chadwick Boseman'})
      MERGE (m:Movie {title: 'Black Panther'})
      MERGE (p)-[:ACTED_IN]-(m)
    
    • 关系方向默认从左到右
  • 使用单子句同时创建节点和关系

      MERGE (p:Person {name: 'Emily Blunt'})-[:ACTED_IN]->(m:Movie {title: 'A Quiet Place'})
      RETURN p, m
    

更新属性

  • 设置关系的内联属性

      MERGE (p:Person {name: 'Michael Cain'})
      MERGE (m:Movie {title: 'Batman Begins'})
      MERGE (p)-[:ACTED_IN {roles: ['Alfred Penny']}]->(m)
      return m, p
    
  • 使用 set 关键字

      match (p:Person)-[r:ACTED_IN]->(m:Movie)
      where p.name='Michael Cain' and m.title='The Dark Kngiht'
      set r.roles = ['Alfred Penny'], r.year = 2008
      return p, r, m
    
    • 设置多个属性用 , 分隔
    • 更新也同样使用 set
  • 删除属性用 remove 或者 set xxx = null

      match (p:Person)-[r:ACTED_IN]->(m:Movie)
      where p.name='Michael Cain' and m.title='The Dark Kngiht'
      remove r.roles
      return p, m
    
    • 或者使用 set null

        match (p:Person)
        set p.roles = null
        return p
      

merge 的处理

  • 我们可以在创建节点和找到节点时,设置属性

      merge (p:Person {name: 'Michael Cain'})
    
      on create set p.createAt = datetime(), m.released=2008
      on match set p.updateAt = datetime()
    
      set p.born = 2006
    
      return p
    
    • 当 merge 创建节点时,设置 createAt 属性
    • 当 merge 找到节点时,设置 updataAt 属性
    • 无论怎样,都设置 born 属性
    • merge 实际上是 find 和 create 的合并操作

删除

  • neo4j 中可以删除的元素有:节点、关系、标签、属性
  • 删除孤立的节点

      match (p:Person)
      where p.name = 'Michael Cain'
      delete p
    
  • 删除关系

      match (p:Person {name: 'Jane Doe'})-[r: ACTED_IN]->(m:Movie {title: 'The Matrix'})
      delete r
      return p, m
    
  • 删除有关系的节点

      match (p:Person {name: 'Jane Doe'})
      detach delete p
    
    • 删除了它的关系和节点
  • 清除图中所有节点

      match (n)
      detach delete n
    
  • 删除标签

      match (p:Person {name: 'Jane Doe'})
      set p:Developer
      return p
    
    • 这里节点变成 (p:person :Developer {name: 'Jane Doe'})
    • 可以用 remove 删除标签

        match (p:Person {name: 'Jane Doe'})
        remove p:Developer
        return p
      
  • 图中存在哪些标签

      call db.labels()