SpringBoot 3.3 + Neo4j 5.1 + Java17 接入教程

官方文档: Spring Data Neo4j 基于官方文档,基本就能完成基本的接入,Neo4j的版本很乱,如果使用SpringBoot2.+的话,一定要注意Neo4j Server的版本与Driver的匹配关系,否则很容易踩坑。 由于我不是生产使用,直接用官方最新反倒是没遇到此类问题,但网上对于这个问题遇到的很多。 简单接入 写了几个单测,看了下流程,得益于SpringBoot-Data的封装,和操作MongoDB、MySQL等关系型数据库基本无异,使用过程很丝滑。 @Test public void readAll() { List<Person> personList = personRepository.findAll(); Assert.isTrue(!personList.isEmpty(), "isEmpty"); } @Test public void delById() { personRepository.findById(71L).ifPresent(person -> { System.out.println("before delete: " + person.getName()); }); personRepository.deleteById(71L); Person person = personRepository.findById(71L).orElse(null); System.out.println("after delete: " + person); } @Test public void save() { Person person = new Person(); person.setName("人参果树"); personRepository.save(person); } findAll delById 删除前: 删除后: saveOne 关系的创建 对于节点的基本查询,由于其结构类似于结构性,和结构性数据库查询无异,但是对于N4o4j特殊的关系结构,就有一些不一样了。 @Repository public interface PersonRepository extends Neo4jRepository<Person, Long> { Person findByName(String name); /** * 创建人物关系 * @param from 源 * @param relation 关系 * @param to 目标 */ @Query("match (n:person {name: $from}),(m:person {name: $to}) " + "create (n)-[:西游人物关系{relation:$relation}]->(m)") void createRelation(String from, String relation, String to); } @SpringBootTest public class PersonRelationShipRepositoryTest { @Resource private PersonRepository personRepository; @Test public void addRelation() { Person personYangjian = personRepository.findByName("杨戬"); Person person2 = new Person(); person2.setName("玉鼎真人"); PersonRelationShip relationShip = new PersonRelationShip(); relationShip.setChild(person2); relationShip.setRelation("师傅"); personYangjian.getPersonRelationShips().add(relationShip); personRepository.save(personYangjian); } @Test public void addRelation2() { personRepository.createRelation("玉皇大帝", "妻子", "王母娘娘"); } } 创建关系第一种方式 创建关系第二种方式 坑 坑1 spring-boot-data 3.+的很多注解都改变了,例如: 在老版本中标注节点使用的是@NodeEntity,新版本被替换为@Node(labels = "person") 在老版本中,关系的表述是在关系对象中通过@RelationshipEntity、@StartNode、@EndNode来描述,在新版这些都没有了。取而代之的是在源对象中补充属性@Relationship(type = "师傅", direction = Relationship.Direction.OUTGOING)来描述关系。 老关系: @Data @RelationshipEntity(type = "xxx") public class PersonRelationShip { @Id @GeneratedValue private Long id; @StartNode private Person parent; @TargetNode private Person child; } 新关系: @Node(labels = "person") @Data public class Person { @Id @GeneratedValue private Long id; @Property private String name; @Relationship(type = "师傅", direction = Relationship.Direction.OUTGOING) private List<PersonRelationShip> personRelationShips; } @Data @RelationshipProperties public class PersonRelationShip { @Id @GeneratedValue private Long id; @TargetNode private Person child; @Property private String relation; } 坑2 spring-boot-data 3.+的@Query中想要获取参数,需要使用 $fieldName,而不是 {0} 的方式,如果自己写的时候一定要注意。比如: /** * 创建人物关系 * @param from 源 * @param relation 关系 * @param to 目标 */ @Query("match (n:person {name: $from}),(m:person {name: $to}) " + "create (n)-[:西游人物关系{relation:$relation}]->(m)") void createRelation(String from, String relation, String to);
Read More ~

使用Cloudflare加速Github Pages

