简体   繁体   English

Spring Data Neo4j:按LocalDate过滤不起作用

[英]Spring Data Neo4j: filter by LocalDate doesn't work

My small Neo4j playground application (based on Spring Boot 2, Spring Data Neo4j and the embedded driver) is a small note-keeping software. 我的小型Neo4j游乐场应用程序(基于Spring Boot 2,Spring Data Neo4j和嵌入式驱动程序)是一个小型的记事软件。 The users can filter their notes by creation date. 用户可以按创建日期过滤其笔记。 To get a better understanding of Cypher I wrote the Cypher query using SDN's @Query ( NoteRepo.findByDay(day) ). 为了更好地了解Cypher,我使用SDN的@QueryNoteRepo.findByDay(day) )编写了Cypher查询。

However I can't get the filtering working. 但是我无法进行过滤。 I'm confused that the LocalDate was transformed into a map (last line of the console output). 我对LocalDate转换为地图(控制台输出的最后一行)感到困惑。 In the previous query ( NoteRepo.findBySnoozeUntil(day) , using SDN's repo query keywords) everything was fine and the day was transformed to a ISO 8601 date. 在上一个查询( NoteRepo.findBySnoozeUntil(day) ,使用SDN的repo查询关键字)中,一切都很好,并且该day已转换为ISO 8601日期。

Can somebody please point out what's wrong with it and fix it? 有人可以指出它的问题并进行修复吗?

Note.java 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 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 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);
    }
}

console output of the Spring Boot application (truncated to the Neo4j queries and System.out.println) Spring Boot应用程序的控制台输出(被截断为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 = []

repro project: Spring Initializr Project repro项目: Spring Initializr项目

add this to build.gradle : 添加到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'
}

and add the classes above. 并添加上面的类。

At the moment the derived queriers (one like List<Note> findBySnoozedUntil(LocalDate day); ) are handled differently than the ones annotated with @Query using the embedded driver. 目前,派生查询器(类似于List<Note> findBySnoozedUntil(LocalDate day); )的处理方式与使用嵌入式驱动程序使用@Query注释的@Query器不同。

The embedded driver currently maps all parameters onto strings. 嵌入式驱动程序当前将所有参数映射到字符串上。 To two this, it uses unconditionally an instance of Jacksons ObjectMapper. 对于这两个,它无条件地使用Jacksons ObjectMapper的实例。

We need several steps to fix this. 我们需要几个步骤来解决此问题。

  1. Teach ObjectMapper to use a reasonable format. 教导ObjectMapper使用合理的格式。 That's easy, you're on Boot, so please add this starter compile('org.springframework.boot:spring-boot-starter-json') , it brings a lot of useful Jackson modules. 这很容易,您使用的是Boot,因此请添加此启动程序compile('org.springframework.boot:spring-boot-starter-json') ,它带来了许多有用的Jackson模块。 If you intend to write a web application and already have spring-boot-starter-web or spring-boot-starter-webflux , you can omit the JSON starter, those modules bring it. 如果您打算编写一个Web应用程序,并且已经有了spring-boot-starter-webspring-boot-starter-webflux ,则可以省略JSON starter,这些模块会将其引入。

  2. Register the included Java 8 Jackson modules with OGMs Embedded Objectmapper and disable writing dates as timestamps, ie in your App.class: 使用OGMs嵌入式Objectmapper注册随附的Java 8 Jackson模块 ,并禁用将日期作为时间戳写入,即在您的App.class中:

final ObjectMapper ogmObjectMapper = org.neo4j.ogm.config.ObjectMapperFactory.objectMapper();
ogmObjectMapper.registerModule(new JavaTimeModule());
ogmObjectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  1. As the embedded driver uses this config to map the value as String, you sadly have to adapt the query: 由于嵌入式驱动程序使用此配置将值映射为String,因此您不得不调整查询:
@Query("MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n")
List<Note> findByDay(LocalDate day);

Related output is now 现在是相关输出

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]

Edit: The same effect would be seen using Bolt against an external database as well, solution is currently the same. 编辑:对外部数据库使用Bolt也会看到相同的效果,解决方案当前是相同的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM