Skip to content

object style where input types#13

Open
olup wants to merge 6 commits into
vinpac:masterfrom
olup:master
Open

object style where input types#13
olup wants to merge 6 commits into
vinpac:masterfrom
olup:master

Conversation

@olup

@olup olup commented Jan 30, 2020

Copy link
Copy Markdown

This is a basic implementation of what I raised in my issue. Seems to work alright, but I did not check the tests.

@vinpac vinpac left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Hey, thanks for your pull request. It's great to have someone willing to help.

I wrote some reviews on things I think we could improve. I miss the checks to see how it goes with tests, but I imagine some of them probably broke since it's currently written on the old format. Could you help me updating the tests and making them pass with this new architecture?

CircleCi didn't run checks on this pull because it comes from a fork. I checked the option to do this on the project settings, so your next commit will trigger the checks. Yay!

Thanks again.

Comment thread src/entity-type-def-manager.ts Outdated
Comment on lines +43 to +44
const whereStringInputType = inputObjectType({
name: 'whereStringInputType',

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think StringFilter will be more clear.

const stringFilterInputType = inputObjectType({
  name: 'StringFilter',

Comment thread src/entity-type-def-manager.ts Outdated
Comment on lines +52 to +53
const whereIntInputType = inputObjectType({
name: 'whereIntInputType',

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think IntFilter will be more clear.

const intFilterInputType = inputObjectType({
  name: 'IntFilter',

@olup olup Jan 30, 2020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

can we add "Input" nontheless ?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Sure!

Comment thread src/entity-type-def-manager.ts Outdated
const whereIntInputType = inputObjectType({
name: 'whereIntInputType',
definition(t) {
t.int('eq')

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think equals makes more verbose queries.

{ post (where: { id: { equals: 1 } } ) }

Rather than

{ post (where: { id: { eq: 1 } } ) }

Comment thread src/entity-type-def-manager.ts Outdated
t.field(`${column.propertyName}_${singleOperandOperation}`, {
type: columnTypeName!,
t.field(column.propertyName, {
type: whereStringInputType!,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I didn't know we could pass the direct object to the type. To mantain a pattern for now. What do you think of including the inputObject at the src/plugin.ts:42?

  return [
    buildEntityFieldOutputMethod(manager),
    buildCRUDOutputMethod(manager),
    buildEntityOutputProperty(manager),
    buildEntityFieldsOutputMethod(manager),
    buildCRUDOutputProperty(manager),
    GraphQLDateTime,
    StringFilterInput,
    IntFilterInput,
  ]
Suggested change
type: whereStringInputType!,
type: 'StringFilter'

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ok, no problem, it's your lib. But generally speaking I prefer using the object directly as It boast better type support and is less error prone.

@olup olup Jan 30, 2020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Didn't get your last comment about plugin.ts ? Do you want to initiate the input types from this file ? (I actually don't know much about nexus-graphql plugin dev)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Yeah. Os this part we return an array defining the field creators and global types (e.g. GraphQLDateTime). Let's put StringFilter and IntFilter there for now so we don't have multiple implementations on how we are managing types. If later we find it better using the object directly, we migrate it.

I'm looking for reaching a stable version by the end of February.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not sure how to fix tests tho. I have to regenarate nexus types in the test forlder, and snapshot as well

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

nvm got it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Is your test suit supposed to pass as is ?

@vinpac vinpac Jan 30, 2020

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

No, you have to adapt the tests. Let me take one test as example.

it('handles simple operations', async () => {
    const result = await query(`
      query {
        users(where: {
-         age_gt: 35,
-         age_lt: 50
+ .       age: { gt: 35, lt: 50 }
        }) {
          id
          name
          age
        }
      }`)

    expect(result).toMatchObject({
      users: [
        {
          age: 40,
          id: expect.any(String),
          name: 'baz',
        },
      ],
    })
  })

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

No, I know 😉 ! I was refering to without my changes. Got almost all test running now, have a few things to work out yet tomorrow. Implementation wise, should I declare my input types in the plugin.ts file or set it up in another file like genericTypes.ts ? Because I also added an "id" input type (only equals and in) and more will come with adaptation of the sorting, and future relay-style pagination.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Wow! Nice work. For now, let's declare it on src/args/arg-where.

@olup olup force-pushed the master branch 2 times, most recently from a4343a0 to d99b9f8 Compare January 30, 2020 15:02
@olup

olup commented Jan 31, 2020 via email

Copy link
Copy Markdown
Author

@olup

olup commented Jan 31, 2020 via email

Copy link
Copy Markdown
Author

@olup

olup commented Feb 1, 2020

Copy link
Copy Markdown
Author

So, following on this, may I change the tests, for example the on that connect a user to a new post. We should be able to connect by name only if the name column on user is Unique ?

Or maybe, just set the name column on user as @Index({unique : true})

@olup

olup commented Feb 1, 2020

Copy link
Copy Markdown
Author

Ok, the pull request is good. I do not know why, one snapshot test doesn't seem to pass on the CI, but works fine on my end.

I had to comment out the query complexity test in complex.test.ts. I do not know how to fix it, maybe you can help me with this.

I apologize for such a big PR. I changed mainly those things :

  • turned to object-style arguments on where input for the find many query
  • turned to object-style arguments on orderBy input for the find many query
  • removed orderBy on find one query. To me, this made no sense at all, tell me if you agree.
  • added a spedial where argument on the find one query, that takes only fields that identifies the ressource as a single object, ie : ID and any @Index({unique : true}) column

Object style orderBy clause breaks choosing precedence of a multiple ordering query, as JS keys are not ordered (or at least not ordered as the coder types them). We can brainstorm a solution if you accept the PR.

Tell me what you think.

@olup

olup commented Feb 2, 2020

Copy link
Copy Markdown
Author

On the OrderBy front : why not have an "AND" or "THEN" option in the clause so that we can chose hierarchy of ordering ? Like this : orderBy : {age : ASC, AND : { name : DESC }}

@vinpac

vinpac commented Feb 3, 2020

Copy link
Copy Markdown
Owner

Hey @olup ! Nice pull request. I will have more time to review more closely later today or tomorrow. I loved the changes. I'm testing some cases to see if the orderBy and the now unique findOne fall short on some cases.

On the orderBy issue, I think it's important to have a form to control the order of ordering. If we can guarantee that { id: ASC, age: DESC } follows the key definition's order, for me, it's all good. What do you think? It didn't understand much the diference from this approach and the "AND" or "THEN" inside orderBy. Could you explain me?

Let's close on what we have now and discusss so the PR doesn't get too big and points to many directions, making it more dificult to merge. It's better if we divide other concerns and improvements on other PR's. Again, nice work on this PR it's a big help to this project.

@olup

olup commented Feb 3, 2020

Copy link
Copy Markdown
Author

Cheers !
So, to ellaborate on orderBy :
Javascript reorders object's keys in it's own way, so you have NO WAY to keep the order your user decided. If you look at nexus-prisma they don't even tackle multi columns sorting. They just don't care, and it's true that the use-case is pretty minimal.
But if you want to offer it nonetheless, you have to use some kind of organisational system. Either use an array like this [{name : ASC},{age : DESC}]
or have a special key (could be called AND ) that let user chain sortable options in order. Like this : {name : ASC, AND : {age : DESC}}. With this we can insure that first criterium is name and second is age

@olup

olup commented Feb 4, 2020

Copy link
Copy Markdown
Author

Should I add this to my PR ?

@vinpac

vinpac commented Feb 4, 2020

Copy link
Copy Markdown
Owner

Hmm, I feel like the object-style is a nice improvement to where, but for orderBy it seems that an array of strings fits better rather than an array of objects.

orderBy: [age_DESC, name_ASC]

insteadOf

orderBy: [{ age: DESC}, { name: ASC }]

An object-style here allows users to use

orderBy: [{ age: DESC, name: DESC }, { name: ASC }]

It feels strange to read this.

I was thinking about this and came up with an idea. I don't know how I feel about it yet.

order: {
  by: age,
  sort: ASC,
  then: {
    by: name,
    sort: DESC
  }
}

Another example in one line

{ order: { by: age, then: { by: name, sort: DESC } } } 
# Compared to
{ orderBy: [age_ASC, name_DESC] } 
1. { orderBy: [age_ASC, name_DESC, updatedAt_DESC] } 
2. { orderBy: [{age: ASC}, { name: DESC}, { updatedAt: DESC }] } 
3. { order: { by: age, sort: DESC, then: { by: name, sort: DESC, then: { by: updatedAt, sort: DESC } } } } 

# Django-style
"age,-name,-updatedAt"

What do you think?

It's more verbose, but can become a large object quickly. An array would do the same with less code. [age_DESC, name_ASC].

Let's keep the orderBy the same way it was and discuss it in another issue/PR? So we don't grow this PR.

I will review it today.

@olup

olup commented Feb 4, 2020

Copy link
Copy Markdown
Author

Ooops ! just published the AND clause. OK, I can revert it, but i really don't like having those double underscore properties. Ok, we can discuss it somewhere else.

@olup

olup commented Feb 4, 2020

Copy link
Copy Markdown
Author

ok, just reverted. Please be careful of the complex test, I had to comment out the last test, I need your input or correction on this.

@vinpac

vinpac commented Feb 4, 2020

Copy link
Copy Markdown
Owner

Yeah, those double underscores seem strange to me too, but I think this is will be a decision with win and losses. I think I've pointed fair points about query size on my previous response, but let's keep discussing it. I think we can reach an agreement about the right way to go with orderBy.

I reviewed the WhereUniqueInput and improved a little bit to include @Unique and @Column({ unique: true }) columns, but I am thinking about how can we cover multiple columns indexes.

For example:
We have the entity UserLikesPost that has a primary key id and one unique index that combines userId and postId. Thus we could find an unique instance by querying { id: <pk> } or { userId: <userId>, postId: <postId> }.

To enable this, we would have to declare a union input type for *WhereUniqueInput.

type UserLikesPostWhereUniqueInput =
  | UserLikesPostWhereUniqueInputPK
  | UserLikesPostWhereUniqueInput_UniqueSetUserIdAndPostId

type UserLikesPostWhereUniqueInput_UniqueSetUserIdAndPostId = {
  userId: Int!
  postId: Int!
}

Sadly union types on GraphQL are still an RFC (https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md).

I think the solution, for now, is checking if all columns are inside one unique index and make *WhereUniqueInput have all properties as required. Otherwise, we only provide the Primary key on *WhereUniqueInput.

Don't worry about this on your PR. The changes I'm working on also include the WhereUniqueInput. I was already implementing it so will be no problem. I will solve this when I merge.

@olup

olup commented Feb 4, 2020 via email

Copy link
Copy Markdown
Author

Comment on lines +386 to +391
const uniqueIndices = [
indices
.filter(index => index.isUnique)
.map(index => index.columns.map(column => column.propertyName)),
uniques.map(unique => unique.columns.map(column => column.propertyName)),
].flat(10)

@olup olup Feb 4, 2020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The comment is misleading : this allows for all unique indexes (even multi columns) AND @column({unique : true})

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.

2 participants