之前就有了解过Cloudflare,但是一直没有去实践,最近了解到它提供了免费的CDN可以加速,所以想找个教程按流程试一下,一个是确实Github Pages访问太慢了(虽说Cloudflare在国内也没有节点...但聊胜于无),另一个主要是也可以学习拓展下知识。其实使用起来整个过程还是挺简单的,基本无脑操作。 注册Cloudflare的账号,填写你的域名,交给它托管(打开官网,点击就送) 拷贝Cloudflare提供的DNS解析,替换你的域名服务商提供的解析(我使用的aliyun,在域名-管理-DNS解析)处可替换 等待几分钟,即可失效。 上面的步骤更像是把大象放进冰箱的步骤,过于简单,主要是网络上针对此流程提供的优秀博客数不胜数,没必要再产生一篇重复的内容,并且文章也主要是用于记录自己接入的过程和遇到的坑。 我看的博客是: 使用cdn加速Github Pages的访问速度 跟着这个博客基本就可以把域名完成托管,整个过程主要是遇到一个坑,在配置完成之后,发现本来通过aliyun解析的网站还能打开,托管之后反倒是打不开了,Chrome提示“重定向的次数过多”,这一步主要是因为解析配置的问题,可以登陆Cloudflare,然后再SSL/TLS菜单里调整下面的配置即可: 原本选中的是“灵活的”,调整为“严谨”即可。
Read More ~

Neo4j常用函数

字符串函数 match (n:Lianhuachi) where n.name = '去种田的向凹凸' return n.name,substring(n.name, 0,3) AGGREGATION聚合函数 和普通SQL一致的含义 match (n:Lianhuachi) return count(n),max(n.age),min(n.age),avg(n.age),sum(n.age) 关系函数 match (x)-[n:`莲花池人物关系`]->(y) return id(n),n 备份 Neo4j 官方文档: Backup modes ➜ bin ./neo4j-admin database dump neo4j --to-path=/tmp 2024-06-26 15:24:15.414+0000 INFO [o.n.c.d.DumpCommand] Starting dump of database 'neo4j' Done: 37 files, 2.993MiB processed. 2024-06-26 15:24:19.338+0000 INFO [o.n.c.d.DumpCommand] Dump completed successfully ➜ bin ll /tmp/ |grep "neo4j.dump" -rw-r--r-- 1 imyzt wheel 305K Jun 26 23:24 neo4j.dump 恢复 Neo4j 官方文档: Restore a database dump ➜ bin ./neo4j-admin database load --from-path=/tmp/ neo4j --overwrite-destination=true Done: 37 files, 2.993MiB processed. 可以看到,数据恢复了过来。
Read More ~

Neo常用命令 二

