Fix deepFiltering with draft & publish

Signed-off-by: Convly <jean-sebastien.herbaux@epitech.eu>
This commit is contained in:
Convly 2020-10-16 11:30:51 +02:00
parent fad9fda318
commit 03f5342123
3 changed files with 85 additions and 40 deletions

View File

@ -13,7 +13,7 @@ const BOOLEAN_OPERATORS = ['or'];
const buildQuery = ({ model, filters }) => qb => { const buildQuery = ({ model, filters }) => qb => {
if (_.has(filters, 'where') && Array.isArray(filters.where) && filters.where.length > 0) { if (_.has(filters, 'where') && Array.isArray(filters.where) && filters.where.length > 0) {
qb.distinct(); qb.distinct();
buildJoinsAndFilter(qb, model, filters.where); buildJoinsAndFilter(qb, model, filters);
} }
if (_.has(filters, 'sort')) { if (_.has(filters, 'sort')) {
@ -45,9 +45,11 @@ const buildQuery = ({ model, filters }) => qb => {
* Add joins and where filters * Add joins and where filters
* @param {Object} qb - knex query builder * @param {Object} qb - knex query builder
* @param {Object} model - Bookshelf model * @param {Object} model - Bookshelf model
* @param {Array<Object>} whereClauses - an array of where clause * @param {Object} filters - The query filters
*/ */
const buildJoinsAndFilter = (qb, model, whereClauses) => { const buildJoinsAndFilter = (qb, model, filters) => {
const { where: whereClauses } = filters;
/** /**
* Returns an alias for a name (simple incremental alias name) * Returns an alias for a name (simple incremental alias name)
* @param {string} name - name to alias * @param {string} name - name to alias
@ -210,12 +212,29 @@ const buildJoinsAndFilter = (qb, model, whereClauses) => {
}); });
}; };
const aliasedWhereClauses = buildWhereClauses(whereClauses, { model }); /**
* Add queries on tree's joins (deep search) based on given filters
* @param tree - joins tree
*/
const addFiltersQueriesToJoinTree = tree => {
_.each(tree.joins, value => {
const { alias, model } = value;
buildJoinsFromTree(qb, tree); runPopulateQueries(
toQueries({
publicationState: { query: filters.publicationState, model, alias },
}),
qb
);
addFiltersQueriesToJoinTree(value);
});
};
const aliasedWhereClauses = buildWhereClauses(whereClauses, { model });
aliasedWhereClauses.forEach(w => buildWhereClause({ qb, ...w })); aliasedWhereClauses.forEach(w => buildWhereClause({ qb, ...w }));
return; buildJoinsFromTree(qb, tree);
addFiltersQueriesToJoinTree(tree);
}; };
/** /**

View File

@ -11,9 +11,9 @@ const {
const optionsMap = { const optionsMap = {
publicationState: { publicationState: {
queries: { queries: {
[DP_PUB_STATE_LIVE]: ({ model }) => qb => { [DP_PUB_STATE_LIVE]: ({ model, alias }) => qb => {
const { collectionName } = model; const { collectionName } = model;
qb.whereNotNull(`${collectionName}.${PUBLISHED_AT_ATTRIBUTE}`); qb.whereNotNull(`${alias || collectionName}.${PUBLISHED_AT_ATTRIBUTE}`);
}, },
[DP_PUB_STATE_PREVIEW]: () => null, [DP_PUB_STATE_PREVIEW]: () => null,
}, },

View File

@ -6,7 +6,10 @@ const utils = require('./utils')();
const populateQueries = require('./utils/populate-queries'); const populateQueries = require('./utils/populate-queries');
const { const {
hasDeepFilters, hasDeepFilters,
contentTypes: { hasDraftAndPublish }, contentTypes: {
constants: { DP_PUB_STATES },
hasDraftAndPublish,
},
} = require('strapi-utils'); } = require('strapi-utils');
const combineSearchAndWhere = (search = [], wheres = []) => { const combineSearchAndWhere = (search = [], wheres = []) => {
@ -139,7 +142,7 @@ const buildDeepQuery = ({ model, filters, search, populate }) => {
// Init the query // Init the query
let query = model let query = model
.aggregate( .aggregate(
buildQueryAggregate(model, { buildQueryAggregate(model, filters, {
paths: _.merge({}, populatePaths, wherePaths), paths: _.merge({}, populatePaths, wherePaths),
}) })
) )
@ -292,7 +295,7 @@ const recursiveCastedWherePaths = (whereClauses, { model }) => {
* Builds an object based on paths: * Builds an object based on paths:
* [ * [
* 'articles', * 'articles',
* 'articles.tags.cateogry', * 'articles.tags.category',
* 'articles.tags.label', * 'articles.tags.label',
* ] => { * ] => {
* articles: { * articles: {
@ -307,14 +310,15 @@ const recursiveCastedWherePaths = (whereClauses, { model }) => {
const pathsToTree = paths => paths.reduce((acc, path) => _.merge(acc, _.set({}, path, {})), {}); const pathsToTree = paths => paths.reduce((acc, path) => _.merge(acc, _.set({}, path, {})), {});
/** /**
* Builds the aggregations pipeling of the query * Builds the aggregations pipeline of the query
* @param {Object} model - Queried model * @param {Object} model - Queried model
* @param {Object} filters - The query filters
* @param {Object} options - Options * @param {Object} options - Options
* @param {Object} options.paths - A tree of paths to aggregate e.g { article : { tags : { label: {}}}} * @param {Object} options.paths - A tree of paths to aggregate e.g { article : { tags : { label: {}}}}
*/ */
const buildQueryAggregate = (model, { paths } = {}) => { const buildQueryAggregate = (model, filters, { paths } = {}) => {
return Object.keys(paths).reduce((acc, key) => { return Object.keys(paths).reduce((acc, key) => {
return acc.concat(buildLookup({ model, key, paths: paths[key] })); return acc.concat(buildLookup({ model, key, paths: paths[key], filters }));
}, []); }, []);
}; };
@ -325,7 +329,7 @@ const buildQueryAggregate = (model, { paths } = {}) => {
* @param {string} options.key - The attribute name to lookup on the model * @param {string} options.key - The attribute name to lookup on the model
* @param {Object} options.paths - A tree of paths to aggregate inside the current lookup e.g { { tags : { label: {}}} * @param {Object} options.paths - A tree of paths to aggregate inside the current lookup e.g { { tags : { label: {}}}
*/ */
const buildLookup = ({ model, key, paths }) => { const buildLookup = ({ model, key, paths, filters }) => {
const assoc = model.associations.find(a => a.alias === key); const assoc = model.associations.find(a => a.alias === key);
const assocModel = strapi.db.getModelByAssoc(assoc); const assocModel = strapi.db.getModelByAssoc(assoc);
@ -341,8 +345,8 @@ const buildLookup = ({ model, key, paths }) => {
localAlias: `$${assoc.alias}`, localAlias: `$${assoc.alias}`,
}, },
pipeline: [] pipeline: []
.concat(buildLookupMatch({ assoc })) .concat(buildLookupMatch({ assoc, assocModel, filters }))
.concat(buildQueryAggregate(assocModel, { paths })), .concat(buildQueryAggregate(assocModel, filters, { paths })),
}, },
}, },
]; ];
@ -351,17 +355,29 @@ const buildLookup = ({ model, key, paths }) => {
/** /**
* Build a lookup match expression (equivalent to a SQL join condition) * Build a lookup match expression (equivalent to a SQL join condition)
* @param {Object} options - Options * @param {Object} options - Options
* @param {Object} options.assoc - The association on which is based the ematching xpression * @param {Object} options.assoc - The association on which is based the matching expression
*/ */
const buildLookupMatch = ({ assoc }) => { const buildLookupMatch = ({ assoc, assocModel, filters = {} }) => {
const defaultMatches = [];
if (hasDraftAndPublish(assocModel) && DP_PUB_STATES.includes(filters.publicationState)) {
const dpQuery = populateQueries.publicationState[filters.publicationState];
if (_.isObject(dpQuery)) {
defaultMatches.push(dpQuery);
}
}
switch (assoc.nature) { switch (assoc.nature) {
case 'oneToOne': { case 'oneToOne': {
return [ return [
{ {
$match: { $match: {
$expr: { $and: defaultMatches.concat({
$eq: [`$${assoc.via}`, '$$localId'], $expr: {
}, $eq: [`$${assoc.via}`, '$$localId'],
},
}),
}, },
}, },
]; ];
@ -369,9 +385,11 @@ const buildLookupMatch = ({ assoc }) => {
case 'oneToMany': { case 'oneToMany': {
return { return {
$match: { $match: {
$expr: { $and: defaultMatches.concat({
$eq: [`$${assoc.via}`, '$$localId'], $expr: {
}, $eq: [`$${assoc.via}`, '$$localId'],
},
}),
}, },
}; };
} }
@ -379,18 +397,22 @@ const buildLookupMatch = ({ assoc }) => {
case 'manyToOne': { case 'manyToOne': {
return { return {
$match: { $match: {
$expr: { $and: defaultMatches.concat({
$eq: ['$$localAlias', '$_id'], $expr: {
}, $eq: ['$$localAlias', '$_id'],
},
}),
}, },
}; };
} }
case 'manyWay': { case 'manyWay': {
return { return {
$match: { $match: {
$expr: { $and: defaultMatches.concat({
$in: ['$_id', '$$localAlias'], $expr: {
}, $in: ['$_id', '$$localAlias'],
},
}),
}, },
}; };
} }
@ -398,18 +420,22 @@ const buildLookupMatch = ({ assoc }) => {
if (assoc.dominant === true) { if (assoc.dominant === true) {
return { return {
$match: { $match: {
$expr: { $and: defaultMatches.concat({
$in: ['$_id', '$$localAlias'], $expr: {
}, $in: ['$_id', '$$localAlias'],
},
}),
}, },
}; };
} }
return { return {
$match: { $match: {
$expr: { $and: defaultMatches.concat({
$in: ['$$localId', `$${assoc.via}`], $expr: {
}, $in: ['$$localId', `$${assoc.via}`],
},
}),
}, },
}; };
} }
@ -422,10 +448,10 @@ const buildLookupMatch = ({ assoc }) => {
{ {
$match: { $match: {
$expr: { $expr: {
$and: [ $and: defaultMatches.concat(
{ $eq: [`$${assoc.via}.ref`, '$$localId'] }, { $eq: [`$${assoc.via}.ref`, '$$localId'] },
{ $eq: [`$${assoc.via}.${assoc.filter}`, assoc.alias] }, { $eq: [`$${assoc.via}.${assoc.filter}`, assoc.alias] }
], ),
}, },
}, },
}, },