How to add multi field constraints?


#1

Is see how to add @isUnique constraint to a single field, but what if I want uniqueness dependent on the relation, what I normally use a multi-field database constraint for

ALTER TABLE 'posts' ADD UNIQUE 'unique_index'('author_id', 'title');

Basically I want the title unique per author, not globally unique.

type Author @model {
  id: ID! @isUnique
  name: String! @isUnique

  posts: [Post!]! @relation(name: "AuthorPosts")
}

type Post @model {
  id: ID! @isUnique
  title: String! @isUniqueForAuthorPosts
  author: [Author!]! @relation(name: "AuthorPosts")
}

#2

That’s not supported (yet).


#3

Can I suggest that this is a priority?

Here is a simple example where it is critical

As per the Author/Post types mentioned by the OP, if I have a Province/City model, with things as they stand, it is not possible to require uniqueness on the City name, eg ‘Jonestown’, which is required to be unique within the Province, but a second entry of ‘Jonestown’ in another Province would be rejected by the @isUnique directive.

In other words, it is not currently possible to properly build a Location-aware production app… or is there a workaround? The following will not work IRL!

type Province @model {
  id: ID! @isUnique
  name: String! @isUnique

  cities: [City!]! @relation(name: "CitiesInProvince")
}

type City @model {
  id: ID! @isUnique
  name: String! @isUnique
  province: Province! @relation(name: "ProvinceHasCity")
}

In fact, this is such a fundamental part of relationship theory, that I can’t believe it’s not implemented :open_mouth:


#4

I assume this is the ticket to track supporting that https://github.com/graphcool/framework/issues/171


#5

Yes, please join the discussion for the feature request @mmrobins shared :slightly_smiling_face:


#6

FWIW here’s the function I wrote (not actually doing author and post, but changed names from our business specific models) to do this kind of constraint checking for now. Obviously it would be much preferred to have this multiple field constraint functionality built in to the type DSL, but for our small project this is enough to get us going.

graphcool.yml

functions:
  validatePostTitleUnique:
    type: operationBefore
    operation: Post.create
    handler:
      code: src/validatePostTitleUnique.js

src/validatePostTitleUnique.js

'use latest'

const { fromEvent } = require('graphcool-lib')

module.exports = (event) => {
  console.log(event)
  const api = fromEvent(event).api('simple/v1')
  const authorId = event['data']['authorId']
  const newPostTitle = event['data']['title']
  const query = `
    query {
      Author(id: "${authorId}") {
        name
        posts(filter: {title: "${newPostTitle}"}) {
          id
        }
      }
    }
  `
  return api.request(query)
    .then(data => {
      if (data["Author"]["posts"].length >= 1) {
        authorName = data["Author"]["name"]
        return {
          error: `${newPostTitle} already exists for Author ${authorName}`
        }
      } else {
        return {
          data: event.data
        }
      }
    })
    .catch(err => {
      return {
        error: err
      }
    })
}

I pretty much copied the outline for this from Accessing data stored in the graph.cool DB from inside functions


#7

There is a simple workaround. please check this answer https://stackoverflow.com/a/56984957/4578951


#8

Yes, but there is a work around https://stackoverflow.com/a/56984957/4578951