mickhansen / graphql-sequelize

GraphQL & Relay for MySQL & Postgres via Sequelize
MIT License
1.9k stars 172 forks source link

TypeError: values.map is not a function #575

Closed negezor closed 6 years ago

negezor commented 6 years ago

What are you doing?

I wanted to implement a search through relay connections

/* models/user.mjs  */
import { Model, fn } from 'sequelize';

export default (sequelize, DataTypes) => {
    const getAttributes = () => ({
        id: {
            type: DataTypes.UUID,
            defaultValue: DataTypes.UUIDV4,
            primaryKey: true,
            allowNull: false
        },

        username: {
            type: DataTypes.STRING(40),
            allowNull: false
        }

        // Other fields
    });

    const getOptions = () => ({
        hooks: {}
    });

    return class User extends Model {
        static init(options = {}) {
            return super.init(getAttributes(), { ...options, ...getOptions() });
        }

        static associate(models) {}
    };
};

/* models/release.mjs */
import { Model, fn } from 'sequelize';

export default (sequelize, DataTypes) => {
    const getAttributes = () => ({
        id: {
            type: DataTypes.UUID,
            defaultValue: DataTypes.UUIDV4,
            primaryKey: true,
            allowNull: false
        },

        name: {
            type: DataTypes.STRING(150),
            allowNull: false
        },
        english: {
            type: DataTypes.STRING(150)
        },
        synonyms: {
            type: DataTypes.ARRAY(DataTypes.STRING(150)),
            allowNull: false,
            defaultValue: []
        },

        // Other fields
    });

    const getOptions = () => ({
        hooks: {}
    });

    return class Release extends Model {
        static init(options = {}) {
            return super.init(getAttributes(), { ...options, ...getOptions() });
        }

        static associate(models) {}
    };
};

/* resolvers/search.mjs */
import { Op } from 'sequelize';
import { relay } from 'graphql-sequelize';

import { Release, User } from '../models';

const { resolve: releaseConnection } = relay.sequelizeConnection({
    name: 'releases',
    target: Release,

    before(options, { query }, context) {
        options.where = {
            name: {
                [Op.iLike]: `%${query}%`
            },
            [Op.or]: [
                {
                    english: {
                        [Op.iLike]: `%${query}%`
                    }
                },
                {
                    synonyms: {
                        [Op.iLike]: `%${query}%`
                    }
                },
            ]
        };

        return options;
    }
});

const { resolve: userConnection } = relay.sequelizeConnection({
    name: 'users',
    target: User,

    before(options, { query }, context) {
        options.where = {
            username: {
                [Op.iLike]: `%${query}%`
            }
        };

        return options;
    }
});

export default {
    Query: {
        search(...args) {
            const [, { type }] = args;

            if (type === 'RELEASE') {
                return releaseConnection(...args);
            }

            if (type === 'USER') {
                return userConnection(...args);
            }

            throw new Error('Unsupported search type!');
        }
    }
};

Search scheme

enum SearchType {
    RELEASE
    USER
}

union SearchResultUnion = Release | User

type SearchEdge {
    node: SearchResultUnion
    cursor: String!
}

type SearchConnection {
    pageInfo: PageInfo!

    totalCount: Int!

    edges: [SearchEdge]
}

type Query {
    search(
        after: String,
        first: Int,

        before: String,
        last: Int,

        query: String!,
        type: SearchType!
    ): SearchConnection!
}

Query

query {
  search(query: "test", type: RELEASE) {
    edges {
      node {
        ... on Release {
          id
          name
          english
          synonyms
        }

        ... on User {
          id
          username
        }
      }
    }
  }
}

What do you expect to happen?

I expected that I would receive a relay connection from the list of search

What is actually happening?

I'm getting a connection for the user. But in releases I get an error

Search error TypeError: values.map is not a function
    at ARRAY._stringify (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\postgres\data-types.js:565:33)
    at ARRAY.stringify (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\data-types.js:32:17)
    at Object.escape (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:923:32)
    at Object._whereParseSingleValueObject (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2413:41)
    at Object.whereItemQuery (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2111:21)
    at Utils.getComplexKeys.forEach.prop (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:1974:25)
    at Array.forEach (<anonymous>)
    at Object.whereItemsQuery (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:1972:35)
    at value.map.item (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2170:30)
    at Array.map (<anonymous>)
    at Object._whereGroupBind (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2169:21)
    at Object.whereItemQuery (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2082:19)
    at Utils.getComplexKeys.forEach.prop (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:1974:25)
    at Array.forEach (<anonymous>)
    at Object.whereItemsQuery (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:1972:35)
    at Object.getWhereConditions (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2440:19)
    at Object.selectQuery (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\dialects\abstract\query-generator.js:1140:28)
    at QueryInterface.select (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\query-interface.js:1105:27)
    at Promise.try.then.then.then (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\model.js:1603:34)
From previous event:
    at Function.findAll (C:\Users\negezor\node\shiza-next\node_modules\sequelize\lib\model.js:1600:8)
    at C:\Users\negezor\node\shiza-next\node_modules\graphql-sequelize\lib\resolver.js:111:51
    at runCallback (timers.js:763:18)
    at tryOnImmediate (timers.js:734:5)
    at processImmediate (timers.js:716:5)
    at process.topLevelDomainCallback (domain.js:101:23)
Error: Cannot return null for non-nullable field Query.search.
    at completeValue (C:\Users\negezor\node\shiza-next\node_modules\graphql\execution\execute.js:631:13)
    at C:\Users\negezor\node\shiza-next\node_modules\graphql\execution\execute.js:617:14
    at <anonymous>

I understand that the error is related to sequelize, but it's also related to graphql-sequelize

What I use

Postgres: 10.2 Node.js: 9.8.0

sequelize: 4.37.1 graphql-sequelize: 8.0.0 graphql-tools: 2.23.1

pg: 7.4.1 pg-hstore: 2.3.2

mickhansen commented 6 years ago

This might not be related to graphql-sequelize, could you try and recreate the query in pure sequelize to see if it fails?

It looks like it's failing on something that's supposed to be a postgres array.

synonyms: {
  [Op.iLike]: `%${query}%`
}

Looks illegal, AFAIK you can't LIKE on array

negezor commented 6 years ago

This does not work with pure sequelize. Are there alternative search options for case-insensitive names in the array?

mickhansen commented 6 years ago

I don't know off the top of my head, you'll have to go searching.

yqhj commented 3 years ago

please confirm the type of query: typeof query