GraphGists

英国公司数据

本 Neo4j 浏览器指南将引导您完成导入和查询英国公司注册、土地所有权和政治捐赠数据的过程。我们将在此过程中学习 Cypher!

在完成示例时,请务必参考Cypher 参考卡

提纲

  • 数据导入

  • 使用 MATCH 进行查询

  • 使用关系

  • 模糊匹配

  • 处理数字

  • 日期

  • 位置数据

  • 图算法

ukcompanies model

导入

请确保在 Neo4j 浏览器中启用多语句查询编辑器,然后运行

CREATE CONSTRAINT ON (c:Company) ASSERT c.companyNumber IS UNIQUE;
//Constraint for a node key is a Neo4j Enterprise feature only - run on an instance with enterprise
//CREATE CONSTRAINT ON (p:Person) ASSERT (p.birthMonth, p.birthYear, p.name) IS NODE KEY
CREATE CONSTRAINT ON (p:Property) ASSERT p.titleNumber IS UNIQUE;
LOAD CSV WITH HEADERS FROM "https://guides.neo4j.com/ukcompanies/data/PSCAmericans.csv" AS row
MERGE (c:Company {companyNumber: row.company_number})
RETURN COUNT(*);
LOAD CSV WITH HEADERS FROM "https://guides.neo4j.com/ukcompanies/data/PSCAmericans.csv" AS row
MERGE (p:Person {name: row.`data.name`, birthYear: row.`data.date_of_birth.year`, birthMonth: row.`data.date_of_birth.month`})
ON CREATE SET p.nationality = row.`data.nationality`,
              p.countryOfResidence = row.`data.country_of_residence`
              // TODO: Address
RETURN COUNT(*);
LOAD CSV WITH HEADERS FROM "https://guides.neo4j.com/ukcompanies/data/PSCAmericans.csv" AS row
MATCH (c:Company {companyNumber: row.company_number})
MATCH (p:Person {name: row.`data.name`, birthYear: row.`data.date_of_birth.year`, birthMonth: row.`data.date_of_birth.month`})
MERGE (p)-[r:HAS_CONTROL]->(c)
 SET r.nature = split(replace(replace(replace(row.`data.natures_of_control`, "[",""),"]",""),  '"', ""), ",")
RETURN COUNT(*);
LOAD CSV WITH HEADERS FROM "https://guides.neo4j.com/ukcompanies/data/CompanyDataAmericans.csv" AS row
MATCH (c:Company {companyNumber: row.` CompanyNumber`})
SET c.name = row.CompanyName,
    c.mortgagesOutstanding = toInteger(row.`Mortgages.NumMortOutstanding`),
    c.incorporationDate = Date(Datetime({epochSeconds: apoc.date.parse(row.IncorporationDate,'s','dd/MM/yyyy')})),
    c.SIC = row.`SICCode.SicText_1`,
    c.countryOfOrigin = row.CountryOfOrigin,
    c.status = row.CompanyStatus,
    c.category = row.CompanyCategory;
LOAD CSV WITH HEADERS FROM "https://guides.neo4j.com/ukcompanies/data/ElectionDonationsAmericans.csv" AS row
MATCH (c:Company) WHERE c.companyNumber = row.CompanyRegistrationNumber
MERGE (p:Recipient {name: row.RegulatedEntityName})
SET p.entityType = row.RegulatedEntityType
MERGE (c)-[r:DONATED {ref: row.ECRef}]->(p)
SET r.date  = Date(Datetime({epochSeconds: apoc.date.parse(row.ReceivedDate,'s','dd/MM/yyyy')})),
    r.value = toFloat(replace(replace(row.Value, "£", ""), ",", ""));
LOAD CSV WITH HEADERS FROM "https://guides.neo4j.com/ukcompanies/data/LandOwnershipAmericans.csv" AS row
MATCH (c:Company {companyNumber: row.`Company Registration No. (1)`})
MERGE (p:Property {titleNumber: row.`Title Number`})
SET p.address = row.`Property Address`,
    p.county  = row.County,
    p.price   = toInteger(row.`Price Paid`),
    p.district = row.District
MERGE (c)-[r:OWNS]->(p)
WITH row, c,r,p WHERE row.`Date Proprietor Added` IS NOT NULL
SET r.date = Date(Datetime({epochSeconds: apoc.date.parse(row.`Date Proprietor Added`,'s','dd-MM-yyyy')}));

CREATE INDEX ON :Company(incorporationDate);

验证数据导入

通过运行以下命令来验证导入的数据模型

CALL db.schema.visualization();

人员

  • 姓名

  • 国籍

  • 居住国

  • 出生年份

  • 出生月份

关系

(:Person)-[:HAS_CONTROL]→(:Company)

公司

  • 公司编号

  • 姓名

  • 状态

  • 行业代码

  • 原产国

  • 成立日期

  • 未偿抵押贷款

