Neo4j

 6th December 2020 at 10:25pm

Neo4j 是图数据库中比较流行的。我在做一个需求时短暂使用过它;那个需求是要构建一棵公司及其股东的树。

安装

下载 Neo4j Desktop。启动后即会有一个可用的 Neo4j 数据库。

查询语言

Neo4j 所使用的查询语言被称为 Cypher。Cypher 之于 Neo4j,类似于 SQL 之于 MySQL。Cypher 的文档中,每个关键字(CREATE / MERGE / DELETE)等都有丰富的示例给出。参考它来判断如何写。

我在需求中写过一些语句:

查询

MATCH (c:Company) 
WHERE c.credit_code IS NOT NULL OR c.name IS NOT NULL AND c.not_found = false 
RETURN c

c:Companyc 是别名(alias),跟 SQL 中的 AS c 差不多。

创建结点$created_props 是配合 Python SDK 使用的,不是 Cypher 语法本身支持的):

MERGE (c:Company { name: $name }) 
ON CREATE SET c = $created_props 
ON MATCH SET c = $modified_props 
RETURN c

关键的一点是,Cypher 中做创建时往往用 MERGE 而不用 CREATEMERGE 在对象还不存在时创建(ON CREATE),在对象存在时做更新(ON MATCH),比较灵活。

创建关系

MATCH (c:Company { name: $company_name }), 
      (p:Person { id: $person_id }) 
CREATE (c)-[r:IS_CONTROLLED_BY {
      type: $type, att: $att, proportion: $proportion, 
      contribution: $contribution, order: $order 
}]->(p) 
RETURN r

CREATE (c)-[r:IS_CONTROLLED_BY]->(p) 这种语句表示创建一条有向的关系。

创建的这个关系,也可以带一些属性,比如代码中的 type att 等。

Python SDK

安装:

pip install neo4j

代码示例:

from datetime import datetime
import dataclasses

import neo4j
from neo4j import Transaction, GraphDatabase

@dataclasses.dataclass
class Person:
    id: str
    name: str
    created_at: datetime = None
    modified_at: datetime = None


def create_or_update_person_node(tx: Transaction, person: Person):
    now = datetime.now()
    props = dataclasses.asdict(person)
    result = tx.run(
        (
            "MERGE (p:Person { id: $id }) "
            "ON CREATE SET p = $created_props "
            "ON MATCH SET p = $modified_props "
            "RETURN p"
        ),
        id=person.id,
        created_props={**props, 'created_at': now, 'modified_at': now},
        modified_props={**props, 'modified_at': now})
    return result.single()

if __name__ == '__main__':
    driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "passsword"))
    with driver.session() as neo4j_session:
        person = Person(id=1, name="Zhiheng")
        neo4j_session.write_transaction(create_or_update_person_node, person)