mirror of
https://github.com/strapi/strapi.git
synced 2025-08-15 04:08:04 +00:00
Merge pull request #5536 from strapi/fix/#4487/filteringDataManyWayError500
Fix deep filtering for manyWay
This commit is contained in:
commit
3e4c9d95d0
@ -79,43 +79,50 @@ const buildJoinsAndFilter = (qb, model, whereClauses) => {
|
||||
* @param {Object} destinationInfo - destination with which we are making a join
|
||||
*/
|
||||
const buildJoin = (qb, assoc, originInfo, destinationInfo) => {
|
||||
if (assoc.nature === 'manyToMany') {
|
||||
if (['manyToMany', 'manyWay'].includes(assoc.nature)) {
|
||||
const joinTableAlias = generateAlias(assoc.tableCollectionName);
|
||||
|
||||
let originColumnNameInJoinTable;
|
||||
if (assoc.nature === 'manyToMany') {
|
||||
originColumnNameInJoinTable = `${joinTableAlias}.${singular(
|
||||
destinationInfo.model.attributes[assoc.via].attribute
|
||||
)}_${destinationInfo.model.attributes[assoc.via].column}`;
|
||||
} else if (assoc.nature === 'manyWay') {
|
||||
originColumnNameInJoinTable = `${joinTableAlias}.${singular(
|
||||
originInfo.model.collectionName
|
||||
)}_${originInfo.model.primaryKey}`;
|
||||
}
|
||||
|
||||
qb.leftJoin(
|
||||
`${originInfo.model.databaseName}.${assoc.tableCollectionName} AS ${joinTableAlias}`,
|
||||
`${joinTableAlias}.${singular(
|
||||
destinationInfo.model.attributes[assoc.via].attribute
|
||||
)}_${destinationInfo.model.attributes[assoc.via].column}`,
|
||||
originColumnNameInJoinTable,
|
||||
`${originInfo.alias}.${originInfo.model.primaryKey}`
|
||||
);
|
||||
|
||||
qb.leftJoin(
|
||||
`${destinationInfo.model.databaseName}.${destinationInfo.model.collectionName} AS ${destinationInfo.alias}`,
|
||||
`${joinTableAlias}.${singular(
|
||||
originInfo.model.attributes[assoc.alias].attribute
|
||||
)}_${originInfo.model.attributes[assoc.alias].column}`,
|
||||
`${joinTableAlias}.${singular(originInfo.model.attributes[assoc.alias].attribute)}_${
|
||||
originInfo.model.attributes[assoc.alias].column
|
||||
}`,
|
||||
`${destinationInfo.alias}.${destinationInfo.model.primaryKey}`
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
const externalKey =
|
||||
assoc.type === 'collection'
|
||||
? `${destinationInfo.alias}.${assoc.via || destinationInfo.model.primaryKey}`
|
||||
: `${destinationInfo.alias}.${destinationInfo.model.primaryKey}`;
|
||||
|
||||
const internalKey =
|
||||
assoc.type === 'collection'
|
||||
? `${originInfo.alias}.${originInfo.model.primaryKey}`
|
||||
: `${originInfo.alias}.${assoc.alias}`;
|
||||
|
||||
qb.leftJoin(
|
||||
`${destinationInfo.model.databaseName}.${destinationInfo.model.collectionName} AS ${destinationInfo.alias}`,
|
||||
externalKey,
|
||||
internalKey
|
||||
);
|
||||
}
|
||||
|
||||
const externalKey =
|
||||
assoc.type === 'collection'
|
||||
? `${destinationInfo.alias}.${assoc.via ||
|
||||
destinationInfo.model.primaryKey}`
|
||||
: `${destinationInfo.alias}.${destinationInfo.model.primaryKey}`;
|
||||
|
||||
const internalKey =
|
||||
assoc.type === 'collection'
|
||||
? `${originInfo.alias}.${originInfo.model.primaryKey}`
|
||||
: `${originInfo.alias}.${assoc.alias}`;
|
||||
|
||||
qb.leftJoin(
|
||||
`${destinationInfo.model.databaseName}.${destinationInfo.model.collectionName} AS ${destinationInfo.alias}`,
|
||||
externalKey,
|
||||
internalKey
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -208,9 +215,7 @@ const buildWhereClause = ({ qb, field, operator, value }) => {
|
||||
if (Array.isArray(value) && !['in', 'nin'].includes(operator)) {
|
||||
return qb.where(subQb => {
|
||||
for (let val of value) {
|
||||
subQb.orWhere(q =>
|
||||
buildWhereClause({ qb: q, field, operator, value: val })
|
||||
);
|
||||
subQb.orWhere(q => buildWhereClause({ qb: q, field, operator, value: val }));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -258,7 +263,6 @@ const findModelByAssoc = assoc => {
|
||||
return models[assoc.collection || assoc.model];
|
||||
};
|
||||
|
||||
const findAssoc = (model, key) =>
|
||||
model.associations.find(assoc => assoc.alias === key);
|
||||
const findAssoc = (model, key) => model.associations.find(assoc => assoc.alias === key);
|
||||
|
||||
module.exports = buildQuery;
|
||||
|
@ -236,6 +236,7 @@ module.exports = ({ models, target }, ctx) => {
|
||||
|
||||
if (otherKey === foreignKey) {
|
||||
otherKey = `related_${otherKey}`;
|
||||
details.attribute = `related_${details.attribute}`;
|
||||
}
|
||||
|
||||
loadedModel[name] = function() {
|
||||
|
164
packages/strapi/__tests__/deepFiltering.test.e2e.js
Normal file
164
packages/strapi/__tests__/deepFiltering.test.e2e.js
Normal file
@ -0,0 +1,164 @@
|
||||
// Test an API with all the possible filed types and simple filterings (no deep filtering, no relations)
|
||||
|
||||
const { registerAndLogin } = require('../../../test/helpers/auth');
|
||||
const createModelsUtils = require('../../../test/helpers/models');
|
||||
const { createAuthRequest } = require('../../../test/helpers/request');
|
||||
|
||||
let rq;
|
||||
let modelsUtils;
|
||||
let data = {
|
||||
paniniCards: [],
|
||||
collectors: [],
|
||||
};
|
||||
|
||||
const paniniCard = {
|
||||
name: 'paniniCard',
|
||||
kind: 'collectionType',
|
||||
attributes: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const collector = {
|
||||
name: 'collector',
|
||||
kind: 'collectionType',
|
||||
attributes: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
panini_cards: {
|
||||
nature: 'manyWay',
|
||||
target: 'application::panini-card.panini-card',
|
||||
unique: false,
|
||||
},
|
||||
collector_friends: {
|
||||
nature: 'manyWay',
|
||||
target: '__self__',
|
||||
unique: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const paniniCardFixtures = [
|
||||
{
|
||||
name: 'Hugo LLORIS',
|
||||
},
|
||||
{
|
||||
name: 'Samuel UMTITI',
|
||||
},
|
||||
];
|
||||
|
||||
async function createFixtures() {
|
||||
for (let paniniCard of paniniCardFixtures) {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/panini-cards',
|
||||
body: paniniCard,
|
||||
});
|
||||
|
||||
data.paniniCards.push(res.body);
|
||||
}
|
||||
|
||||
const collector1Res = await rq({
|
||||
method: 'POST',
|
||||
url: '/collectors',
|
||||
body: {
|
||||
name: 'Bernard',
|
||||
panini_cards: [data.paniniCards[0].id, data.paniniCards[1].id],
|
||||
},
|
||||
});
|
||||
data.collectors.push(collector1Res.body);
|
||||
|
||||
const collector2Res = await rq({
|
||||
method: 'POST',
|
||||
url: '/collectors',
|
||||
body: {
|
||||
name: 'Isabelle',
|
||||
panini_cards: [data.paniniCards[0].id],
|
||||
collector_friends: [data.collectors[0].id],
|
||||
},
|
||||
});
|
||||
data.collectors.push(collector2Res.body);
|
||||
}
|
||||
|
||||
async function deleteFixtures() {
|
||||
for (let paniniCard of data.paniniCards) {
|
||||
await rq({
|
||||
method: 'DELETE',
|
||||
url: `/panini-cards/${paniniCard.id}`,
|
||||
});
|
||||
}
|
||||
for (let collector of data.collectors) {
|
||||
await rq({
|
||||
method: 'DELETE',
|
||||
url: `/collectors/${collector.id}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
describe('Deep Filtering API', () => {
|
||||
beforeAll(async () => {
|
||||
const token = await registerAndLogin();
|
||||
rq = createAuthRequest(token);
|
||||
|
||||
modelsUtils = createModelsUtils({ rq });
|
||||
await modelsUtils.createContentTypes([paniniCard, collector]);
|
||||
await createFixtures();
|
||||
}, 60000);
|
||||
|
||||
afterAll(async () => {
|
||||
await deleteFixtures();
|
||||
await modelsUtils.deleteContentTypes(['collector', 'panini-card']);
|
||||
}, 60000);
|
||||
|
||||
describe('Filter on a manyWay relation', () => {
|
||||
test('Should return 2 results', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/collectors',
|
||||
qs: {
|
||||
'panini_cards.name': data.paniniCards[0].name,
|
||||
},
|
||||
});
|
||||
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
expect(res.body.length).toBe(2);
|
||||
expect(res.body[0]).toMatchObject(data.collectors[0]);
|
||||
expect(res.body[1]).toMatchObject(data.collectors[1]);
|
||||
});
|
||||
|
||||
test('Should return 1 result', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/collectors',
|
||||
qs: {
|
||||
'panini_cards.name': data.paniniCards[1].name,
|
||||
},
|
||||
});
|
||||
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
expect(res.body.length).toBe(1);
|
||||
expect(res.body[0]).toMatchObject(data.collectors[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filter on a self manyWay relation', () => {
|
||||
test('Should return 1 result', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/collectors',
|
||||
qs: {
|
||||
'collector_friends.name': data.collectors[0].name,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('res', JSON.stringify(res.body, null, 2));
|
||||
|
||||
expect(Array.isArray(res.body)).toBe(true);
|
||||
expect(res.body.length).toBe(1);
|
||||
expect(res.body[0]).toMatchObject(data.collectors[1]);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user