1

I developed an API using node with GraphQL, when I add the DataLoaders, the resolvers starts returning wrong data, like I pass 4 Ids [3, 3, 1, 2], the batch function find in Database and return ordered like [1, 2, 3] and when return to the resolver, the first object who has id 3, receive the response with id 1.

Project Packages versions ->
    "dataloader": "1.3.0",
    "express": "^4.17.1",
    "express-graphql": "^0.11.0",
    "graphql": "^15.3.0",
    "graphql-fields": "^2.0.3",
    "graphql-tools": "^6.2.4",

I also tried to use the dataloader latest version, but didn't worked

Follow my code.

Resolver ->

Address: {
        idCity: (parent, args, { db, dataloaders: { cityLoader } }: { db: DbConnection, dataloaders: DataLoaders }, info: GraphQLResolveInfo) => {
            return cityLoader.load({ info, key: parent.get('idCity') })
                .catch(handleError);
        }
    },
Data Loader Factory ->

cityLoader: new DataLoader<DataLoaderParam<number>, CityInstance>(
                (params: DataLoaderParam<number>[]) => CityLoader.batchCities(this.db.City, params, this.requestedFields),
                {
                    cacheKeyFn: (param: DataLoaderParam<number[]>) => param.key
                }
            ),
DataLoaderParam ->

export interface DataLoaderParam<T> {
    key: T;
    info: GraphQLResolveInfo;
}
City Loader ->

export class CityLoader {
    static async batchCities(City: CityModel, params: DataLoaderParam<number>[], requestedFields: RequestedFields) {
        const ids: number[] = params.map(item => item.key);

        return City["findAll"]({
            where: { id: { $in: ids } },
            attributes: requestedFields.getFields(params[0].info, { keep: ['id'], exclude: [] })
        });
    }
}
Ronaldo
  • 11
  • 1

1 Answers1

1

I recently was running into this issue as well. I think you might have a similar problem to the one I had.

Mongo's $in operator doesn't guarantee order. So if you have a query where it has something like _id: {$in: [1,2,3]} it could be be returned in 2,3,1.

This is outlined in the docs for dataloader (although not in big and red enough font like maybe it should be)

There are a few constraints this function must uphold:

The Array of values must be the same length as the Array of keys. Each index in the Array of values must correspond to the same index in the Array of keys.

So my issue (and I assume yours) is that the order of the input doesn't match the order of the output. So as a stopgap I did this:

const sortOrder = new Map<string, number>()
for (let i = 0; i < IDs.length; i++) {
  sortOrder.set(IDs[i], i)
}
objects.sort((a, b) => (sortOrder.get(a._id) > sortOrder.get(b._id) ? 1 : -1))

A better solution would be to extend dataloader to allow a mapping/matching function

iweir
  • 21
  • 3