Skip to content
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

fix: Error if given the wrong side of a one-one relationship #795

Merged
merged 7 commits into from
Sep 16, 2022

Conversation

AndrewSisley
Copy link
Contributor

Relevant issue(s)

Resolves #513

Description

Errors nicely if given the wrong side of a one-one relationship (on write).

Specify the platform(s) on which this was tested:

  • Debian Linux

@AndrewSisley AndrewSisley added bug Something isn't working area/query Related to the query component area/collections Related to the collections system action/no-benchmark Skips the action that runs the benchmark. labels Sep 14, 2022
@AndrewSisley AndrewSisley added this to the DefraDB v0.3.1 milestone Sep 14, 2022
@AndrewSisley AndrewSisley requested a review from a team September 14, 2022 15:24
@AndrewSisley AndrewSisley self-assigned this Sep 14, 2022
@AndrewSisley AndrewSisley changed the title fix: Error nicely if given the wrong side of a one-one relationship fix: Error if given the wrong side of a one-one relationship Sep 14, 2022
@AndrewSisley AndrewSisley force-pushed the sisley/fix/I513-one-one-wrong-side branch 2 times, most recently from 7ff3df2 to 914cb92 Compare September 14, 2022 18:34
client/errors.go Outdated
@@ -26,3 +25,7 @@ var (
ErrInvalidUpdater = errors.New("The updater of a document is of invalid type")
ErrInvalidDeleteTarget = errors.New("The target document to delete is of invalid type")
)

func ErrFieldNotExist(name string) error {
Copy link
Member

Choose a reason for hiding this comment

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

praise: I Like this!

db/collection.go Outdated
}

// isFieldRelationIdD returns true if the given field is the id field backing a relationship.
func (c *collection) isFieldRelationIdD(fieldDescription *client.FieldDescription) bool {
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: better name? does last character D stand for Description?

Copy link
Contributor Author

@AndrewSisley AndrewSisley Sep 15, 2022

Choose a reason for hiding this comment

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

Yeah I have no idea, I am way to used to having these overloads use the same name, makes this kind of thing much cleaner IMO. Is not just Go that does this, I think Rust has this restriction too and I wonder why. What would you guys do?

Copy link
Collaborator

Choose a reason for hiding this comment

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

you could alternatively do this:

func(fd *FieldDescription) isFieldRelationID(c *collection) bool {...}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lol, that is hacky pile of... 😆

Copy link
Collaborator

Choose a reason for hiding this comment

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

hey... you wanted an alternative to D... there it is ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😁

Copy link
Member

@shahzadlone shahzadlone Sep 16, 2022

Choose a reason for hiding this comment

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

I don't have any good idea atm, as long as the name is meaningful haha, and we don't have to guess what D might mean for example.

Copy link
Member

Choose a reason for hiding this comment

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

Would suggest:

IsFieldRelationID
and
IsDescriptionRelationID

its not the best as they are both about fields, but one takes a string as a field, and the other a description as a field.

Really dont like the trailing D atm 😂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IsDescriptionRelationID

Description of what? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tweaked the names slightly pre-merge, they are now a variant of John's suggestion

Comment on lines 19 to 32
var schema = (`
type book {
name: String
rating: Float
author: author
}

type author {
name: String
age: Int
verified: Boolean
published: book @primary
}
`)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
var schema = (`
type book {
name: String
rating: Float
author: author
}
type author {
name: String
age: Int
verified: Boolean
published: book @primary
}
`)
var schema = `
type book {
name: String
rating: Float
author: author
}
type author {
name: String
age: Int
verified: Boolean
published: book @primary
}
`

Copy link
Contributor Author

@AndrewSisley AndrewSisley Sep 15, 2022

Choose a reason for hiding this comment

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

Ops lol, will also change where I pasted this from 😆 Thanks

  • RM extra brackets

Copy link
Collaborator

@fredcarle fredcarle left a comment

Choose a reason for hiding this comment

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

Looking good. Just a few comments.

db/collection.go Outdated
if !valid {
return false
}
if fd2.RelationType > 0 && fd2.RelationType&client.Relation_Type_Primary <= 0 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

thought: This looks like black magic. Can you add a comment to help with understanding?

I feel like fd2.RelationType&client.Relation_Type_Primary would be 0 or 128. Why <= and not ==?

Maybe:

// check that it's a relationship but not the primary.

Copy link
Contributor Author

@AndrewSisley AndrewSisley Sep 15, 2022

Choose a reason for hiding this comment

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

cheers, I lazily copy-pasted this, I'll do it properly and document. I also thought I changed the variable names in here (from the WIP code), but looks like fd2 is still here lol

  • ==
  • rename

db/collection.go Outdated
@@ -914,6 +919,31 @@ func (c *collection) tryGetSchemaFieldID(fieldName string) (uint32, bool) {
return uint32(0), false
}

// isFieldRelationId returns true if the given field is the id field backing a relationship.
// Will return false if the field is not found at all.
Copy link
Collaborator

Choose a reason for hiding this comment

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

nitpick: Will also return false.... It could lead to thinking that it returns false only if the field is not found.

Copy link
Contributor Author

@AndrewSisley AndrewSisley Sep 15, 2022

Choose a reason for hiding this comment

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

will look at tweaking the wording

  • comment update

db/collection.go Outdated
}

