Merge pull request #8356 from strapi/fix/dp-deep-filtering

Fix deepFiltering with draft & publish
This commit is contained in:
Alexandre BODIN 2020-10-20 14:03:10 +02:00 committed by GitHub
commit b1aa7b95d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 46 deletions

View File

@ -13,7 +13,7 @@ const BOOLEAN_OPERATORS = ['or'];
const buildQuery = ({ model, filters }) => qb => {
if (_.has(filters, 'where') && Array.isArray(filters.where) && filters.where.length > 0) {
qb.distinct();
buildJoinsAndFilter(qb, model, filters.where);
buildJoinsAndFilter(qb, model, filters);
}
if (_.has(filters, 'sort')) {
@ -45,9 +45,11 @@ const buildQuery = ({ model, filters }) => qb => {
* Add joins and where filters
* @param {Object} qb - knex query builder
* @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)
* @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 }));
return;
buildJoinsFromTree(qb, tree);
addFiltersQueriesToJoinTree(tree);
};
/**

View File

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

View File

@ -6,7 +6,10 @@ const utils = require('./utils')();
const populateQueries = require('./utils/populate-queries');
const {
hasDeepFilters,
contentTypes: { hasDraftAndPublish },
contentTypes: {
constants: { DP_PUB_STATES },
hasDraftAndPublish,
},
} = require('strapi-utils');
const combineSearchAndWhere = (search = [], wheres = []) => {
@ -134,12 +137,11 @@ const buildDeepQuery = ({ model, filters, search, populate }) => {
populate,
where: filters.where,
});
const customQueryOptions = _.pick(filters, ['publicationState']);
// Init the query
let query = model
.aggregate(
buildQueryAggregate(model, {
buildQueryAggregate(model, filters, {
paths: _.merge({}, populatePaths, wherePaths),
})
)
@ -165,8 +167,7 @@ const buildDeepQuery = ({ model, filters, search, populate }) => {
$in: ids,
},
},
null,
{ custom: customQueryOptions }
null
)
.populate(populate);
@ -292,7 +293,7 @@ const recursiveCastedWherePaths = (whereClauses, { model }) => {
* Builds an object based on paths:
* [
* 'articles',
* 'articles.tags.cateogry',
* 'articles.tags.category',
* 'articles.tags.label',
* ] => {
* articles: {
@ -307,14 +308,15 @@ const recursiveCastedWherePaths = (whereClauses, { model }) => {
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} filters - The query filters
* @param {Object} options - Options
* @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 acc.concat(buildLookup({ model, key, paths: paths[key] }));
return acc.concat(buildLookup({ model, key, paths: paths[key], filters }));
}, []);
};
@ -325,7 +327,7 @@ const buildQueryAggregate = (model, { paths } = {}) => {
* @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: {}}}
*/
const buildLookup = ({ model, key, paths }) => {
const buildLookup = ({ model, key, paths, filters }) => {
const assoc = model.associations.find(a => a.alias === key);
const assocModel = strapi.db.getModelByAssoc(assoc);
@ -341,8 +343,8 @@ const buildLookup = ({ model, key, paths }) => {
localAlias: `$${assoc.alias}`,
},
pipeline: []
.concat(buildLookupMatch({ assoc }))
.concat(buildQueryAggregate(assocModel, { paths })),
.concat(buildLookupMatch({ assoc, assocModel, filters }))
.concat(buildQueryAggregate(assocModel, filters, { paths })),
},
},
];
@ -351,17 +353,29 @@ const buildLookup = ({ model, key, paths }) => {
/**
* Build a lookup match expression (equivalent to a SQL join condition)
* @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) {
case 'oneToOne': {
return [
{
$match: {
$expr: {
$eq: [`$${assoc.via}`, '$$localId'],
},
$and: defaultMatches.concat({
$expr: {
$eq: [`$${assoc.via}`, '$$localId'],
},
}),
},
},
];
@ -369,9 +383,11 @@ const buildLookupMatch = ({ assoc }) => {
case 'oneToMany': {
return {
$match: {
$expr: {
$eq: [`$${assoc.via}`, '$$localId'],
},
$and: defaultMatches.concat({
$expr: {
$eq: [`$${assoc.via}`, '$$localId'],
},
}),
},
};
}
@ -379,18 +395,22 @@ const buildLookupMatch = ({ assoc }) => {
case 'manyToOne': {
return {
$match: {
$expr: {
$eq: ['$$localAlias', '$_id'],
},
$and: defaultMatches.concat({
$expr: {
$eq: ['$$localAlias', '$_id'],
},
}),
},
};
}
case 'manyWay': {
return {
$match: {
$expr: {
$in: ['$_id', '$$localAlias'],
},
$and: defaultMatches.concat({
$expr: {
$in: ['$_id', '$$localAlias'],
},
}),
},
};
}
@ -398,18 +418,22 @@ const buildLookupMatch = ({ assoc }) => {
if (assoc.dominant === true) {
return {
$match: {
$expr: {
$in: ['$_id', '$$localAlias'],
},
$and: defaultMatches.concat({
$expr: {
$in: ['$_id', '$$localAlias'],
},
}),
},
};
}
return {
$match: {
$expr: {
$in: ['$$localId', `$${assoc.via}`],
},
$and: defaultMatches.concat({
$expr: {
$in: ['$$localId', `$${assoc.via}`],
},
}),
},
};
}
@ -421,12 +445,14 @@ const buildLookupMatch = ({ assoc }) => {
},
{
$match: {
$expr: {
$and: [
{ $eq: [`$${assoc.via}.ref`, '$$localId'] },
{ $eq: [`$${assoc.via}.${assoc.filter}`, assoc.alias] },
],
},
$and: defaultMatches.concat({
$expr: {
$and: [
{ $eq: [`$${assoc.via}.ref`, '$$localId'] },
{ $eq: [`$${assoc.via}.${assoc.filter}`, assoc.alias] },
],
},
}),
},
},
];