UNIQUE约束 避免重复时使用(姓名、身份证不能重复) 旧版本 create constraint on (n:Lianhuachi) assert n.name is unique 新版本 如果再执行上面的语法时报错,说明你使用的是新版本的Neo4j,变更了命令语法。 create constraint uniq_name for (n:Lianhuachi) require n.name is unique 在加了唯一索引后,可以看到,在添加重复数据时会报错: 删除唯一约束 drop constraint uniq_name 可以看到,在删除之后再进行添加,是可以添加进去的。 create (n:Lianhuachi {name: "我是野农", "field": "test"}) return n match (n:Lianhuachi {name: "我是野农""}) return n distinct去重 和SQL一致,对内容进行去重 match (n:Lianhuachi) return distinct(n.name)
Read More ~

Spring AI

前言 当一开始听说Spring-AI项目时是很懵的,什么?Spring开始训练模型了?不应该啊,Java还能卷模型赛道了吗?... 打开官网了解了下,才知道原来是缝合怪: Spring AI is an application framework for AI engineering. Its goal is to apply to the AI domain Spring ecosystem design principles such as portability and modular design and promote using POJOs as the building blocks of an application to the AI domain. 官网地址:https://spring.io/projects/spring-ai 换言之就是虽然Java不能卷算法,但是我可以提供一套封装来让你们调用模型提供的API服务,众所周知,我的抽象封装能力还是很强的。你看你们现在这么多厂商提供服务,不得需要一个统一的门面来减少接入成本嘛... 官网列举了目前国外主流的平台: Chat Models OpenAI Azure Open AI Amazon Bedrock Cohere's Command AI21 Labs' Jurassic-2 Meta's LLama 2 Amazon's Titan Google Vertex AI Palm Google Gemini HuggingFace - access thousands of models, including those from Meta such as Llama2 Ollama - run AI models on your local machine MistralAI Text-to-image Models OpenAI with DALL-E StabilityAI Transcription (audio to text) Models OpenAI ... 数不胜数,更何况还有很多国内大厂开源的模型。 所以Spring提供了一套统一的封装门面,其他的厂商也可以基于门面来实现自己的Client,比如Alibaba就接入了com.alibaba.cloud.ai.tongyi.chat.TongYiChatClient。 这就引申出了本博客,记录了下我接入Spring-Ai(OpenAI)、Spring-Ai-Alibaba(TongYi)的过程。 接入Spring-Ai Spring-Ai的接入相对简单,因为最新的start.spring.io已经维护了OpenAI的依赖包,在IDEA使用Spring-Initializr即可完成初始化。 完成Gradle的初始化之后,得到了一个标准的Spring项目,只需要做一个简单的配置: spring.application.name=spring-ai-demo # 生成结果多样性参数,值在0~2之间,值越大越随机越小越固定,但就算为0也会有随机性 spring.ai.openai.chat.temperature=0.7 spring.ai.chat.client.enabled=true # 如果你需要代理的话 spring.ai.openai.base-url=https://api.xty.app # 填写自己的key spring.ai.openai.api-key=${OPENAI_API_KEY} # 填写你需要使用的模型(也可以使用时代码指定) spring.ai.openai.chat.options.model=gpt-3.5-turbo 接下来只需要编写一个Java的控制器,来接收HTTP请求,就可以完成对OpenAI的对话。 /** * @author imyzt * @date 2024/06/19 * @description AI 入口 */ @RestController @RequestMapping public class AiController { @Resource private ChatClient chatClient; @GetMapping("/ai/chat") Map<String, Object> chat(@RequestParam String question) { ChatClient.ChatClientRequest.CallPromptResponseSpec call = chatClient.prompt(new Prompt(question)).call(); return Map.of("question", question, "answer", call.chatResponse()); } } 整体接入还是比较简单的,但是这里踩了一个坑,不知道是我引入的版本比较新还是什么缘故,它的ChatClient Bean 竟然没有自动注册!所以我还手动注册了一个Bean,代码如下: @Bean public ChatClient chatClient(@Autowired OpenAiChatModel openAiChatModel) { return ChatClient.builder(openAiChatModel).build(); } 代码 https://github.com/imyzt/learning-technology-code/tree/master/dive-in-springboot-projects/spring-ai-demo/spring-ai-demo Spring-Ai-Alibaba OpenAI由于API-KEY的费用蛮高,虽然完成了代码的接入,但是最终我还是没有购买它的API...所以又看了国内的厂商,目前主要是Alibaba完成了Spring-Ai的接入,整体接入其实也很简单,建议首先看一遍官网的例子,接下来可以跟着步骤走一下试试。 创建项目,引入依赖 首先完成一个普通SpringBootWeb项目创建,然后引入Alibaba-Ai的依赖 <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-ai</artifactId> <exclusions> <exclusion> <artifactId>spring-ai-core</artifactId> <groupId>org.springframework.ai</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.springboot.ai</groupId> <artifactId>spring-ai-core</artifactId> <version>1.0.3</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2023.0.1.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 上面的内容,我把无关紧要的依赖去除了,只保留了核心部分,其中有一个关键点是exclusions了spring-ai-core,因为Alibaba引入的版本太老了。改为自己重新引入最新的版本,其他的和官方文档无差别。 配置 spring: application: name: spring-ai-alibaba-demo cloud: ai: tongyi: api-key: ${TONGYI_KEY} images: enabled: true chat: enabled: true 编写控制器 @Slf4j @RestController @RequestMapping public class AiController { @Resource private ChatClient chatClient; @Resource private ImageClient imageClient; @GetMapping("/ai/chat") public String chat(@RequestParam String question) { ChatResponse call = chatClient.call(new Prompt(question)); return call.getResult().getOutput().getContent(); } @GetMapping("/ai/aigc") public String aigc(@RequestParam String question) { ImageResponse call = imageClient.call(new ImagePrompt(question)); return call.getResult().getOutput().getUrl(); } } 完成上面的步骤,基本就完成了接入,在postman上面输入地址,就可以进行测试了。因为通义不仅有chat,还可以文生图,所以我完成下演示: 文生文 (质量不予置评) 文生图 Spring-Ai-Alibaba还提供了一些示例,在他们的官方Github上,可以参考。 官方还提供了一个简单的HTML来进行页面展示,也可以自己跑一下看看,最终效果如下: 代码 https://github.com/imyzt/learning-technology-code/tree/master/dive-in-springboot-projects/spring-ai-alibaba-demo/spring-ai-alibaba-demo 参考博客 阿里也出手了!Spring CloudAlibaba AI问世了 AI框架之Spring AI与Spring Cloud Alibaba AI使用讲解
Read More ~

Java New Future CDS

什么是CDS?CDS即 Class-Data Sharing,类数据共享功能,该功能可以减少Java应用程序的启动时间和内存占用。 类数据共享功能有助于减少多个Java虚拟机之间的启动时间和内存占用,从JDK12开始,默认的CDS归档文件和JDK二进制文件预先打包,我是用的JDK为OpenJDK OpenJDK 64-Bit Server VM Zulu17.42+19-CA (build 17.0.7+7-LTS, mixed mode, sharing),是支持CDS的。 使用 训练应用程序 首先初始化一个标准的SpringBoot应用,使用 SpringBoot-3.3.0 + Java17 ├── pom.xml ├── src │   ├── main │   │   ├── java │   │   │   └── top │   │   │   └── imyzt │   │   │   └── learning │   │   │   └── cds │   │   │   └── Java12NewFuturesCdsApplication.java @SpringBootApplication public class Java12NewFuturesCdsApplication { public static void main(String[] args) { SpringApplication.run(Java12NewFuturesCdsApplication.class, args); } } 将其执行Maven打包成jar文件 ➜ mvn package -DskipTests=true 执行训练命令 ➜ cd target ➜ target ✗ java -Djarmode=tools -jar java12-new-futures-cds-0.0.1-SNAPSHOT.jar extract --destination application ➜ cd application ➜ application ✗ java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefresh -jar java12-new-futures-cds-0.0.1-SNAPSHOT.jar 训练完成后,application目录下,生成了一系列文件: ➜ application git:(master) ✗ tree . ├── application.jsa ├── java12-new-futures-cds-0.0.1-SNAPSHOT.jar └── lib ├── jackson-annotations-2.17.1.jar ├── jackson-core-2.17.1.jar ├── jackson-databind-2.17.1.jar ├── jackson-datatype-jdk8-2.17.1.jar ├── jackson-datatype-jsr310-2.17.1.jar ├── jackson-module-parameter-names-2.17.1.jar ├── jakarta.annotation-api-2.1.1.jar ├── jul-to-slf4j-2.0.13.jar ├── log4j-api-2.23.1.jar ├── log4j-to-slf4j-2.23.1.jar ├── logback-classic-1.5.6.jar ├── logback-core-1.5.6.jar ├── micrometer-commons-1.13.0.jar ├── micrometer-observation-1.13.0.jar ├── slf4j-api-2.0.13.jar ├── snakeyaml-2.2.jar ├── spring-aop-6.1.8.jar ├── spring-beans-6.1.8.jar ├── spring-boot-3.3.0.jar ├── spring-boot-autoconfigure-3.3.0.jar ├── spring-boot-jarmode-tools-3.3.0.jar ├── spring-context-6.1.8.jar ├── spring-core-6.1.8.jar ├── spring-expression-6.1.8.jar ├── spring-jcl-6.1.8.jar ├── spring-web-6.1.8.jar ├── spring-webmvc-6.1.8.jar ├── tomcat-embed-core-10.1.24.jar ├── tomcat-embed-el-10.1.24.jar └── tomcat-embed-websocket-10.1.24.jar 1 directory, 32 files 使用训练的缓存,在启动应用程序时,补充-XX:SharedArchiveFile参数即可。 ➜ application ✗ java -XX:SharedArchiveFile=application.jsa -jar java12-new-futures-cds-0.0.1-SNAPSHOT.jar 启动日志: Started Java12NewFuturesCdsApplication in 2.262 seconds (process running for 2.805) 对比不使用CDS缓存的启动日志: Started Java12NewFuturesCdsApplication in 4.464 seconds (process running for 5.341) 可以看出来,尽管只是一个空项目,但是相同配置情况下启动速度都有倍增。 结语 从上面的测试可以看出来,项目的启动速度是有成本的效率增长的,但同时也存在弊端,就是每次应用程序发生变更时,需要冲洗进行jsa文件的训练。 不管是native-jar还是CDS,都是Java在云原生时代解决应用启动过程慢的探索,在应用的自动扩容领域,还是有不少的应用场景。 参考 Spring Boot 3.3.0 新特性| 使用 CDS 优化启动时间 CDS即Class-Data Sharing
Read More ~

Spring 异步初始化

在Spring6.2版本后,支持了异步初始化。 什么是异步初始化?见名知意,就是将Spring项目的初始化过程中的Bean通过异步加载的方式提高启动速度。 在业务系统中通常启动不会特别耗时,但也可以将系统启动过程中需要耗时初始化的Bean通过异步将其并行初始化,节省部分启动时间。本文作为学习Why哥文章之后的简单使用记录。 模拟初始化Bean耗时 ABean @Slf4j public class TestABean { @SneakyThrows public TestABean() { log.info("A Bean开始初始化"); TimeUnit.SECONDS.sleep(5); log.info("A Bean初始化完成"); } } BBean @Slf4j public class TestBBean { @SneakyThrows public TestBBean() { log.info("B Bean开始初始化"); TimeUnit.SECONDS.sleep(6); log.info("B Bean初始化完成"); } } 配置类 @Configuration public class RegisterConfiguration { @Bean public TestABean testABean() { return new TestABean(); } @Bean public TestBBean testBBean() { return new TestBBean(); } } 启动类 @Slf4j @ComponentScan("top.imyzt.learning.spring.startup") public class Application { public static void main(String[] args) { StopWatch stopWatch = new StopWatch("Spring启动"); stopWatch.start(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Application.class); stopWatch.stop(); System.out.println(stopWatch.prettyPrint()); } } 启动日志 21:39:33.658 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testABean' 21:39:33.663 [main] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean开始初始化 21:39:38.667 [main] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean初始化完成 21:39:38.669 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testBBean' 21:39:38.670 [main] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean开始初始化 21:39:44.673 [main] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean初始化完成 StopWatch 'Spring启动': 11.555947229 seconds ------------------------------------------ Seconds % Task name ------------------------------------------ 11.55594723 100% 小结 可以看到,在常规情况下,A和B Bean是串行初始化的,整个初始化耗时11.5s。 异步初始化 在Spring6.2版本中,@Bean注解引入了一个新的属性:bootstrap,默认Bean.Bootstrap.DEFAULT时为串行初始化,当指定为Bean.Bootstrap.BACKGROUND时,Spring会尝试异步初始化该Bean,但是需要配置一个名为bootstrapExecutor的线程池,用作异步初始化时所需的线程。 只需要将配置类稍作修改,就可以将指定的Bean进行异步初始化: @Configuration public class RegisterConfiguration { @Bean(bootstrap = Bean.Bootstrap.BACKGROUND) public TestABean testABean() { return new TestABean(); } @Bean(bootstrap = Bean.Bootstrap.BACKGROUND) public TestBBean testBBean() { return new TestBBean(); } @Bean() public Executor bootstrapExecutor() { return new ThreadPoolExecutor(2, 10, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1024)); } } 然后我们再查看启动效果: 21:53:49.023 [pool-1-thread-1] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testABean' 21:53:49.024 [pool-1-thread-1] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean开始初始化 21:53:49.025 [pool-1-thread-2] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'testBBean' 21:53:49.026 [pool-1-thread-2] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean开始初始化 21:53:54.029 [pool-1-thread-1] INFO top.imyzt.learning.spring.startup.core.TestABean - A Bean初始化完成 21:53:55.031 [pool-1-thread-2] INFO top.imyzt.learning.spring.startup.core.TestBBean - B Bean初始化完成 StopWatch 'Spring启动': 6.740055683 seconds ----------------------------------------- Seconds % Task name ----------------------------------------- 6.740055683 100% 可以看到启动时间从11.5s降为6.7s,效果十分显著,在部分需要依赖外部或已知需要耗时初始化的Bean,可以通过此方法进行优化。“我可以不用,你不能没有”。 参考 13年过去了,Spring官方竟然真的支持Bean的异步初始化了! 博客对应代码
Read More ~

非静态内部类持有外部类的引用

在分析内存泄漏领域的问题时,时常会提到的就是非静态内部类持有外部类的引用导致的内存泄漏,因为内部类持有了外部类的引用,导致垃圾回收器无法回收外部类对象,久而久之就会存在内存泄漏的隐患。 我们在使用非静态内部类的时候,并不是说我们不手动编码引用外部类对象属性就不存在引用关系,实际上这层关系是通过隐式引用来实现的。 静态内部类持有外部类的引用 我们写一个非静态内部类对象示例: /** * @author imyzt * @date 2024/05/13 * @description 非静态内部类,匿名持有外部类的引用 */ public class NonStaticInnerClazz { private final String name; public NonStaticInnerClazz(String name) { this.name = name; } public class Inner { public void print() { System.out.println(name); } } } 可以看到javac之后的代码中,第九行方法形参将外部类NonStaticInnerClazz传入给了内部类的this.this$0属性进行引用。 非静态内部类无法引用外部类的属性 而非静态内部类则没有这种情况。 /** * @author imyzt * @date 2024/05/13 * @description 静态内部类,无法引用外部类的属性 */ public class StaticInnerClazz { private final String name; public StaticInnerClazz(String name) { this.name = name; } public static class Inner2 { public void print2() { // Non-static field 'name' cannot be referenced from a static context // System.out.println(name); } } } 其他非静态内部类引用的情况 而其他非静态内部类的情况也有类似的问题: 编写测试类: /** * @author imyzt * @date 2024/05/13 * @description 描述信息 */ public class NonStaticFieldClazz { // 成员变量-匿名内部类的非static实例 private Inner3 inner3_1 = new Inner3() { private Integer field_111; }; // 成员变量-非静态内部类的非static实例 private Inner3 inner3_2 = new Inner3(); public void print() { // 局部变量-匿名内部类的非static实例 Inner3 inner3_3 = new Inner3() { private Integer field_333; }; // 局部变量-非静态内部类的非static实例 Inner3 inner3_4 = new Inner3(); } public class Inner3 { } } 1. 成员变量-匿名内部类的非static实例 class NonStaticFieldClazz$1 extends NonStaticFieldClazz.Inner3 { private Integer field_111; NonStaticFieldClazz$1(NonStaticFieldClazz var1) { super(var1); this.this$0 = var1; } } 2. 局部变量-匿名内部类的非static实例 class NonStaticFieldClazz$2 extends NonStaticFieldClazz.Inner3 { private Integer field_333; NonStaticFieldClazz$2(NonStaticFieldClazz var1) { super(var1); this.this$0 = var1; } } 3. 局部变量和成员变量的非static内部类的非static实例 public class NonStaticFieldClazz$Inner3 { public NonStaticFieldClazz$Inner3(NonStaticFieldClazz var1) { this.this$0 = var1; } } 参考: 内存泄漏-内部类持有外部类引用
Read More ~

Neo4j 常用命令 一

WHERE 可以类似于SQL的写法来实现,首先通过match将节点查询出来(n,m),然后通过WHERE将数据 match (n:Lianhuachi),(m:Lianhuachi) where n.name = '去种田的向凹凸' and m.name = "向云朵" return id(n),n.name,id(m),m.name match (n:Lianhuachi {name: "去种田的向凹凸"}),(m:Lianhuachi {name: "向云朵"}) return id(n),n.name,id(m),m.name DELETE-删除节点 删除节点(如果节点存在关系时,是无法删除的) match (n: Person {name: "xxx"}) delete n 删除关系 match (n: Person {name: "小红"})-[r]->(m: Person {name: "小明"}) delete r REMOVE-删除属性 首先,在student和person标签创建1一个节点 create (n:student:person {name: "张三", age: 18}) return n 查询节点 MATCH (n:person) where n.name = "张三" RETURN n LIMIT 25 MATCH (n:student) RETURN n LIMIT 25 删除标签中的节点 MATCH (n:person) where n.name = "张三" remove n 删除标签中节点的属性 删除前,age=18 MATCH (n:student) where n.name = "张三" return n 删除后,age无了 MATCH (n:student) where n.name = "张三" remove n.age return n 修改标签中节点的属性 修改后,age回来了 MATCH (n:student) where n.name = "张三" set n.age=18 return n 给榜首增加一个属性 match (n:Lianhuachi {name: "我是野农"}) set n.title = '虾米榜榜首' return n ORDER BY - 排序 补充测试数据 create (n:student {name: "张三", age: 18}),(m:student {name: "李四", age: 22}) return n,m 排序 match (n:student) return id(n), n.name,n.age order by n.age desc id(n) n.name n.age 120 "李四" 22 119 "张三" 18 NULL - NULL属性 查询name属性不为空的节点 match (n:student) where n.name is not null return n.name,n.label n.name n.label "张三" null "李四" null 查询sex属性为空的节点 match (n:student) where n.sex is null return n.name,n.label n.name n.label "张三" null "李四" null 给张三设置sex属性,注意where在前,set在后,和MySQL等相反 match (n:student) where n.name='张三' set n.sex='男' return n.name,n.sex,n.age 查询结果:match (n:student) return n IN - 查询多个 查询name等于张三或李四的人: match (n:student) where n.name in ["张三", "李四"] return n 索引 官方文档 旧版本 创建索引: create index on :节点 (属性) 删除索引: drop index on :节点(属性) 新版本(5或更高版本) 创建索引: 缺省索引名称: CREATE INDEX [index_name(可缺省)] FOR (n:person) ON (n.name) 指定索引名称: create index index_test for (n:person) on (n.name) Added 1 index, completed after 992 ms. 查询所有索引: show indexes id name state populationPercent type entityType labelsOrTypes properties indexProvider owningConstraint lastRead readCount 1 "index_343aff4e" "ONLINE" 100 "LOOKUP" "NODE" null null "token-lookup-1.0" null "2024-05-18T13:26:33.878000000Z" 571 3 "index_a302cc54" "ONLINE" 100 "RANGE" "NODE" ["person"] ["name"] "range-1.0" null null 0 2 "index_f7700477" "ONLINE" 100 "LOOKUP" "RELATIONSHIP" null null "token-lookup-1.0" null "2024-05-10T00:48:00.395000000Z" 67 删除索引: drop index index_a302cc54 Removed 1 index, completed after 5 ms.
Read More ~

使用Neo4j建立莲花池人物关系图(核心人物,简版)

最近关注了沅陵的莲花池,整个村子都是钓鱼佬,人均up主,基本都是沾亲带故的,错综复杂,恰逢正在学习Neo4j,可以借此机会将人物关系通过图谱的方式呈现出来。 人物标签创建 首先将人物关系建立出来,利用create来创建效率实在太低了 ,所以决定采用数据导入的方式。 点击查看莲花池人物详情 去种田的向凹凸 小白兔的胡萝卜甜(冉甜) 向云朵 我是野农 画燕儿 向鹿鸣 路人阿丙X 大明星(冉蜜) 向偶然 荷塘星星 向尘俊 向星言 多肉葡萄肉多多 白叔 播音哥 三叔 丹宝 守山人阿亮 导入数据: load csv from "file:///莲花池人物.csv" as line create (:Lianhuachi {name: line[0]}) 最终效果图: MATCH (n:Lianhuachi) RETURN n LIMIT 25 人物关系标签创建 莲花池人物关系(简版) 小白兔的胡萝卜甜(冉甜),丈夫,去种田的向凹凸 去种田的向凹凸,妻子,小白兔的胡萝卜甜(冉甜) 小白兔的胡萝卜甜(冉甜),女儿,向云朵 去种田的向凹凸,女儿,向云朵 画燕儿,丈夫,我是野农 我是野农,妻子,画燕儿 画燕儿,儿子,向鹿鸣 我是野农,儿子,向鹿鸣 大明星(冉蜜),丈夫,路人阿丙X 路人阿丙X,妻子,大明星(冉蜜) 大明星(冉蜜),女儿,向偶然 路人阿丙X,女儿,向偶然 多肉葡萄肉多多,丈夫,荷塘星星 荷塘星星,妻子,多肉葡萄肉多多 荷塘星星,儿子,向尘俊 多肉葡萄肉多多,儿子,向尘俊 荷塘星星,女儿,向星言 多肉葡萄肉多多,女儿,向星言 向星言,亲哥,向尘俊 向尘俊,亲妹,向星言 荷塘星星,父亲,三叔 三叔,儿子,荷塘星星 荷塘星星,表哥,守山人阿亮 守山人阿亮,表弟,荷塘星星 我是野农,亲弟,路人阿丙X 路人阿丙X,亲哥,我是野农 我是野农,钓友,去种田的向凹凸 去种田的向凹凸,钓友,我是野农 去种田的向凹凸,同村,荷塘星星 我是野农,钓友,荷塘星星 荷塘星星,钓友,我是野农 去种田的向凹凸,同村,荷塘星星 导入数据: load csv from "file:///莲花池人物关系.csv" as line create (n:LianhuachiRelation {from: line[0], relation: line[1], to: line[2]}) return n.from, n.relation, n.to 最终效果: MATCH (n:LianhuachiRelation) RETURN n.from, n.relation, n.to 借助人物关系标签,创建人物关系 match (f:Lianhuachi),(r:LianhuachiRelation),(t:Lianhuachi) where f.name=r.from and t.name=r.to create (f)-[rr:莲花池人物关系 {relation: r.relation}]->(t) return f.name, rr.relation, t.name 最终效果: MATCH p=()-[r:莲花池人物关系]->() RETURN p LIMIT 25 查询人物关系 match (n:Lianhuachi {name: "向云朵"}),(m:LianhuachiRelation) where m.from='向云朵' return n.name, m.relation, m.to 在现有的基础上,补充人物关系 match (n:Lianhuachi{name:"大明星(冉蜜)"}),(m:Lianhuachi{name:"小白兔的胡萝卜甜(冉甜)"}) create (n)-[r:`莲花池人物关系`{relation:"亲姐"}]->(m) match (n:Lianhuachi{name:"大明星(冉蜜)"}),(m:Lianhuachi{name:"小白兔的胡萝卜甜(冉甜)"}) create (m)-[r:`莲花池人物关系`{relation:"亲妹"}]->(n) 删除重建人物关系 MATCH (n:Lianhuachi {name:"去种田的向凹凸"})-[r:`莲花池人物关系` {relation: "钓友"}]->(m:Lianhuachi {name:"我是野农"}) delete r MATCH (n:Lianhuachi {name:"我是野农"})-[r:`莲花池人物关系` {relation: "钓友"}]->(m:Lianhuachi {name:"去种田的向凹凸"}) delete r MATCH (n:Lianhuachi {name:"去种田的向凹凸"}),(m:Lianhuachi {name:"我是野农"}) create (n)-[r2:`莲花池人物关系` {relation: "表哥"}]->(m) create (m)-[r3:`莲花池人物关系` {relation: "表弟"}]->(n) return r2.relation,r3.relation
Read More ~