// isFieldRelationIdD returns true if the given field is the id field backing a relationship.
func (c *collection) isFieldRelationIdD(fieldDescription *client.FieldDescription) bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

you could alternatively do this:

func(fd *FieldDescription) isFieldRelationID(c *collection) bool {...}

db/collection.go Outdated
@@ -914,6 +919,31 @@ func (c *collection) tryGetSchemaFieldID(fieldName string) (uint32, bool) {
return uint32(0), false
}

// isFieldRelationId returns true if the given field is the id field backing a relationship.
// Will return false if the field is not found at all.
func (c *collection) isFieldRelationId(fieldName string) bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah will leave as is :) We have so many like this, and I disagree with that anyway (makes it harder to spot the start/end of words)

Copy link
Member

Choose a reason for hiding this comment

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

nitpick: isFieldRelationID according to https://github.com/golang/go/wiki/CodeReviewComments#initialisms

LOL some of these go wiki code review comments crack me up, is there even any advantage of following minor subjective rules like those?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Consistency across the Go ecosystem.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This is why it's much easier, in most cases, to dive into new Go codebases than it is for most other languages.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is why it's much easier, in most cases, to dive into new Go codebases than it is for most other languages.

I really disagree with that :) It is easier because it is a simple language, similar to JavaScript, with C-like typing. If they wanted to make things easier to pick up they should improve-the/write-some (non-blog-based) documentation instead of worrying about nitpicks like this

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think having guidelines like these help a lot. I find the Go documentation to be quite good. Much better than Rust (at least since the last time I looked at it) and incomparable to pretty much all the others. JavaScript is an easy language but there is no real unified guidelines on how to write it and it shows (same with python, ruby, etc.). Javascript codebases are usually as similar to one another as Quebec French and Belgium French dialects are to one another. Might as well learn a new language every time (I'm exaggerating here but barely). Following guideline also helps having a less developper preference discussions and less disparities between the different sections of the codebase. It makes the project approachable to newcomers and makes it easier to maintain open source projects.

It's important to look at this not from our own experienced developper's perspective, but from that of new devs that are potentially learning their first language. That's why I nitpick on those thing and I find it important to do so.

Copy link
Member

Choose a reason for hiding this comment

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

I think theres many aspects about Go that make it easy to jump into a new codebases. Eg, less features that are more orthogonal to eachother, leading to fewer options to implement various patterns. However nitpicking Id vs ID isn't really one of them. Its good to be consistent, but I've never been perplexed when seeing one vs the other.

I think we should be consistent, and I personally much prefer ID over Id but at the moment, its not 100% consistent across the codebase, so it should be a separate issue if we want it to be.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I don't care that much about ID vs Id or others similar capitalization guidelines specifically. I just care about the guidelines in general and I think we should follow them as much as possible for consistency of the ecosystem. I like to point them out to give people a reminder that they exist (And they have a good reason to exist). If we decide collectively that we prefer something else then so be it. I won't lose sleep on it 🙂

@AndrewSisley AndrewSisley force-pushed the sisley/fix/I513-one-one-wrong-side branch 3 times, most recently from 2c0991b to b8d713c Compare September 16, 2022 16:46
@AndrewSisley AndrewSisley force-pushed the sisley/fix/I513-one-one-wrong-side branch from b8d713c to 5153e8c Compare September 16, 2022 18:39
@AndrewSisley AndrewSisley force-pushed the sisley/fix/I513-one-one-wrong-side branch from 5153e8c to dd287b8 Compare September 16, 2022 19:21
Copy link
Member

@jsimnz jsimnz left a comment

Choose a reason for hiding this comment

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

Suggesting an alternative approach:

At the moment, you add a IsFieldRelation check to both the save and applyMerge as they are the two ways we actually mutate state.

A (potentially) more succinct way would be to change the code that generates the SchemaDescription to not generate the <type>_id field we don't care about. Which would result in not needed to do any of these checks, as both the tryGetField and the GetField calls would return false and we already return errors for !exists -> return ErrFieldNotExist.

We can go with this route youve implemented, but it
A) Requires making sure that the IsFieldRealtion check is always present in any future new mutation function (at the moment we have two, would need to be added for applyPatch which is todo).
B) Leaves the unused field on the SchemaDescription

