1. Cypher 查询语言基础语法
1.1 节点(Node)
- 用圆括号
()表示一个节点。 - 节点可以有标签(类型),用冒号
:标记。 - 节点可以有属性,属性写在花括号
{}里,类似字典。
示例:
(:Person {name:'zhangsan', age:33})
表示一个标签为 Person 的节点,属性有 name 和 age。
1.2 关系(Relationship)
- 用中括号
[]表示关系。 - 关系可以有类型,用冒号
:标记。 - 关系可以有属性,也写在花括号
{}里。 - 关系有方向,用箭头
->或<-表示。
示例:
()-[:FRIENDS {since:2020}]->()
表示一个类型为 FRIENDS 的关系,属性是 since:2020,方向从左到右。
1.3 常用操作语句
| 操作 | 语法结构说明 | 示例代码 |
|---|---|---|
| 创建节点 | CREATE (变量:标签 {属性}) | CREATE (p:Person {name:'zhangsan'}) |
| 查询节点 | MATCH (变量:标签 {属性}) RETURN 变量 | MATCH (p:Person {name:'zhangsan'}) RETURN p |
| 创建关系 | MATCH 找节点,MERGE (a)-[:关系类型 {属性}]->(b) | MATCH (a:Person),(b:Person) MERGE (a)-[:FRIENDS]->(b) |
| 修改属性 | MATCH 找节点,SET 变量.属性=值 | MATCH (p:Person) SET p.age=33 |
| 删除属性 | MATCH 找节点,REMOVE 变量.属性 | MATCH (p:Person) REMOVE p.age |
| 删除节点 | MATCH 找节点,DETACH DELETE 变量 | MATCH (p:Person) DETACH DELETE p |
| 删除关系 | MATCH 找关系,DELETE 关系变量 | MATCH (a)-[r:FRIENDS]->(b) DELETE r |
1.4 关键词说明
- MATCH:查找已有节点或关系,相当于 SQL 的 SELECT。
- CREATE:新建节点或关系。
- MERGE:如果不存在则新建,已存在则不变。
- RETURN:返回查询结果。
- SET:设置节点或关系的属性。
- REMOVE:删除节点或关系的属性。
- DELETE/DETACH DELETE:删除节点或关系,DETACH DELETE 会一并删除相关关系。
2. Cypher Demo
2.1 清空数据库
MATCH (n) DETACH DELETE n
2.2 批量创建人物节点
CREATE (:Person {name:'zhangsan'}),
(:Person {name:'lisi'}),
(:Person {name:'wangwu'}),
(:Person {name:'zhaoliu'}),
(:Person {name:'sunqi'}),
(:Person {name:'liuba'});
2.3 批量创建地区节点
CREATE (:Location {city:'Beijing', state:'BJ'}),
(:Location {city:'Shanghai', state:'SH'}),
(:Location {city:'Guangzhou', state:'GZ'}),
(:Location {city:'Shenzhen', state:'SZ'}),
(:Location {city:'Chengdu', state:'CD'});
2.4 创建人物间关系及关系属性
MATCH (a:Person {name:'zhangsan'}), (b:Person {name:'lisi'})
MERGE (a)-[:FRIENDS {since:2020}]->(b);
MATCH (a:Person {name:'wangwu'}), (b:Person {name:'zhaoliu'})
MERGE (a)-[:FRIENDS {since:2021}]->(b);
MATCH (a:Person {name:'sunqi'}), (b:Person {name:'liuba'})
MERGE (a)-[:FRIENDS {since:2019}]->(b);
MATCH (a:Person {name:'zhangsan'}), (b:Person {name:'wangwu'})
MERGE (a)-[:MARRIED {since:2015}]->(b);
2.5 创建人物与地区的关系(出生地)
MATCH (a:Person {name:'zhangsan'}), (b:Location {city:'Beijing'})
MERGE (a)-[:BORN_IN {year:1990}]->(b);
(其它人物同理)
2.6 查询示例
- 查询所有在 Beijing 出生的人物
MATCH (a:Person)-[:BORN_IN]->(b:Location {city:'Beijing'}) RETURN a, b; - 查询所有有外向关系的节点
MATCH (a)-->() RETURN a; - 查询所有有关系的节点及类型
MATCH (a)-[r]->() RETURN a.name, type(r); - 查找某人的朋友的朋友
MATCH (a:Person {name:'zhangsan'})-[:FRIENDS]-()-[:FRIENDS]-(friend_of_a_friend) RETURN friend_of_a_friend.name AS fofName;
2.7 节点属性操作
- 增加/修改属性
MATCH (a:Person {name:'zhangsan'}) SET a.age=33; - 删除属性
MATCH (a:Person {name:'zhangsan'}) REMOVE a.age;
2.8 删除节点及关系
- 删除有关系的节点
MATCH (a:Person {name:'zhangsan'})-[rel]-(b:Person) DELETE rel, a, b; - 删除所有 FRIENDS 关系
MATCH ()-[r:FRIENDS]-() DELETE r; - 删除指定节点
MATCH (n:Person {name:'zhangsan'}) DETACH DELETE n;
3. 代码 Demo
3.1 basic
package com.example.neo4jdemo.demo;
import com.example.neo4jdemo.entity.Person;
import com.example.neo4jdemo.repository.PersonRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Component;
/**
* Cypher 操作实例 Demo
* 演示如何使用 Spring Data Neo4j 执行各种 Cypher 操作
*
* 对应的 Cypher 操作示例:
* 2.1 清空数据库
* 2.2 批量创建人物节点
* 2.3 批量创建地区节点
* 2.4 创建人物间关系及关系属性
* 2.5 创建人物与地区的关系(出生地)
* 2.6 查询示例
* 2.7 节点属性操作
* 2.8 删除节点及关系
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class CypherOperationDemo implements CommandLineRunner {
private final PersonRepository personRepository;
private final Neo4jClient neo4jClient;
// 是否自动执行 Demo
private static final boolean AUTO_RUN_DEMO = false;
@Override
public void run(String... args) throws Exception {
if (!AUTO_RUN_DEMO) {
log.info("CypherOperationDemo 已禁用。如需启用,请设置 AUTO_RUN_DEMO = true");
return;
}
log.info("========== Cypher 操作 Demo 开始 ==========");
try {
// 2.1 清空数据库
log.info("\n>>> 2.1 清空数据库");
clearDatabase();
// 2.2 批量创建人物节点
log.info("\n>>> 2.2 批量创建人物节点");
createPersonNodes();
// 2.3 批量创建地区节点
log.info("\n>>> 2.3 批量创建地区节点");
createLocationNodes();
// 2.4 创建人物间关系及关系属性
log.info("\n>>> 2.4 创建人物间关系及关系属性");
createPersonRelationships();
// 2.5 创建人物与地区的关系(出生地)
log.info("\n>>> 2.5 创建人物与地区的关系(出生地)");
createBornInRelationships();
// 2.6 查询示例
log.info("\n>>> 2.6 查询示例");
queryExamples();
// 2.7 节点属性操作
log.info("\n>>> 2.7 节点属性操作");
nodePropertyOperations();
// 2.8 删除节点及关系
log.info("\n>>> 2.8 删除节点及关系");
deleteOperations();
log.info("\n========== Cypher 操作 Demo 完成 ==========");
} catch (Exception e) {
log.error("Demo 执行失败", e);
throw e;
}
}
/**
* 2.1 清空数据库
* Cypher: MATCH (n) DETACH DELETE n
*/
private void clearDatabase() {
log.info("执行: MATCH (n) DETACH DELETE n");
neo4jClient.query("MATCH (n) DETACH DELETE n")
.run();
log.info("✓ 数据库已清空");
}
/**
* 2.2 批量创建人物节点
* Cypher:
* CREATE (:Person {name:'zhangsan'}),
* (:Person {name:'lisi'}),
* (:Person {name:'wangwu'}),
* (:Person {name:'zhaoliu'}),
* (:Person {name:'sunqi'}),
* (:Person {name:'liuba'});
*/
private void createPersonNodes() {
String[] names = {"zhangsan", "lisi", "wangwu", "zhaoliu", "sunqi", "liuba"};
// 方式1: 使用 Repository 逐个保存
log.info("方式1: 使用 Repository 逐个保存");
for (String name : names) {
Person person = new Person(name, null);
personRepository.save(person);
log.info(" 创建人物: {}", name);
}
// 方式2: 使用 Neo4jClient 执行 Cypher 批量创建(更高效)
log.info("方式2: 使用 Neo4jClient 执行 Cypher 批量创建");
String cypherQuery = "CREATE (:Person {name:'zhangsan'})," +
" (:Person {name:'lisi'})," +
" (:Person {name:'wangwu'})," +
" (:Person {name:'zhaoliu'})," +
" (:Person {name:'sunqi'})," +
" (:Person {name:'liuba'})";
neo4jClient.query(cypherQuery).run();
log.info("✓ 人物节点创建完成");
}
/**
* 2.3 批量创建地区节点
* Cypher:
* CREATE (:Location {city:'Beijing', state:'BJ'}),
* (:Location {city:'Shanghai', state:'SH'}),
* (:Location {city:'Guangzhou', state:'GZ'}),
* (:Location {city:'Shenzhen', state:'SZ'}),
* (:Location {city:'Chengdu', state:'CD'});
*/
private void createLocationNodes() {
log.info("执行: CREATE (:Location {...}) 批量创建地区节点");
String cypherQuery = "CREATE (:Location {city:'Beijing', state:'BJ'})," +
" (:Location {city:'Shanghai', state:'SH'})," +
" (:Location {city:'Guangzhou', state:'GZ'})," +
" (:Location {city:'Shenzhen', state:'SZ'})," +
" (:Location {city:'Chengdu', state:'CD'})";
neo4jClient.query(cypherQuery).run();
log.info("✓ 地区节点创建完成");
}
/**
* 2.4 创建人物间关系及关系属性
* Cypher:
* MATCH (a:Person {name:'zhangsan'}), (b:Person {name:'lisi'})
* MERGE (a)-[:FRIENDS {since:2020}]->(b);
* ... 其他关系
*/
private void createPersonRelationships() {
// 关系定义: [源人物, 目标人物, 关系类型, 关系属性]
Object[][] relationships = {
{"zhangsan", "lisi", "FRIENDS", 2020},
{"wangwu", "zhaoliu", "FRIENDS", 2021},
{"sunqi", "liuba", "FRIENDS", 2019},
{"zhangsan", "wangwu", "MARRIED", 2015}
};
for (Object[] rel : relationships) {
String personA = (String) rel[0];
String personB = (String) rel[1];
String relType = (String) rel[2];
Integer year = (Integer) rel[3];
String cypherQuery = String.format(
"MATCH (a:Person {name:'%s'}), (b:Person {name:'%s'}) " +
"MERGE (a)-[:%s {since:%d}]->(b)",
personA, personB, relType, year
);
neo4jClient.query(cypherQuery).run();
log.info(" 创建关系: {} -[{}]-> {} (since: {})", personA, relType, personB, year);
}
log.info("✓ 人物间关系创建完成");
}
/**
* 2.5 创建人物与地区的关系(出生地)
* Cypher:
* MATCH (a:Person {name:'zhangsan'}), (b:Location {city:'Beijing'})
* MERGE (a)-[:BORN_IN {year:1990}]->(b);
*/
private void createBornInRelationships() {
// 人物与出生地的映射
Object[][] bornInData = {
{"zhangsan", "Beijing", 1990},
{"lisi", "Shanghai", 1992},
{"wangwu", "Guangzhou", 1988},
{"zhaoliu", "Shenzhen", 1995},
{"sunqi", "Chengdu", 1991},
{"liuba", "Beijing", 1993}
};
for (Object[] data : bornInData) {
String personName = (String) data[0];
String city = (String) data[1];
Integer year = (Integer) data[2];
String cypherQuery = String.format(
"MATCH (a:Person {name:'%s'}), (b:Location {city:'%s'}) " +
"MERGE (a)-[:BORN_IN {year:%d}]->(b)",
personName, city, year
);
neo4jClient.query(cypherQuery).run();
log.info(" 创建关系: {} -[BORN_IN]-> {} (year: {})", personName, city, year);
}
log.info("✓ 出生地关系创建完成");
}
/**
* 2.6 查询示例
*/
private void queryExamples() {
// 查询1: 查询所有在 Beijing 出生的人物
log.info("\n查询1: 查询所有在 Beijing 出生的人物");
log.info("Cypher: MATCH (a:Person)-[:BORN_IN]->(b:Location {city:'Beijing'}) RETURN a, b");
String query1 = "MATCH (a:Person)-[:BORN_IN]->(b:Location {city:'Beijing'}) RETURN a.name as name, b.city as city";
neo4jClient.query(query1)
.fetch()
.all()
.forEach(record -> {
log.info(" {} 出生于 {}", record.get("name"), record.get("city"));
});
// 查询2: 查询所有有外向关系的节点
log.info("\n查询2: 查询所有有外向关系的节点");
log.info("Cypher: MATCH (a)-->() RETURN a");
String query2 = "MATCH (a)-->() RETURN DISTINCT a.name as name";
neo4jClient.query(query2)
.fetch()
.all()
.forEach(record -> {
Object name = record.get("name");
if (name != null) {
log.info(" {}", name);
}
});
// 查询3: 查询所有有关系的节点及类型
log.info("\n查询3: 查询所有有关系的节点及类型");
log.info("Cypher: MATCH (a)-[r]->() RETURN a.name, type(r)");
String query3 = "MATCH (a)-[r]->() RETURN a.name as name, type(r) as relType";
neo4jClient.query(query3)
.fetch()
.all()
.forEach(record -> {
log.info(" {} -[{}]->", record.get("name"), record.get("relType"));
});
// 查询4: 查找某人的朋友的朋友
log.info("\n查询4: 查找 zhangsan 的朋友的朋友");
log.info("Cypher: MATCH (a:Person {name:'zhangsan'})-[:FRIENDS]-()-[:FRIENDS]-(friend_of_a_friend) RETURN friend_of_a_friend.name");
String query4 = "MATCH (a:Person {name:'zhangsan'})-[:FRIENDS]-()-[:FRIENDS]-(fof) RETURN DISTINCT fof.name as fofName";
neo4jClient.query(query4)
.fetch()
.all()
.forEach(record -> {
log.info(" 朋友的朋友: {}", record.get("fofName"));
});
}
/**
* 2.7 节点属性操作
*/
private void nodePropertyOperations() {
// 增加/修改属性
log.info("\n增加/修改属性");
log.info("Cypher: MATCH (a:Person {name:'zhangsan'}) SET a.age=33");
String updateQuery = "MATCH (a:Person {name:'zhangsan'}) SET a.age=33";
neo4jClient.query(updateQuery).run();
log.info("✓ 已设置 zhangsan 的年龄为 33");
// 查询验证
String verifyQuery = "MATCH (a:Person {name:'zhangsan'}) RETURN a.age as age";
neo4jClient.query(verifyQuery)
.fetch()
.all()
.forEach(record -> {
log.info(" 验证: zhangsan 的年龄 = {}", record.get("age"));
});
// 删除属性
log.info("\n删除属性");
log.info("Cypher: MATCH (a:Person {name:'zhangsan'}) REMOVE a.age");
String removeQuery = "MATCH (a:Person {name:'zhangsan'}) REMOVE a.age";
neo4jClient.query(removeQuery).run();
log.info("✓ 已删除 zhangsan 的年龄属性");
}
/**
* 2.8 删除节点及关系
*/
private void deleteOperations() {
// 删除有关系的节点
log.info("\n删除有关系的节点");
log.info("Cypher: MATCH (a:Person {name:'zhangsan'})-[rel]-(b:Person) DELETE rel, a, b");
String deleteWithRelQuery = "MATCH (a:Person {name:'zhangsan'})-[rel]-(b:Person) DELETE rel, a, b";
neo4jClient.query(deleteWithRelQuery).run();
log.info("✓ 已删除 zhangsan 及其关系");
// 删除所有 FRIENDS 关系
log.info("\n删除所有 FRIENDS 关系");
log.info("Cypher: MATCH ()-[r:FRIENDS]-() DELETE r");
String deleteFriendsQuery = "MATCH ()-[r:FRIENDS]-() DELETE r";
neo4jClient.query(deleteFriendsQuery).run();
log.info("✓ 已删除所有 FRIENDS 关系");
// 删除指定节点
log.info("\n删除指定节点");
log.info("Cypher: MATCH (n:Person {name:'lisi'}) DETACH DELETE n");
String deleteNodeQuery = "MATCH (n:Person {name:'lisi'}) DETACH DELETE n";
neo4jClient.query(deleteNodeQuery).run();
log.info("✓ 已删除 lisi 节点");
// 统计剩余节点
log.info("\n统计剩余节点");
String countQuery = "MATCH (n:Person) RETURN count(n) as count";
neo4jClient.query(countQuery)
.fetch()
.all()
.forEach(record -> {
log.info(" 剩余 Person 节点数: {}", record.get("count"));
});
}
}
3.2 advanced
package com.example.neo4jdemo.demo;
import com.example.neo4jdemo.entity.Location;
import com.example.neo4jdemo.entity.Person;
import com.example.neo4jdemo.repository.LocationRepository;
import com.example.neo4jdemo.repository.PersonRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 高级 Cypher 操作 Demo
* 演示参数化查询、事务管理、复杂查询等高级用法
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class AdvancedCypherDemo {
private final Neo4jClient neo4jClient;
private final PersonRepository personRepository;
private final LocationRepository locationRepository;
/**
* 示例1: 参数化查询(防止 Cypher 注入)
*/
public void parameterizedQueryExample() {
log.info("\n========== 示例1: 参数化查询 ==========");
String name = "zhangsan";
Integer minAge = 25;
// ✅ 推荐: 使用参数化查询
String query = "MATCH (p:Person {name:$name}) WHERE p.age > $minAge RETURN p";
neo4jClient.query(query)
.bind(name).to("name")
.bind(minAge).to("minAge")
.fetch()
.all()
.forEach(record -> {
log.info("找到人物: {}", record.get("p"));
});
log.info("✓ 参数化查询完成");
}
/**
* 示例2: 复杂查询 - 查找共同朋友
* 查找两个人的共同朋友
*/
public void findCommonFriendsExample() {
log.info("\n========== 示例2: 查找共同朋友 ==========");
String person1 = "zhangsan";
String person2 = "lisi";
String query = "MATCH (p1:Person {name:$person1})-[:FRIENDS]-(friend), " +
"(p2:Person {name:$person2})-[:FRIENDS]-(friend) " +
"RETURN DISTINCT friend.name as commonFriend";
neo4jClient.query(query)
.bind(person1).to("person1")
.bind(person2).to("person2")
.fetch()
.all()
.forEach(record -> {
log.info("共同朋友: {}", record.get("commonFriend"));
});
log.info("✓ 共同朋友查询完成");
}
/**
* 示例3: 聚合查询 - 统计信息
*/
public void aggregationQueryExample() {
log.info("\n========== 示例3: 聚合查询 ==========");
// 统计总人数
String countQuery = "MATCH (p:Person) RETURN count(p) as totalPeople";
neo4jClient.query(countQuery)
.fetch()
.one()
.ifPresent(record -> {
log.info("总人数: {}", record.get("totalPeople"));
});
// 统计每个城市的人数
String cityCountQuery = "MATCH (p:Person)-[:BORN_IN]->(l:Location) " +
"RETURN l.city as city, count(p) as count " +
"ORDER BY count DESC";
neo4jClient.query(cityCountQuery)
.fetch()
.all()
.forEach(record -> {
log.info("城市: {}, 人数: {}", record.get("city"), record.get("count"));
});
log.info("✓ 聚合查询完成");
}
/**
* 示例4: 路径查询 - 查找最短路径
*/
public void pathQueryExample() {
log.info("\n========== 示例4: 路径查询 ==========");
String startPerson = "zhangsan";
String endPerson = "liuba";
// 查找两个人之间的所有路径(最多3跳)
String pathQuery = "MATCH path = (p1:Person {name:$start})-[*1..3]-(p2:Person {name:$end}) " +
"RETURN path, length(path) as pathLength";
neo4jClient.query(pathQuery)
.bind(startPerson).to("start")
.bind(endPerson).to("end")
.fetch()
.all()
.forEach(record -> {
log.info("找到路径,长度: {}", record.get("pathLength"));
});
log.info("✓ 路径查询完成");
}
/**
* 示例5: 条件更新 - 批量更新属性
*/
public void conditionalUpdateExample() {
log.info("\n========== 示例5: 条件更新 ==========");
Integer ageThreshold = 30;
String city = "Beijing";
// 更新所有在 Beijing 出生且年龄超过 30 的人的属性
String updateQuery = "MATCH (p:Person)-[:BORN_IN]->(l:Location {city:$city}) " +
"WHERE p.age > $ageThreshold " +
"SET p.vip = true " +
"RETURN p.name as name";
neo4jClient.query(updateQuery)
.bind(city).to("city")
.bind(ageThreshold).to("ageThreshold")
.fetch()
.all()
.forEach(record -> {
log.info("已更新: {}", record.get("name"));
});
log.info("✓ 条件更新完成");
}
/**
* 示例6: 事务管理 - 创建人物及其关系
*/
@Transactional
public void transactionalOperationExample() {
log.info("\n========== 示例6: 事务管理 ==========");
try {
// 创建新人物
Person newPerson = new Person("xiaoming", 28);
personRepository.save(newPerson);
log.info("✓ 创建人物: xiaoming");
// 创建新地区
Location newLocation = new Location("Hangzhou", "HZ");
locationRepository.save(newLocation);
log.info("✓ 创建地区: Hangzhou");
// 创建关系
String relationQuery = "MATCH (p:Person {name:'xiaoming'}), (l:Location {city:'Hangzhou'}) " +
"MERGE (p)-[:BORN_IN {year:1996}]->(l)";
neo4jClient.query(relationQuery).run();
log.info("✓ 创建关系: xiaoming -[BORN_IN]-> Hangzhou");
log.info("✓ 事务操作完成(全部成功)");
} catch (Exception e) {
log.error("事务操作失败,将自动回滚", e);
throw e;
}
}
/**
* 示例7: 返回多种数据类型
*/
public void multipleReturnTypesExample() {
log.info("\n========== 示例7: 返回多种数据类型 ==========");
String query = "MATCH (p:Person)-[r:FRIENDS]->(friend) " +
"RETURN p.name as personName, " +
" friend.name as friendName, " +
" r.since as since, " +
" p.age as age, " +
" p.vip as isVip";
neo4jClient.query(query)
.fetch()
.all()
.forEach(record -> {
String personName = (String) record.get("personName");
String friendName = (String) record.get("friendName");
Integer since = (Integer) record.get("since");
Integer age = (Integer) record.get("age");
Boolean isVip = (Boolean) record.get("isVip");
log.info("人物: {}, 年龄: {}, VIP: {}, 朋友: {}, 关系始于: {}",
personName, age, isVip, friendName, since);
});
log.info("✓ 多类型返回查询完成");
}
/**
* 示例8: 使用 CASE 语句
*/
public void caseStatementExample() {
log.info("\n========== 示例8: CASE 语句 ==========");
String query = "MATCH (p:Person) " +
"RETURN p.name as name, " +
" CASE " +
" WHEN p.age < 25 THEN '年轻' " +
" WHEN p.age < 35 THEN '中年' " +
" ELSE '年长' " +
" END as ageGroup";
neo4jClient.query(query)
.fetch()
.all()
.forEach(record -> {
log.info("人物: {}, 年龄组: {}", record.get("name"), record.get("ageGroup"));
});
log.info("✓ CASE 语句查询完成");
}
/**
* 示例9: 使用 COLLECT 聚合
*/
public void collectAggregationExample() {
log.info("\n========== 示例9: COLLECT 聚合 ==========");
String query = "MATCH (p:Person)-[:FRIENDS]->(friend) " +
"RETURN p.name as person, collect(friend.name) as friends";
neo4jClient.query(query)
.fetch()
.all()
.forEach(record -> {
String person = (String) record.get("person");
List<?> friends = (List<?>) record.get("friends");
log.info("人物: {}, 朋友列表: {}", person, friends);
});
log.info("✓ COLLECT 聚合查询完成");
}
/**
* 示例10: 使用 APOC 函数(如果已安装)
* 计算两个节点之间的最短路径长度
*/
public void shortestPathExample() {
log.info("\n========== 示例10: 最短路径 ==========");
String person1 = "zhangsan";
String person2 = "liuba";
// 使用 Cypher 的 shortestPath 函数
String query = "MATCH (p1:Person {name:$person1}), (p2:Person {name:$person2}) " +
"MATCH path = shortestPath((p1)-[*]-(p2)) " +
"RETURN length(path) as distance";
neo4jClient.query(query)
.bind(person1).to("person1")
.bind(person2).to("person2")
.fetch()
.one()
.ifPresent(record -> {
log.info("最短路径距离: {}", record.get("distance"));
});
log.info("✓ 最短路径查询完成");
}
/**
* 示例11: 批量删除操作
*/
public void batchDeleteExample() {
log.info("\n========== 示例11: 批量删除 ==========");
// 删除所有年龄小于 20 的人
String deleteQuery = "MATCH (p:Person) WHERE p.age < $minAge " +
"DETACH DELETE p " +
"RETURN count(p) as deletedCount";
neo4jClient.query(deleteQuery)
.bind(20).to("minAge")
.fetch()
.one()
.ifPresent(record -> {
log.info("已删除 {} 个人物", record.get("deletedCount"));
});
log.info("✓ 批量删除完成");
}
/**
* 示例12: 使用 DISTINCT 去重
*/
public void distinctExample() {
log.info("\n========== 示例12: DISTINCT 去重 ==========");
String query = "MATCH (p:Person)-[:BORN_IN]->(l:Location) " +
"RETURN DISTINCT l.city as city";
neo4jClient.query(query)
.fetch()
.all()
.forEach(record -> {
log.info("城市: {}", record.get("city"));
});
log.info("✓ DISTINCT 查询完成");
}
/**
* 示例13: 使用 ORDER BY 和 LIMIT
*/
public void orderByLimitExample() {
log.info("\n========== 示例13: ORDER BY 和 LIMIT ==========");
String query = "MATCH (p:Person) " +
"RETURN p.name as name, p.age as age " +
"ORDER BY p.age DESC " +
"LIMIT 5";
neo4jClient.query(query)
.fetch()
.all()
.forEach(record -> {
log.info("人物: {}, 年龄: {}", record.get("name"), record.get("age"));
});
log.info("✓ ORDER BY LIMIT 查询完成");
}
/**
* 示例14: 使用 SKIP 和 LIMIT 进行分页
*/
public void paginationExample() {
log.info("\n========== 示例14: 分页查询 ==========");
int pageSize = 3;
int pageNumber = 1;
int skip = (pageNumber - 1) * pageSize;
String query = "MATCH (p:Person) " +
"RETURN p.name as name, p.age as age " +
"ORDER BY p.name " +
"SKIP $skip LIMIT $limit";
neo4jClient.query(query)
.bind(skip).to("skip")
.bind(pageSize).to("limit")
.fetch()
.all()
.forEach(record -> {
log.info("人物: {}, 年龄: {}", record.get("name"), record.get("age"));
});
log.info("✓ 分页查询完成");
}
/**
* 示例15: 使用 WITH 进行多步查询
*/
public void multiStepQueryExample() {
log.info("\n========== 示例15: 多步查询 ==========");
String query = "MATCH (p:Person)-[:FRIENDS]->(friend) " +
"WITH p, count(friend) as friendCount " +
"WHERE friendCount > 1 " +
"RETURN p.name as name, friendCount";
neo4jClient.query(query)
.fetch()
.all()
.forEach(record -> {
log.info("人物: {}, 朋友数: {}", record.get("name"), record.get("friendCount"));
});
log.info("✓ 多步查询完成");
}
}