我的neo4j学习笔记

时间:2023-01-30 18:03:44

这篇博文目录如下:
- About neo4j
- Install neo4j
- learn to create and query data
- Import your data
包括对Neo4j图形数据库的简介和其重要组成部分、内部结构的讲解,对其查询语言cypher的介绍和实例。通过实例转换、比较传统关系型数据库与图形数据库。希望有时间下一次可以来总结一下用java实现neo4j的学习过程(* ~ *)

About neo4j

Neo4j是一个高性能的,Nosql图形数据库。Nosql =no sql,即与传统的将数据结构化并存储在表中的数据库不一样。Neo4j将数据存储在网络上,我们也可以把Neo4j视为一个图引擎。我们打交道的是一个面对对象的、灵活的网络结构而不是严格的、静态的表。传统关系型数据库,当数据量很大时,查询性能会明显受影响,尤其是一度以上的查询。但是图形数据库却在这方面表现得很好。
neo4j 支持多种主流编程语言,包括.Net、Java、JavaScript、Python

Install Neo4j

从官网这里下载neo4j,解压,然后进入bin文件夹,开启数据库

~$ path/to/your/neo4j/file
~$ cd bin
~$./neo4j console

开启后我们在浏览器*问 localhost:7474,就可以本地运行

Learn to create and query data

既然Neo4j是数据库,则必然有它自己的查询语言。Cypher是一种面向图形的申明式语言。它由分句、关键词和一些表示判断、代表函数的语句组成。比如:WHERE,ORDER BY,SKIP LIMIT ,p.unitPrice > 10。与SQL不同的是,cypher是对于图形模式的表达。我们总是先用MATCH去匹配我们要操作的数据。

  • cypher: cypher是一种申明式查询语言。可以分解为下面几个部分:
    • nodes
      • 我们通常用圆括号来包含节点标示符(当然也可以不用)。节点标签也会一并注明。比如(p: Person),如果想添加属性:(p: Person {name: "yinglish"})。 注意:节点标签、关系类型和属性名称区分大小写。
    • relationships
      • 我们通常用箭头 –> 表示关系,没有尖箭头的如 – 指不强调方向关系。例子:
      • (a)-[:KNOWS|:LIKE]->(b) a到b是“相识”的关系或者”喜欢“
      • (a)-[rel:KNOWS]->(b) 关系的变量名为rel,类型是 KNOWS
      • -[{since: 2016}]-> 添加了额外的属性
      • -[:KNOWS*..4]->
        example: a -[rel: type*minHop..maxHop]-> b

        可变数量关系:从a节点到b节点最少可经过minHop、最大可经过maxHop个节点
    • patterns 模式由一个或多个路径组成,路径间用逗号分隔,一条路径就是一个节点和关系的序列,这个序列指定开始和结束节点,如:(a) –> (b),这条路径开始于a,通过outgoing关系指向b。如果你不在乎具体是哪个节点,不用指定它,只需要用空括号来表示就可以了,如:a –>()–>b。模式是cypher中的关键部分,灵活书写模式能帮我们正确获取、实现推测计算等等各种任务。

节点和关系是图数据库的基石,每个节点都可以有属性,每个节点每条关系都可以有标签。因此neo4j也被称为Property graph,看个图也许会更好理解:(当然了,属性和标签都是可选的,也可以没有)
我的neo4j学习笔记

cypher是学习neo4j的重中之重,它有很多关键词,很多灵活的搭配与使用,在理解neo4j的最基本要素:节点,关系,模式,标签,属性的概念及其相互间的组成关系后,对查询语句cypher的学习就是下一站要攻克的堡垒。但是在这篇博文不详细接受,可以通过这份官方的教程细细学习。

下面我们来看一个简单但足以说明情况的应用实例:
Create a recode for youself
CREATE (you: Person {name: "You"})
RETURN you

And add a properties to this note
MATCH (you: Person {name: "you"})
CREATE (you) -[like:LIKE]->(neo:Database {name: "Neo4j"})
RETURN you, like, neo

