121 lines
3.7 KiB
JavaScript
Raw Normal View History

'use strict';
/**
* Loaders.js service
*
* @description: A set of functions similar to controller's actions to avoid code duplication.
*/
const _ = require('lodash');
const DataLoader = require('dataloader');
module.exports = {
loaders: {},
createLoader: function(model) {
this.loaders[model] = new DataLoader(keys => {
return new Promise(async (resolve, reject) => {
try {
// Extract queries from keys and merge similar queries.
const { queries, map } = this.extractQueries(model, _.cloneDeep(keys));
// Run queries in parallel.
const results = await Promise.all(queries.map((query) => this.makeQuery(model, query)));
// Use to match initial queries order.
2018-11-22 17:33:51 +01:00
const data = this.mapData(model, keys, map, results);
resolve(data);
} catch (e) {
reject(e);
}
});
}, {
cacheKeyFn: (key) => {
return _.isObjectLike(key) ? JSON.stringify(_.cloneDeep(key)) : key;
}
});
},
mapData: function(model, originalMap, map, results) {
// Use map to re-dispatch data correctly based on initial keys.
return originalMap.map((query, index) => {
// Find the index of where we should extract the results.
const indexResults = map.findIndex(queryMap => queryMap.indexOf(index) !== -1);
const data = results[indexResults];
// Retrieving referring model.
const ref = this.retrieveModel(model, query.options.source);
if (query.single) {
// Return object instead of array for one-to-many relationship.
return data.find(entry => (entry._id || entry.id || '').toString() === query.params[ref.primaryKey].toString());
}
// Extracting ids from original request to map with query results.
const ids = query.options.query[ref.primaryKey];
2018-11-22 17:33:51 +01:00
return ids
.map(id => data.find(entry => entry[ref.primaryKey].toString() === id.toString()))
2018-11-22 17:33:51 +01:00
.filter(entry => entry !== undefined);
});
},
makeQuery: async function(model, query = {}) {
// Retrieve refering model.
const ref = this.retrieveModel(model, _.get(query.options, 'source'));
// Run query and remove duplicated ID.
const request = await strapi.plugins['content-manager'].services['contentmanager'].fetchAll({ model }, {
...query.options,
query: {
[ref.primaryKey]: _.uniq(query.ids.map(x => x.toString()))
},
populate: []
});
return request && request.toJSON ? request.toJSON() : request;
},
retrieveModel: function(model, source) {
// Retrieve refering model.
return source ?
strapi.plugins[source].models[model]:
strapi.models[model];
},
extractQueries: function(model, keys) {
const queries = [];
const map = [];
keys.forEach((current, index) => {
// Extract query options.
const { single = false, params = {} } = current;
const { query = {}, ...options } = current.options;
// Retrieving referring model.
const ref = this.retrieveModel(model, options.source);
// Find similar query.
const indexQueries = queries.findIndex(query => _.isEqual(query.options, options));
if (indexQueries !== -1) {
// Push to the same query the new IDs to fetch.
queries[indexQueries].ids.push(...(single ? [params[ref.primaryKey]] : query[ref.primaryKey]));
map[indexQueries].push(index);
} else {
// Create new query in the query.
queries.push({
ids: single ? [params[ref.primaryKey]] : query[ref.primaryKey],
options: options
});
map[queries.length - 1 > 0 ? queries.length - 1 : 0] = [];
map[queries.length - 1].push(index);
}
});
return {
queries,
map
};
}
};