function computeCriteria (opts) {
    const where = opts.where || {}
    const criteria = []
    for (const key of Object.keys(where)) {
      if (key === 'or') {
        const orCriteria = []
        for (const orPart of where[key]) {
          const cret = computeCriteria({ where: orPart })
          orCriteria.push(sql`(${sql.join(cret, sql` AND `)})`)
        }
        criteria.push(sql`(${sql.join(orCriteria, sql` OR `)})`)
        continue
      }
      const value = where[key]
      const field = inputToFieldMap[key]
      if (!field) {
        throw new errors.UnknownFieldError(key)
      }
      for (const key of Object.keys(value)) {
        const operator = whereMap[key]
        /* istanbul ignore next */
        if (!operator) {
          // This should never happen
          throw new errors.UnsupportedWhereClauseError(JSON.stringify(where[key]))
        }
        const fieldWrap = fields[field]
        /* istanbul ignore next */
        if (fieldWrap.isArray) {
          if (operator === 'ANY') {
            criteria.push(sql`${value[key]} = ANY (${sql.ident(field)})`)
          } else if (operator === 'ALL') {
            criteria.push(sql`${value[key]} = ALL (${sql.ident(field)})`)
          } else if (operator === '@>') {
            criteria.push(sql`${sql.ident(field)} @> ${value[key]}`)
          } else if (operator === '<@') {
            criteria.push(sql`${sql.ident(field)} <@ ${value[key]}`)
          } else if (operator === '&&') {
            criteria.push(sql`${sql.ident(field)} && ${value[key]}`)
          } else {
            throw new errors.UnsupportedOperatorForArrayFieldError()
          }
        } else if (operator === '=' && value[key] === null) {
          criteria.push(sql`${sql.ident(field)} IS NULL`)
        } else if (operator === '<>' && value[key] === null) {
          criteria.push(sql`${sql.ident(field)} IS NOT NULL`)
        } else if (operator === 'LIKE' || operator === 'ILIKE') {
          let leftHand = sql.ident(field)
          // NOTE: cast fields AS CHAR(64) and TRIM the whitespaces
          // to prevent errors with fields different than VARCHAR & TEXT
          if (!['text', 'varchar'].includes(fieldWrap.sqlType)) {
            leftHand = sql`TRIM(CAST(${sql.ident(field)} AS CHAR(64)))`
          }
          const like = operator === 'LIKE' ? sql`LIKE` : queries.hasILIKE ? sql`ILIKE` : sql`LIKE`
          criteria.push(sql`${leftHand} ${like} ${value[key]}`)
        } else if (operator === 'ANY' || operator === 'ALL' || operator === '@>' || operator === '<@' || operator === '&&') {
          throw new errors.UnsupportedOperatorForNonArrayFieldError()
        } else {
          criteria.push(sql`${sql.ident(field)} ${sql.__dangerous__rawValue(operator)} ${computeCriteriaValue(fieldWrap, value[key])}`)
        }
      }
    }
    return criteria
  }
The 'field' can be a relational field which is undefined
in the inputToFieldMap
@see sql-graphql