Create your friends
MATCH (you: Person {name: "you"})
FOREACH (name in ["Johan", Rajesh", "Anna", "Julia", "Andrew"])
CREATE (you) -[:FRIED]->(: Person {name: name}))

Create second degree friends and expertise
MATCH (neo: Database {name: "Neo4j"})
MATCH (anna: Person {name: "Anna"})
CREATE (anna) -[:FRIEND]->(:Person:Expert {name:" Amanda"}) -[:WORKED_WITH]->(neo)

现在我们创造了一个关系图如下:
我的neo4j学习笔记
Find someone in your network who can help you learn neo4j
MATCH (you {name: "You"})
MATCH (expert) -[:WORKED_WITH]->(db: Database {name:"Neo4j"})
MATCH path = shortestPath ((you)-[: FRIEND*..5]-(expert))
RETURN db.expert, path

更加细致详细的学习资料:
neo4j Cypher Refcard (巨详细的官方手册)
官网教程
他人博客
他人博客

Import your data

本小节讲解如何从传统数据库的数据集PostgreSQL(RDBMS)导出数据到Neo4j(GraphDB),使之成图。从中可以看到一些这两种数据库的差别。
RDBMS数据集
用的数据集是NorthWind dataset(可点击下载),该数据库的E-R图如下:
我的neo4j学习笔记
构建图模型
将一个E-R模型转换成图模型时注意:
1、一行数据表示一个节点
2、一个表名对应一个Label名
NorthWind dataset表示成图模型的一个局部示意图如下:
我的neo4j学习笔记

那么我们的图模型和关系模型的差别在哪里呢?
-  前者没有空节点。在关系数据库中,如果一个雇员不需要向谁报告,他在“ReportsTo”这一列的属性为空,而在图模型中,我们只是不需要为他添加一条指向边而已。
-  能够更详细地描述关系。比如说,我们能够直观地了解到一个雇员售卖一件产品,而不必通过外链在雇员表和产品表中查找。边上也可以添加元数据。
-  前者对于描述网络关系更规范。

将数据导出成csv
导出数据主要是 COPY语句,比如这样:COPY (SELECT * FROM customers) TO '/tmp/customer.csv' WITH CSV header; 把customers表中的数据导出
用Cypher导入数据
我们把数据从PostgreSQL中导出后,用LOAD CSV把CSV文件的内容转为图数据
First, create the nodes

//Create periodic commit
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:customers.csv" AS row
CREATE (:Customer {companyName: row.CompanyName, customerID: row.CustomerID, fax: row.Fax, phone: row.Phone});


//Create products
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:products.csv" AS row
CREATE (:Product {productName: row.ProductName, productID: row.ProductID, unitPrice: toFloat(row.UnitPrice)});


// Create suppliers
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:suppliers.csv" AS row
CREATE (:Supplier {companyName: row.CompanyName, supplierID: row.SupplierID});


// Create employees
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:employees.csv" AS row
CREATE (:Employee {employeeID:row.EmployeeID, firstName: row.FirstName, lastName: row.LastName, title: row.Title});


// Create categories
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:categories.csv" AS row
CREATE (:Category {categoryID: row.CategoryID, categoryName: row.CategoryName, description: row.Description});


USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:orders.csv" AS row
MERGE (order:Order {orderID: row.OrderID}) ON CREATE SET order.shipName = row.ShipName;

create indexes
接下来在我们刚刚创建的节点上添加索引,以确保下一步添加关系时我们可以快速地找到节点

CREATE INDEX ON :Product(productID);
CREATE INDEX ON :Product(productName);
CREATE INDEX ON :Category(categoryID);
CREATE INDEX ON :Employee(employeeID);
CREATE INDEX ON :Supplier(supplierID);
CREATE INDEX ON :Customer(customerID);
CREATE INDEX ON :Customer(customerName);
CREATE CONSTRAINT ON (o:Order) ASSERT o.orderID IS UNIQUE;

create relationships among orders, products and employees

USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:orders.csv" AS row
MATCH (order:Order {orderID: row.OrderID})
MATCH (product:Product {productID: row.ProductID})
MERGE (order)-[pu:PRODUCT]->(product)
ON CREATE SET pu.unitPrice = toFloat(row.UnitPrice), pu.quantity = toFloat(row.Quantity);



USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:orders.csv" AS row
MATCH (order:Order {orderID: row.OrderID})
MATCH (employee:Employee {employeeID: row.EmployeeID})
MERGE (employee)-[:SOLD]->(order);


USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:orders.csv" AS row
MATCH (order:Order {orderID: row.OrderID})
MATCH (customer:Customer {customerID: row.CustomerID})
MERGE (customer)-[:PURCHASED]->(order);

create relationships among products, suppliers and categories

USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:products.csv" AS row
MATCH (product:Product {productID: row.ProductID})
MATCH (supplier:Supplier {supplierID: row.SupplierID})
MERGE (supplier)-[:SUPPLIES]->(product);


USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:products.csv" AS row
MATCH (product:Product {productID: row.ProductID})
MATCH (category:Category {categoryID: row.CategoryID})
MERGE (product)-[:PART_OF]->(category);

create “REPORTS_TO”
用REPORTS_TO来表示员工之间的报告关系

USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:employees.csv" AS row
MATCH (employee:Employee {employeeID: row.EmployeeID})
MATCH (manager:Employee {employeeID: row.ReportsTo})
MERGE (employee)-[:REPORTS_TO]->(manager);

最后图形如下:
我的neo4j学习笔记
在这个图上我们来尝试使用一个查询语句吧~~
员工之间是怎么报告的呢?

MATCH path = (e:Employee)<-[:REPORTS_TO]-(sub)
RETURN e.employeeID AS manager, sub.employeeID AS employee;

返回:


manager employee
2 1
2 3
2 4
2 5
2 8
5 6
5 7
5 9

可见雇员6,7,9向雇员5汇报工作,雇员5同雇员1,3,4,8一起向雇员2汇报。当你熟悉cypher语句后你就可以自己这样玩啦!~你会发现cypher的强大与灵活哒:)