Skip to content

Add support for spatial point values and distance comparisons #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 6, 2020

Conversation

Andy2003
Copy link
Collaborator

@Andy2003 Andy2003 commented Jul 16, 2020

This PR add support for spatials and distance comparisons.

Now you can rund queries like:

query {
  person(location:{longitude: 1, latitude: 2 }){
    name
    location {
      crs
      longitude
      latitude
      height
    }
  }
}

or

{
  person(filter:{ location_distance_gte: { distance: 3, point: {longitude: 1, latitude:2, height: 3}}}){
    name
  }
}

resolves #11

@Andy2003 Andy2003 requested a review from jexp July 16, 2020 09:41
@michael-simons
Copy link

FYI Cypher-DSL 2020.0.1 with your PR should be on central soonish.

<dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-cypher-dsl</artifactId>
        <version>2020.0.1</version>
</dependency>

if (op != null) {
return ExpressionPredicate(definedField.name, op, value, definedField)
if (definedField.type.isNeo4jType()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we extract the block into a method?

"$not$variable.${name.quote()} ${op.op} \$$paramName"
val appendField = if (nestedField != null) {
paramName += "_$nestedField"
".$nestedField"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

paramNames can't contain dots ?
is there a better way than modfiying the paramName?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they can, but it breaks the "style" so I've chosen to use underscore

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they can't, since neo4j will try to resolve everything behind the . as property of a scalar type

val condition = conditionField.op.conditionCreator(prop, parameter)
filterParams[parameter.name] = conditionField.queryField.value.toJavaValue()
where = conditionAdder(where, condition)
if (conditionField.fieldDefinition.type.isNeo4jType()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have a lot of places where larger chunks of code related to spatial / distance are now spread through the code-base like here, would be there any way of extracting them/putting them together so that the implementation doesn't leak into all these places?

} else {
val parameter = Cypher.parameter(variablePrefix + "_" + conditionField.queryField.name)
var name = conditionField.fieldDefinition.name
if (appendObjectFieldName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also more boolean conditions like this one that are passed through which make the code much harder to understand and maintain.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esp. as they only communicate the what but not the why and what does it mean.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would also be good if we found a way to co-locate these code-chunks for the different operations (mostly expressions) with the enums / expression types that were originally meant for keeping that functionality isolated.

queriedFields.remove(queryFieldName)?.let { (index, objectField) ->
Predicate(predicate, objectField, definedField, index)
queriedFields[queryFieldName]?.let { (index, objectField) ->
if (predicate.requireParam xor (objectField.value !is NullValue)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first time I've seen an XOR in real code. esp. xor with negations is kinda ouch

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:-)

Predicate(predicate, objectField, definedField, index)
queriedFields[queryFieldName]?.let { (index, objectField) ->
if (predicate.requireParam xor (objectField.value !is NullValue)) {
// if we got a value but the predicate requires none
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we turn the comment into code instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this before the xor but the condition was even uglier than the xor. Do you really don't like the xor so much.
In my opinion this line is even worse:

if ((predicate.requireParam && objectField.value is NullValue) || (!predicate.requireParam && objectField.value !is NullValue))

is Float -> v.toDouble(); is Int -> v.toLong(); else -> v
is Float -> v.toDouble()
is Int -> v.toLong()
is List<*> -> v.map { fixNumber(it) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably for Iterable/Sequence?

@@ -214,6 +229,42 @@ type _Neo4jLocalTime {
second: Int
}

type _Neo4jPoint {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can minimize that.

Either by splitting it into 2 point types.
And removing / limiting a bit of the capabilities (e.g. coordinate systems) ?

/cc @johnymontana

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks to match the point type used in neo4j-graphql.js, which is good. I don't think we want multiple GraphQL point types, and instead we should match the Neo4j database point type.

I think the issue @jexp is getting at here is that there are many optional fields on the point type. This stems from the fact that there aren't separate database types for 2D, or 3D - instead they are the same type. So to match that behavior the GraphQL type needs to have these optional fields. One thing that could be done in the future to simplify this is to use a GraphQL union input type (coming to the GraphQL spec soon) for the mutations and filters, which would have a LatLon, xyz, etc types wrapped up in a point union input type which I think would make it more clear what the various options for specifying a point type are.

In most cases I think users are interested in latitude, longitude with the geographic CRS, which is the default.

Copy link
Contributor

@jexp jexp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see if we can do anything about my comments.

Otherwise it's ok if we merge it but we need to make sure the code stays maintainable and we're not just slapping more if conditionals and booleans into all the places.

@Andy2003 Andy2003 requested a review from jexp August 5, 2020 17:31
Copy link
Contributor

@jexp jexp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks so much!

@jexp jexp merged commit 2ad5915 into neo4j-graphql:master Aug 6, 2020
@Andy2003 Andy2003 deleted the feature/GH11-support-spatial branch August 27, 2020 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

support spatial
4 participants