Spring Data Neo4j:按 LocalDate 过滤不起作用
Spring Data Neo4j: filter by LocalDate doesn't work
我的小型 Neo4j playground 应用程序(基于 Spring Boot 2、Spring Data Neo4j 和嵌入式驱动程序)是一个小型笔记软件。用户可以按创建日期过滤他们的笔记。为了更好地理解 Cypher,我使用 SDN 的 @Query
(NoteRepo.findByDay(day)
).
编写了 Cypher 查询
但是我无法使过滤工作。我很困惑 LocalDate 被转换成地图(控制台输出的最后一行)。在之前的查询中(NoteRepo.findBySnoozeUntil(day)
,使用 SDN 的 repo 查询关键字)一切正常,day
被转换为 ISO 8601 日期。
有人可以指出它有什么问题并修复它吗?
Note.java
@NodeEntity
class Note {
@Id
@GeneratedValue
private Long id;
private String content;
private LocalDateTime created;
private LocalDate snoozedUntil;
// constructors, getters, setters, ... omitted
}
NoteRepo.java
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import java.time.LocalDate;
import java.util.List;
interface NoteRepo extends Neo4jRepository<Note, Long> {
// FIXME find all notes created on a specific day
// currently returns an empty list
@Query("MATCH (n: Note) WHERE date(datetime(n.created)) = {0} RETURN n")
List<Note> findByDay(LocalDate day);
List<Note> findBySnoozeUntil(LocalDate day);
}
App.java
package com.example.neo4jquerywithlocaldate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.time.LocalDate;
import java.time.LocalDateTime;
@SpringBootApplication
public class App {
public static void main(String[] args) {
var ctx = SpringApplication.run(App.class, args);
var repo = ctx.getBean(NoteRepo.class);
var tomorrow = LocalDate.now().plusDays(1);
repo.save(new Note("learn neo4j", LocalDateTime.now(), tomorrow));
var notesForTomorrow = repo.findBySnoozeUntil(tomorrow);
System.out.println("notes snoozed until tomorrow = " + notesForTomorrow);
var todaysNotes = repo.findByDay(LocalDate.now());
System.out.println("today's notes = " + todaysNotes);
}
}
Spring 启动应用程序的控制台输出(截断为 Neo4j 查询和 System.out.println)
UNWIND {rows} as row CREATE (n:`Note`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-1, props={snoozeUntil=2018-09-21, created=2018-09-20T19:38:54.732260, content=learn neo4j}}]}
MATCH (n:`Note`) WHERE n.`snoozeUntil` = { `snoozeUntil_0` } WITH n RETURN n, ID(n) with params {snoozeUntil_0=2018-09-21}
< notes for tomorrow = [Note{id=0, content='learn neo4j', created=2018-09-20T19:38:54.732260}]
MATCH (n: Note) WHERE date(datetime(n.created)) = {0} RETURN n with params {0={year=2018, month=SEPTEMBER, monthValue=9, dayOfMonth=20, chronology={id=ISO, calendarType=iso8601}, dayOfWeek=THURSDAY, era=CE, dayOfYear=263, leapYear=false}}
< today's notes = []
重现项目:Spring Initializr Project
将此添加到 build.gradle
:
ext['neo4j-ogm.version'] = '3.1.3'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
runtimeOnly 'org.neo4j:neo4j:3.4.7'
runtimeOnly 'org.neo4j:neo4j-ogm-embedded-driver:3.1.3'
}
并添加上面的类。
目前派生查询器(如 List<Note> findBySnoozedUntil(LocalDate day);
)的处理方式不同于使用嵌入式驱动程序使用 @Query
注释的查询器。
嵌入式驱动程序当前将所有参数映射到字符串。为此,它无条件地使用 Jacksons ObjectMapper 的一个实例。
我们需要几个步骤来解决这个问题。
教ObjectMapper使用合理的格式。这很简单,你正在启动,所以请添加这个 starter compile('org.springframework.boot:spring-boot-starter-json')
,它带来了很多有用的 Jackson 模块。如果你打算编写一个 web 应用程序并且已经有 spring-boot-starter-web
或 spring-boot-starter-webflux
,你可以省略 JSON starter,那些模块自带。
向 OGMs Embedded Objectmapper 注册包含的 Java 8 Jackson modules 并禁用将日期写为时间戳,即在您的 App.class:
final ObjectMapper ogmObjectMapper = org.neo4j.ogm.config.ObjectMapperFactory.objectMapper();
ogmObjectMapper.registerModule(new JavaTimeModule());
ogmObjectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
- 由于嵌入式驱动程序使用此配置将值映射为字符串,遗憾的是您必须调整查询:
@Query("MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n")
List<Note> findByDay(LocalDate day);
相关输出现在
2018-09-24 10:50:53.313 INFO 2860 --- [ main] o.n.o.d.e.request.EmbeddedRequest : Request: MATCH (n:`Note`) WHERE n.`snoozedUntil` = { `snoozedUntil_0` } WITH n RETURN n, ID(n) with params {snoozedUntil_0=2018-09-25}
notes snoozed until tomorrow = [com.example.demo.Note@2420e962]
2018-09-24 10:50:53.522 INFO 2860 --- [ main] o.n.o.d.e.request.EmbeddedRequest : Request: MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n with params {0=2018-09-24}
today's notes = [com.example.demo.Note@363d3958]
编辑: 对外部数据库使用 Bolt 也会看到同样的效果,解决方案目前是相同的。
我的小型 Neo4j playground 应用程序(基于 Spring Boot 2、Spring Data Neo4j 和嵌入式驱动程序)是一个小型笔记软件。用户可以按创建日期过滤他们的笔记。为了更好地理解 Cypher,我使用 SDN 的 @Query
(NoteRepo.findByDay(day)
).
但是我无法使过滤工作。我很困惑 LocalDate 被转换成地图(控制台输出的最后一行)。在之前的查询中(NoteRepo.findBySnoozeUntil(day)
,使用 SDN 的 repo 查询关键字)一切正常,day
被转换为 ISO 8601 日期。
有人可以指出它有什么问题并修复它吗?
Note.java
@NodeEntity
class Note {
@Id
@GeneratedValue
private Long id;
private String content;
private LocalDateTime created;
private LocalDate snoozedUntil;
// constructors, getters, setters, ... omitted
}
NoteRepo.java
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import java.time.LocalDate;
import java.util.List;
interface NoteRepo extends Neo4jRepository<Note, Long> {
// FIXME find all notes created on a specific day
// currently returns an empty list
@Query("MATCH (n: Note) WHERE date(datetime(n.created)) = {0} RETURN n")
List<Note> findByDay(LocalDate day);
List<Note> findBySnoozeUntil(LocalDate day);
}
App.java
package com.example.neo4jquerywithlocaldate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.time.LocalDate;
import java.time.LocalDateTime;
@SpringBootApplication
public class App {
public static void main(String[] args) {
var ctx = SpringApplication.run(App.class, args);
var repo = ctx.getBean(NoteRepo.class);
var tomorrow = LocalDate.now().plusDays(1);
repo.save(new Note("learn neo4j", LocalDateTime.now(), tomorrow));
var notesForTomorrow = repo.findBySnoozeUntil(tomorrow);
System.out.println("notes snoozed until tomorrow = " + notesForTomorrow);
var todaysNotes = repo.findByDay(LocalDate.now());
System.out.println("today's notes = " + todaysNotes);
}
}
Spring 启动应用程序的控制台输出(截断为 Neo4j 查询和 System.out.println)
UNWIND {rows} as row CREATE (n:`Note`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-1, props={snoozeUntil=2018-09-21, created=2018-09-20T19:38:54.732260, content=learn neo4j}}]}
MATCH (n:`Note`) WHERE n.`snoozeUntil` = { `snoozeUntil_0` } WITH n RETURN n, ID(n) with params {snoozeUntil_0=2018-09-21}
< notes for tomorrow = [Note{id=0, content='learn neo4j', created=2018-09-20T19:38:54.732260}]
MATCH (n: Note) WHERE date(datetime(n.created)) = {0} RETURN n with params {0={year=2018, month=SEPTEMBER, monthValue=9, dayOfMonth=20, chronology={id=ISO, calendarType=iso8601}, dayOfWeek=THURSDAY, era=CE, dayOfYear=263, leapYear=false}}
< today's notes = []
重现项目:Spring Initializr Project
将此添加到 build.gradle
:
ext['neo4j-ogm.version'] = '3.1.3'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
runtimeOnly 'org.neo4j:neo4j:3.4.7'
runtimeOnly 'org.neo4j:neo4j-ogm-embedded-driver:3.1.3'
}
并添加上面的类。
目前派生查询器(如 List<Note> findBySnoozedUntil(LocalDate day);
)的处理方式不同于使用嵌入式驱动程序使用 @Query
注释的查询器。
嵌入式驱动程序当前将所有参数映射到字符串。为此,它无条件地使用 Jacksons ObjectMapper 的一个实例。
我们需要几个步骤来解决这个问题。
教ObjectMapper使用合理的格式。这很简单,你正在启动,所以请添加这个 starter
compile('org.springframework.boot:spring-boot-starter-json')
,它带来了很多有用的 Jackson 模块。如果你打算编写一个 web 应用程序并且已经有spring-boot-starter-web
或spring-boot-starter-webflux
,你可以省略 JSON starter,那些模块自带。向 OGMs Embedded Objectmapper 注册包含的 Java 8 Jackson modules 并禁用将日期写为时间戳,即在您的 App.class:
final ObjectMapper ogmObjectMapper = org.neo4j.ogm.config.ObjectMapperFactory.objectMapper();
ogmObjectMapper.registerModule(new JavaTimeModule());
ogmObjectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
- 由于嵌入式驱动程序使用此配置将值映射为字符串,遗憾的是您必须调整查询:
@Query("MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n")
List<Note> findByDay(LocalDate day);
相关输出现在
2018-09-24 10:50:53.313 INFO 2860 --- [ main] o.n.o.d.e.request.EmbeddedRequest : Request: MATCH (n:`Note`) WHERE n.`snoozedUntil` = { `snoozedUntil_0` } WITH n RETURN n, ID(n) with params {snoozedUntil_0=2018-09-25}
notes snoozed until tomorrow = [com.example.demo.Note@2420e962]
2018-09-24 10:50:53.522 INFO 2860 --- [ main] o.n.o.d.e.request.EmbeddedRequest : Request: MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n with params {0=2018-09-24}
today's notes = [com.example.demo.Note@363d3958]
编辑: 对外部数据库使用 Bolt 也会看到同样的效果,解决方案目前是相同的。