简体   繁体   English

Neo4j在“ WHERE”中进行类型比较的问题

[英]Neo4j problems with type comparisons in “WHERE”

I am getting the following error when executing my Neo4j queries through java: 通过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)

But I am sure that I have the "0" as a String inside the Database. 但是我可以确定数据库中的字符串为“ 0”。 And that I am entering it as a string in the query: 我在查询中将它作为字符串输入:

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

You're using string mangling instead of parameters, so you lose the type info (because it's just putting it all in a string). 您使用的是字符串修饰而不是参数,因此您丢失了类型信息(因为它只是将所有内容都放在了字符串中)。 If you type the character '0' without quotes in Cypher, it is going to be interpreted as an Int . 如果您在Cypher中键入不带引号的字符'0',它将被解释为Int So instead of your current query, 因此,除了您当前的查询,

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

you actually want it to say 你实际上想说

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

with the quotes around the '0', so it treats it as a string. 引号括在“ 0”周围,因此将其视为字符串。

The real solution, though, is instead of string mangling, write a query to say: 但是,真正的解决方案是代替字符串改写,而写一个查询说:

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

and then pass a parameter zero_string that is set to Long.toString(0) . 然后传递参数zero_string ,该参数设置为Long.toString(0) That way the driver will handle types for you while packing, unpacking, and interpreting the data. 这样,驱动程序将在打包,解包和解释数据时为您处理类型。

EDIT: Or if you really need the property name to be dynamic as well, pass it as a parameter too: 编辑:或如果您真的需要属性名称也是动态的,也将其作为参数传递:

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

UPDATE: You can modify this to work for multiple key-value pairs by passing in a Map and doing some iterative work. 更新:您可以通过传入Map并进行一些迭代的工作来修改它以适用于多个键/值对。 The naive way to do it would be like so: 天真的方法是这样的:

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

but this is likely to be very slow on any scale, as I don't think the query planner will be able to optimize it. 但这在任何规模上都可能非常慢,因为我认为查询计划程序无法对其进行优化。 Try to narrow down your r matches on some other criteria before you apply this filter. 在应用此过滤器之前,请尝试缩小其他条件下的r匹配范围。

The string generated by your format statement is: 您的format语句生成的字符串是:

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

The Long.toString(0) value still causes a numeric 0 to be output, as far as Cypher is concerned, since the literal value is not surrounded by quotes. 就Cypher而言, Long.toString(0)值仍会导致输出数字0 ,因为文字值没有用引号引起来。

To fix this, you have to make sure the values being compared have compatible types. 要解决此问题,必须确保要比较的值具有兼容的类型。

1. Comparing as strings 1.比较字符串

One way to do this is to make sure the numeric value is represented as a string (by quoting it): 一种方法是确保数字值表示为字符串(用引号引起来):

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

2. Comparing as numeric values 2.比较为数值

However, if you expect the result of the comparison to make sense arithmetically, it generally does not make sense to use a string comparison. 但是,如果您希望比较的结果在算术上有意义,则通常不使用字符串比较。 For example, (".1" <= "0") is true , whereas (.1 <= 0) is false . 例如, (".1" <= "0")true ,而(.1 <= 0)false Therefore, for comparisons that make sense arithmetically, you should probably convert r.snapshotId to either a float or an integer. 因此,对于在算术上有意义的比较,您可能应该将r.snapshotId转换为浮点数或整数。

For example: 例如:

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

Use parameters 使用参数

Finally, as @ToreEschliman suggested, you should be passing parameters instead of using string mangling. 最后,正如@ToreEschliman所建议的那样,您应该传递参数而不是使用字符串修饰。

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

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