【问题标题】:Neo4j problems with type comparisons in "WHERE"Neo4j 在“WHERE”中的类型比较问题
【发布时间】:2016-11-03 14:40:01
【问题描述】:

通过 java 执行 Neo4j 查询时出现以下错误:

org.neo4j.graphdb.QueryExecutionException: Don't know how to compare that. Left: "0" (String); Right: 0 (Long)
at org.neo4j.kernel.impl.query.QueryExecutionKernelException.asUserException(QueryExecutionKernelException.java:35)
at org.neo4j.cypher.internal.javacompat.ExecutionResult.converted(ExecutionResult.java:399)
at org.neo4j.cypher.internal.javacompat.ExecutionResult.hasNext(ExecutionResult.java:232)
at main.java.com.bag.server.database.Neo4jDatabaseAccess.readObject(Neo4jDatabaseAccess.java:172)
at main.java.com.bag.server.TestServer.handleNodeRead(TestServer.java:259)
at main.java.com.bag.server.TestServer.appExecuteUnordered(TestServer.java:153)
at bftsmart.tom.server.defaultservices.DefaultRecoverable.executeUnordered(DefaultRecoverable.java:417)
at bftsmart.tom.ServiceReplica.receiveReadonlyMessage(ServiceReplica.java:214)
at bftsmart.tom.core.DeliveryThread.deliverUnordered(DeliveryThread.java:289)
at bftsmart.tom.core.TOMLayer.requestReceived(TOMLayer.java:290)
at bftsmart.communication.client.netty.NettyClientServerCommunicationSystemServerSide.channelRead0(NettyClientServerCommunicationSystemServerSide.java:184)
at bftsmart.communication.client.netty.NettyClientServerCommunicationSystemServerSide.channelRead0(NettyClientServerCommunicationSystemServerSide.java:61)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278)
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:277)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:264)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:962)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:485)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:399)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:371)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)

但我确信我在数据库中将“0”作为字符串。 我在查询中将其作为字符串输入:

String.format(" WHERE r.%s <= %s OR n.%s IS NULL", "snapshotId", Long.toString(0), "snapshotId")

【问题讨论】:

    标签: java neo4j cypher


    【解决方案1】:

    你使用的是字符串修饰而不是参数,所以你会丢失类型信息(因为它只是把它全部放在一个字符串中)。如果您在 Cypher 中键入不带引号的字符“0”,它将被解释为Int。因此,而不是您当前的查询,

    WHERE r.snapshotId <= 0 or n.snapshotId IS NULL
    

    你真的想让它说

    WHERE r.snapshotId <= '0' or n.snapshotId IS NULL
    

    在 '0' 两边加上引号,所以它把它当作一个字符串。

    然而,真正的解决方案是编写一个查询来代替字符串修改:

    WHERE r.snapshotId <= {zero_string} or n.snapshotId IS NULL
    

    然后传递一个设置为Long.toString(0) 的参数zero_string。这样,驱动程序将在打包、解包和解释数据时为您处理类型。

    编辑:或者如果您真的需要属性名称也是动态的,也可以将其作为参数传递:

    WHERE r[{zero_param}] <= {zero_string} or n[{zero_param}] IS NULL
    

    更新:您可以通过传入 Map 并进行一些迭代工作来修改它以适用于多个键值对。天真的做法是这样的:

    WHERE ALL(k IN KEYS({map_param}) WHERE r[k] <= {map_param}[k] OR n[k] IS NULL)
    

    但这在任何规模上都可能非常慢,因为我认为查询规划器无法优化它。在应用此过滤器之前,请尝试根据其他条件缩小您的 r 匹配范围。

    【讨论】:

    • 谢谢。有没有办法在查询中获取带有键的属性变量列表以从数据库中检索它?我有一个 映射,我想将它们与数据库匹配,但对象可能是 long、int、double、string 等。
    • 我的哈希图中通常会有大约 3-4 个属性。还是会比在单独的 where 中添加每个键慢吗?
    • 老实说,您必须PROFILE 两者才能找到性能更好的...在生成查询字符串时显式分解过滤器可以加快执行速度,但也会阻止查询计划如果您使用不同的 Map (并因此生成不同的查询字符串),则从缓存中提取。我会从幼稚的解决方案开始,如果它变得太慢,我只会担心优化。
    【解决方案2】:

    format 语句生成的字符串是:

    " WHERE r.snapshotId <= 0 OR n.snapshotId IS NULL"
    

    就 Cypher 而言,Long.toString(0) 值仍然会导致输出数字 0,因为字面值没有被引号括起来。

    要解决此问题,您必须确保被比较的值具有兼容的类型。

    1。作为字符串比较

    这样做的一种方法是确保将数值表示为字符串(通过引用它):

    String.format(" WHERE r.%s <= '%s' OR n.%s IS NULL", "snapshotId", 0, "snapshotId")
    

    2。数值比较

    但是,如果您希望比较的结果在算术上有意义,那么使用字符串比较通常是没有意义的。例如,(".1" &lt;= "0")true,而 (.1 &lt;= 0)false。因此,对于在算术上有意义的比较,您可能应该将 r.snapshotId 转换为浮点数或整数。

    例如:

    String.format(" WHERE TOFLOAT(r.%s) <= %s OR n.%s IS NULL", "snapshotId", 0, "snapshotId")
    

    使用参数

    最后,正如@ToreEschliman 建议的那样,您应该传递参数而不是使用字符串修饰。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多