关系

  • (:Company)-[:OWNS]→(:Property)

  • (:Company)-[:DONATED]→(:Recipient)

房产

  • 房产证号

  • 地址

  • 价格

接收方

  • 姓名

  • 实体类型

使用 MATCH 进行查询

现在我们已经导入了数据,是时候对其进行查询了!

在 Neo4j 中,我们使用 MATCH 命令来查询数据。语法为 MATCH,后跟一个图模式。例如

MATCH (p:Person {name: "Margery Kraus"})
RETURN p
  • () 表示一个节点

  • :Person 是节点标签

  • {} 表示属性

  • {name: ""}

  • p 成为一个变量,绑定到与模式匹配的图片段

  • 我们使用 RETURN 返回数据并可视化结果

使用 MATCH 进行查询 - 练习

现在轮到你了!

  • 查找名为 Michael Rubens BloombergPerson 节点。

  • 查找名为 GRAPHIC PLCCompany

请记住 MATCH 的基本格式

MATCH (variable:NodeLabel {property: "value"})
RETURN variable

使用 MATCH 进行查询 - 答案

查找名为 Michael Rubens BloombergPerson 节点。

MATCH (p:Person {name: "Michael Rubens Bloomberg"})
RETURN p

查找名为 GRAPHIC PLCCompany

MATCH (c:Company {name: "GRAPHIC PLC"})
RETURN c

使用关系

节点通过关系连接。我们可以定义更复杂的图模式,这些模式在我们的 MATCH 语句中包含关系,使用方括号 [] 来定义关系。例如

MATCH (p:Person {name: "Margery Kraus"})-[:HAS_CONTROL]->(c:Company)
RETURN p, c
  • 注意 -[:HAS_CONTROL]→ 模式

使用关系 - 练习

  • 你能找到与 Michael Rubens Bloomberg 相关的公司吗?

  • 这些与 Michael Ruben Bloomberg 相关的公司是否拥有任何房产?

  • 这些与 Michael Ruben Bloomberg 相关的公司是否进行了任何政治捐款?

使用关系 - 答案

与 Michael Rubens Bloomberg 相关的公司?

MATCH (p:Person {name: "Michael Rubens Bloomberg"})-[:HAS_CONTROL]->(c:Company)
RETURN p, c

这些公司是否拥有任何房产?

MATCH (p:Person {name: "Michael Rubens Bloomberg"})-[:HAS_CONTROL]->(c:Company)-[:OWNS]->(pr:Property)
RETURN p, c, pr

这些公司是否进行了任何政治捐款?

MATCH (p:Person {name: "Michael Rubens Bloomberg"})-[:HAS_CONTROL]->(c:Company)-[:DONATED]->(r:Recipient)
RETURN p, c, r

模糊匹配

我们已经了解了如何进行精确比较,但是“模糊”匹配呢?例如,如果我们不知道 Michael Bloomberg 的中间名怎么办?或者想要考虑轻微的拼写错误?

对于非精确匹配,我们有一些选择

  • CONTAINS 字符串比较运算符

  • 正则表达式

  • 使用全文索引进行真正的模糊匹配

模糊匹配 - CONTAINS

CONTAINS 字符串比较运算符可用于匹配包含子字符串的字符串。

为了利用 CONTAINS,我们需要引入 WHERE 子句。我们可以在 WHERE 子句中使用任何布尔表达式来过滤匹配项。例如

MATCH (p:Person)
WHERE p.name CONTAINS "Bloomberg"
RETURN p

模糊匹配 - 正则表达式

我们还可以使用正则表达式。

这等效于使用 CONTAINS

MATCH (p:Person)
WHERE p.name =~ ".*Bloomberg.*"
RETURN p

我们还可以进行不区分大小写的匹配

MATCH (c:Company)
WHERE c.name =~ "(?i)graphic.*"
RETURN c

请参阅正则表达式文档以获取更多示例。

模糊匹配 - 全文索引

全文索引可以帮助我们进行真正的模糊比较 - 考虑拼写错误。

首先,我们必须创建全文索引

CALL db.index.fulltext.createNodeIndex("nameIndex", ["Person"], ["name"])

然后我们可以查询它

CALL db.index.fulltext.queryNodes("nameIndex", "Peterson~")

请注意名称中的 ~。这表示我们应该匹配搜索词的轻微拼写错误。在此处阅读有关模糊匹配查询语法的更多信息此处

模糊匹配 - 练习

1) 包含

查找 Abigail Johnson 及其相关的任何公司。提示:数据可能包含标题前缀名称(先生、女士、小姐等),因此我们需要考虑这一点。

2) 正则表达式

我们想要查找伦敦的所有房产;但是,我们注意到 Property 节点上的 address 属性同时包含“London”和“LONDON”。编写一个使用正则表达式的查询来查找伦敦的所有 Property 节点。

模糊匹配 - 答案

1) 包含

MATCH (p:Person)-[:HAS_CONTROL]->(c:Company)
WHERE p.name CONTAINS "Abigail Johnson"
RETURN p,c