client/errors.go Outdated
Comment on lines 29 to 36
func ErrFieldNotExist(name string) error {
return errors.New("The given field does not exist", errors.NewKV("Name", name))
}
Copy link
Member

Choose a reason for hiding this comment

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

thought: Somewhat independant of this PR, We should make sure to establish a pattern for the codebase for "paramaterized" errors.

There is a consequence using a function like this as it won't allow a parent to use errors.Is(err, ErrFieldNotExist)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a consequence using a function like this as it won't allow a parent to use

Good point, would want to keep the string publicly available for that, might tweak pre merge

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed to allow for Is.

db/collection.go Outdated
@@ -914,6 +919,31 @@ func (c *collection) tryGetSchemaFieldID(fieldName string) (uint32, bool) {
return uint32(0), false
}

// isFieldRelationId returns true if the given field is the id field backing a relationship.
// Will return false if the field is not found at all.
func (c *collection) isFieldRelationId(fieldName string) bool {
Copy link
Member

Choose a reason for hiding this comment

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

I think theres many aspects about Go that make it easy to jump into a new codebases. Eg, less features that are more orthogonal to eachother, leading to fewer options to implement various patterns. However nitpicking Id vs ID isn't really one of them. Its good to be consistent, but I've never been perplexed when seeing one vs the other.

I think we should be consistent, and I personally much prefer ID over Id but at the moment, its not 100% consistent across the codebase, so it should be a separate issue if we want it to be.

db/collection.go Outdated
}

// isFieldRelationIdD returns true if the given field is the id field backing a relationship.
func (c *collection) isFieldRelationIdD(fieldDescription *client.FieldDescription) bool {
Copy link
Member

Choose a reason for hiding this comment

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

Would suggest:

IsFieldRelationID
and
IsDescriptionRelationID

its not the best as they are both about fields, but one takes a string as a field, and the other a description as a field.

Really dont like the trailing D atm 😂

@AndrewSisley
Copy link
Contributor Author

A (potentially) more succinct way would be to change the code that generates the SchemaDescription to not generate the _id field we don't care about.

Could be good, but I don't know what else that might impact, and if you really want that route it can go in for 0.4.0 (is 5:20pm on Friday of the release lol)

@jsimnz
Copy link
Member

jsimnz commented Sep 16, 2022

Could be good, but I don't know what else that might impact, and if you really want that route it can go in for 0.4.0 (is 5:20pm on Friday of the release lol)

LOL. Fair ;)

@jsimnz
Copy link
Member

jsimnz commented Sep 16, 2022

LGTM. Merge at your convenience based on whatever you wanted to change pre-merge.

@AndrewSisley AndrewSisley force-pushed the sisley/fix/I513-one-one-wrong-side branch from dd287b8 to 1b8af36 Compare September 16, 2022 21:35
@codecov
Copy link

codecov bot commented Sep 16, 2022

Codecov Report

Merging #795 (919ddb4) into develop (0fe9dd3) will increase coverage by 0.06%.
The diff coverage is 66.66%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #795      +/-   ##
===========================================
+ Coverage    59.61%   59.67%   +0.06%     
===========================================
  Files          154      155       +1     
  Lines        17257    17283      +26     
===========================================
+ Hits         10287    10313      +26     
  Misses        6044     6044              
  Partials       926      926              
Impacted Files Coverage Δ
client/document.go 64.72% <20.00%> (ø)
db/collection.go 65.99% <70.00%> (+0.07%) ⬆️
db/collection_update.go 56.48% <75.00%> (+1.00%) ⬆️
client/descriptions.go 84.37% <100.00%> (+1.04%) ⬆️
client/errors.go 100.00% <100.00%> (ø)
query/graphql/planner/update.go 75.00% <0.00%> (+3.40%) ⬆️

@AndrewSisley AndrewSisley merged commit f294f19 into develop Sep 16, 2022
@AndrewSisley AndrewSisley deleted the sisley/fix/I513-one-one-wrong-side branch September 16, 2022 21:48
shahzadlone pushed a commit to shahzadlone/defradb that referenced this pull request Feb 23, 2024
…etwork#795)

* Remove extra brackets

* Move collection update simple tests into dir

* Remove invalid and irrelevant test data

It is setting the author_id from the wrong side of the relationship, and is not relevant to the test anyway

* Include field name in missing field error

* Error if creating 1-1 from wrong side
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action/no-benchmark Skips the action that runs the benchmark. area/collections Related to the collections system area/query Related to the query component bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug: One directional joins.
4 participants