prisma-engines icon indicating copy to clipboard operation
prisma-engines copied to clipboard

feat: support referencing field in filters

Open Weakky opened this issue 3 years ago β€’ 0 comments

TODO BEFORE MERGING

Overview

fixes https://github.com/prisma/prisma/issues/14664

  • Supports referencing fields in (not unique) filters (meaning in *WhereInputs)
  • Adds a fieldReference preview feature

Refactors

  • Refactored the MongoDB filter renderer
  • Refactored the Scalar filter extractor

Unrelated bug fixes

  • fixes https://github.com/prisma/prisma/issues/14664 (I know it should've ideally been fixed in a separate PR, but excluding MongoDB from all the insensitive field reference tests felt too painful... It's a 10-line fix anyway, you can find it in my own review down below)
  • fixes https://github.com/prisma/prisma-engines/issues/3132 (a14f56b)
  • fixes https://github.com/prisma/prisma-engines/issues/3133 (a14f56b)
  • fixes https://github.com/prisma/prisma/issues/14663 (6e15e95)
  • fixes https://github.com/prisma/prisma-engines/issues/3115 (4a205a1)

Notes

  • We've disabled referencing field on alphanumeric json filters for MySQL & MariaDB
  • If you traverse a relation in a filter, you can currently only reference fields from that relation and not from the parent model.
  • Referenced fields in having filters need to be put in the by argument like scalars are. @SevInf Ideally, this should be type-safe.

Schema Additions

Given this datamodel

model User {
 id Int @id
}
schema Query {
  findManyUser(where: UserWhereInput)
}

input UserWhereInput {
  id: IntFilter
}

// Notice the additional FieldRefInputs
input IntFilter {
  equals: Int | IntFieldRefInput
  gt: Int | IntFieldRefInput
  gte: Int | IntFieldRefInput
  lt: Int | IntFieldRefInput
  lte: Int | IntFieldRefInput
  in: Int[] | IntListFieldRefInput
  notIn: Int[] | IntListFieldRefInput
}

input IntFieldRefInput {
  _ref: String
}

input IntListFieldRefInput {
  _ref: String
}

DMMF Additions

Given this datamodel

model User {
  id Int @id
}

See how IntFilter is affected with the addition of the field ref inputπŸ‘‡

        {
          "name": "IntFilter",
          "constraints": {
            "maxNumFields": null,
            "minNumFields": null
          },
          "fields": [
            {
              "name": "equals",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": false
                },
                {
                  "type": "IntFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "in",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": true
                },
                {
                  "type": "IntListFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "notIn",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": true
                },
                {
                  "type": "IntListFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "lt",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": false
                },
                {
                  "type": "IntFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "lte",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": false
                },
                {
                  "type": "IntFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "gt",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": false
                },
                {
                  "type": "IntFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "gte",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": false
                },
                {
                  "type": "IntFieldRefInput",
                  "namespace": "prisma",
                  "location": "fieldRefTypes",
                  "isList": false
                }
              ]
            },
            {
              "name": "not",
              "isRequired": false,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "Int",
                  "location": "scalar",
                  "isList": false
                },
                {
                  "type": "NestedIntFilter",
                  "namespace": "prisma",
                  "location": "inputObjectTypes",
                  "isList": false
                }
              ]
            }
          ]
        }

Additionally, we have a new "location" called "fieldRefTypes" where all field ref input types are located πŸ‘‡

{
  "datamodel": {
    "enums": [],
    "models": [],
    "types": []
  },
  "schema": {
    "inputObjectTypes": {},
    "outputObjectTypes": {},
    "enumTypes": {},
    "fieldRefTypes": {
      "prisma": [
        {
          "name": "IntFieldRefInput",
          "allowTypes": [
            {
              "type": "Int",
              "location": "scalar",
              "isList": false
            }
          ],
          "fields": [
            {
              "name": "_ref",
              "isRequired": true,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "String",
                  "location": "scalar",
                  "isList": false
                }
              ]
            }
          ]
        },
        {
          "name": "IntListFieldRefInput",
          "allowTypes": [
            {
              "type": "Int",
              "location": "scalar",
              "isList": true
            }
          ],
          "fields": [
            {
              "name": "_ref",
              "isRequired": true,
              "isNullable": false,
              "inputTypes": [
                {
                  "type": "String",
                  "location": "scalar",
                  "isList": false
                }
              ]
            }
          ]
        }
      ]
    }
  },
  "mappings": {}
}

Finally, all <Model>WhereInput get a new meta.source field. eg:

{
  "name": "UserWhereInput",
  "meta": {
    // source designates the model or composite type from which this type was derived. It's required to help the TS client pass the right generic. See notion doc & proposal #4 to understand more why this is needed.
    "source": "User"
  },
  "constraints": {
    "maxNumFields": null,
    "minNumFields": null
  },
  "fields": []
}

Weakky avatar Jul 28 '22 09:07 Weakky