2) 正则表达式

MATCH (c:Company)
WHERE c.name =~ "(?i).*london.*"
RETURN c

处理数字

将属性值存储为数字对于回答以下问题很有用

显示所有 1,000 至 10,000 英镑之间的政治捐款

MATCH (c:Company)-[d:DONATED]->(r:Recipient)
WHERE 1000 < d.value < 10000
RETURN c,d,r

请注意,这里我们正在访问关系上的属性!

显示伦敦所有价值超过 1000 万英镑的房产,这些房产由美国人控制的公司拥有。

MATCH path=(prop:Property)<-[:OWNS]-(:Company)<-[:HAS_CONTROL]-(per:Person)
WHERE prop.price > 10000000 AND prop.address =~ "(?i).*London.*"
    AND per.nationality = "American"
RETURN path

对于特定个人,他们控制的公司所做的政治捐款总额是多少?

MATCH (p:Person {name: "Ms Abigail Johnson"})-[:HAS_CONTROL]->(c:Company)-[d:DONATED]->(:Recipient)
RETURN sum(d.value) AS totalDonations, p.name AS person, c.name AS company

在这里,我们执行聚合,对在我们的模式中匹配的所有 DONATED 关系的 value 属性求和。在此处阅读有关 Cypher 中聚合函数的更多信息此处

处理数字 - 练习

  • 查找 Michael Bloomberg 控制的公司所做的竞选捐款总额。

  • 这些捐款给了哪些政党?每个政党的总额是多少?

  • 在与 Bloomberg 相关的公司中,哪家公司进行的竞选捐款最多?

处理数字 - 答案

查找 Michael Bloomberg 控制的公司所做的竞选捐款总额。

//Find the total value of campaign donations made by companies controlled by Michael Bloomberg.

// First be sure to find all Michael Bloombergs in the data
MATCH (p:Person)
WHERE p.name =~  "(?i).*Michael.*Bloomberg.*"
// Find all companies connected to Bloomberg and donations
MATCH (p)-[:HAS_CONTROL]->(c:Company)-[r:DONATED]->(party:Recipient)
// Aggregate the value property of all donations from these companies
RETURN sum(r.value) AS total

这些捐款给了哪些政党?每个政党的总额是多少?

MATCH (p:Person)
WHERE p.name =~  "(?i).*Michael.*Bloomberg.*"
MATCH (p)-[:HAS_CONTROL]->(c:Company)-[r:DONATED]->(party:Recipient)
// When we add party.name to the RETURN clause we group our sum aggregation by party.name
RETURN party.name, sum(r.value) AS total
ORDER BY total DESC

在与 Bloomberg 相关的公司中,哪家公司进行的竞选捐款最多?

MATCH (p:Person)
WHERE p.name =~  "(?i).*Michael.*Bloomberg.*"
MATCH (p)-[:HAS_CONTROL]->(c:Company)-[r:DONATED]->(party:Recipient)
RETURN c.name, sum(r.value) AS total
ORDER BY total DESC

日期

日期在 Neo4j 中被视为一种特殊类型,并在 Cypher 中具有自己的函数。

例如,要构造一个日期

RETURN date("2019-03-06")

我们可以像这样过滤某个日期范围内的事件

MATCH (c:Company)
WHERE date("2017-01-01") < c.incorporationDate < date("2017-01-15")
RETURN c

日期 - 练习

  • 查找2016年1月1日之后创建的所有进行了竞选捐赠的公司。

日期 - 答案

MATCH (c:Company)-[r:DONATED]->(party:Recipient)
WHERE Date("2016-01-01") < c.incorporationDate
RETURN c

位置数据

源数据包含地址。如果我们能够将这些地址转换为经纬度,我们就可以搜索彼此靠近的属性,搜索某个点或多边形一定范围内的属性,或者创建交互式地理数据可视化。

幸运的是,我们可以使用Neo4j的地理编码过程来实现这一点。

CALL apoc.spatial.geocodeOnce("6 Anchorage Terrace, Durham (DH1 3DL)") YIELD location, latitude, longitude

我们可以使用新的属性location(点类型)更新Property节点。

MATCH (p:Property) WITH p LIMIT 1
CALL apoc.spatial.geocodeOnce(p.address) YIELD location, latitude, longitude, description
SET p.location = Point({latitude: latitude, longitude: longitude})
RETURN p

注意:您需要具有数据库的写入权限。

查找数据集中位于Neo4j伦敦办公室10公里范围内的属性。

MATCH path=(p:Property)<-[:OWNS]-(:Company)<-[:HAS_CONTROL]-(:Person)
WHERE distance(p.location, Point({latitude:51.5122338, longitude:-0.1180369})) < 10000
RETURN path

位置数据 - 练习

开放式练习

假设您正在研究黑石集团的首席执行官Stephen A. Schwarzman。您可以在数据中找到关于他的哪些信息?