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

feat: GraphQL upsert mutation #3075

Merged
merged 6 commits into from
Sep 27, 2024

Conversation

nasdf
Copy link
Member

@nasdf nasdf commented Sep 25, 2024

Relevant issue(s)

Resolves #783

Description

This PR adds a new upsert GraphQL mutation operation.

Tasks

  • I made sure the code is well commented, particularly hard-to-understand areas.
  • I made sure the repository-held documentation is changed accordingly.
  • I made sure the pull request title adheres to the conventional commit style (the subset used in the project can be found in tools/configs/chglog/config.yml).
  • I made sure to discuss its limitations such as threats to validity, vulnerability to mistake and misuse, robustness to invalidation of assumptions, resource requirements, ...

How has this been tested?

Added integration tests.

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

  • MacOS

@nasdf nasdf added feature New feature or request area/query Related to the query component labels Sep 25, 2024
@nasdf nasdf added this to the DefraDB v0.14 milestone Sep 25, 2024
@nasdf nasdf self-assigned this Sep 25, 2024
Copy link

codecov bot commented Sep 25, 2024

Codecov Report

Attention: Patch coverage is 81.31868% with 34 lines in your changes missing coverage. Please review.

Project coverage is 79.44%. Comparing base (54c99e7) to head (2f94014).
Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
internal/planner/upsert.go 69.77% 15 Missing and 11 partials ⚠️
internal/request/graphql/parser/mutation.go 89.47% 5 Missing and 3 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #3075      +/-   ##
===========================================
- Coverage    79.50%   79.44%   -0.06%     
===========================================
  Files          345      346       +1     
  Lines        26565    26705     +140     
===========================================
+ Hits         21118    21214      +96     
- Misses        3936     3963      +27     
- Partials      1511     1528      +17     
Flag Coverage Δ
all-tests 79.44% <81.32%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
client/request/mutation.go 100.00% <ø> (ø)
internal/planner/create.go 81.16% <100.00%> (ø)
internal/planner/errors.go 25.00% <ø> (ø)
internal/planner/explain.go 60.87% <ø> (ø)
internal/planner/mapper/mapper.go 88.83% <100.00%> (+0.01%) ⬆️
internal/planner/planner.go 83.39% <100.00%> (+0.23%) ⬆️
internal/planner/update.go 78.48% <100.00%> (-0.27%) ⬇️
internal/request/graphql/schema/generate.go 88.11% <100.00%> (+0.14%) ⬆️
internal/request/graphql/parser/mutation.go 91.07% <89.47%> (+5.16%) ⬆️
internal/planner/upsert.go 69.77% <69.77%> (ø)

... and 9 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 54c99e7...2f94014. Read the comment docs.

@nasdf nasdf requested a review from a team September 25, 2024 20:27
Copy link
Member

@shahzadlone shahzadlone left a comment

Choose a reason for hiding this comment

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

Wowza, super awesome stuff! Left some comments in first pass :)

internal/request/graphql/schema/descriptions.go Outdated Show resolved Hide resolved
tests/integration/explain/debug/upsert_test.go Outdated Show resolved Hide resolved
internal/planner/upsert.go Outdated Show resolved Hide resolved
Copy link
Contributor

@islamaliev islamaliev left a comment

Choose a reason for hiding this comment

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

Looks nice for far! Very useful feature. I have most suggestions.

@@ -0,0 +1,185 @@
// Copyright 2022 Democratized Data Foundation
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: 2024

Copy link
Member Author

Choose a reason for hiding this comment

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

done

updateInput map[string]any
doc *client.Document
isUpserting bool
results planNode
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: I find the name results a bit confusing for planNode. When reading methods' bodies I though it was a results objects of storage iterator.
I think a more common name would be source or plan

Copy link
Member Author

Choose a reason for hiding this comment

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

changed to source

createInput map[string]any
updateInput map[string]any
doc *client.Document
isUpserting bool
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: was also a bit puzzled why upsertNode has isUpserting prop. Maybe call it something like isInitialized? As a bonus you wouldn't need to initialize it to true

Copy link
Member Author

Choose a reason for hiding this comment

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

I copied it from the update node which has an isUpdating prop.

Comment on lines 42 to 43
switch next {
case true:
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: first time in my life I see this kind of switch :D
Maybe a classical if?

Copy link
Member

Choose a reason for hiding this comment

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

lol +1 for making into if

Copy link
Member Author

Choose a reason for hiding this comment

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

done

Comment on lines 108 to 112
doc, err := client.NewDocFromMap(n.createInput, n.collection.Definition())
if err != nil {
return err
}
n.doc = doc
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: this part is not needed when upserting. Maybe move it to "create" branch?

Copy link
Member Author

Choose a reason for hiding this comment

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

done

}

// Next only returns once.
func (n *upsertNode) Next() (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: this upsertNode looks (to some extend) as copy&paste of 2 nodes updateNode and createNode.
Did you consider making upsertNode detect if it's update or create op and (on the fly?) use one of those?

Copy link
Member Author

Choose a reason for hiding this comment

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

I did have that thought, but I think its worth the extra LOC to keep it separate incase we decide to extend or modify it.

testUtils.ExecuteTestCase(t, test)
}

func TestMutationUpsertSimple_WithUniqueCompositeIndexAndDuplicateUpdate_ReturnsError(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

praise: thanks for this test and for making clear test names.

Comment on lines +37 to +56
testUtils.Request{
Request: `mutation {
upsert_Users(
filter: {name: {_eq: "Bob"}},
create: {name: "Bob", age: 40},
update: {age: 40}
) {
name
age
}
}`,
Results: map[string]any{
"upsert_Users": []map[string]any{
{
"name": "Bob",
"age": int64(40),
},
},
},
},
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: I think it also makes sense to run a follow up query request to make it really fetches them from storage and not just as a response.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

Comment on lines 23 to +25
Input = "input"
CreateInput = "create"
UpdateInput = "update"
Copy link
Contributor

Choose a reason for hiding this comment

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

question: shouldn't "create" and "update" replace "input"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think keeping the input label for create and update mutations is fine.

Changing it to create_User(create: {name: "Bob"}) doesn't read as nice imo.

return false, err
}
if next {
return false, ErrUpsertMultipleDocuments
Copy link
Contributor

Choose a reason for hiding this comment

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

question: is there an inherent problemt with upserting multiple docs?

Copy link
Member Author

@nasdf nasdf Sep 26, 2024

Choose a reason for hiding this comment

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

There's nothing stopping us from supporting it in the future. It's there to stop users from doing something unexpected like updating all documents when they just wanted to upsert a single document.

If we do support it in the future we will want to add a variable to control the behavior: unique or all

Copy link
Contributor

@islamaliev islamaliev left a comment

Choose a reason for hiding this comment

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

Great job, Keenan!

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.

LGTM!

Copy link
Member

@shahzadlone shahzadlone left a comment

Choose a reason for hiding this comment

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

Thanks for doing the requested changes and answering all the comments =)

@nasdf nasdf merged commit 88972df into sourcenetwork:develop Sep 27, 2024
41 of 43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/query Related to the query component feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support upsert mutation
4 participants