Fix deep filtering for manyWay

Signed-off-by: Pierre Noël <pierre.noel@strapi.io>
This commit is contained in:
Pierre Noël 2020-03-18 17:15:24 +01:00
parent ac1898ee9f
commit 8aa50cd80c
2 changed files with 180 additions and 29 deletions

View File

@ -84,38 +84,52 @@ const buildJoinsAndFilter = (qb, model, whereClauses) => {
qb.leftJoin(
`${originInfo.model.databaseName}.${assoc.tableCollectionName} AS ${joinTableAlias}`,
`${joinTableAlias}.${singular(
destinationInfo.model.attributes[assoc.via].attribute
)}_${destinationInfo.model.attributes[assoc.via].column}`,
`${joinTableAlias}.${singular(destinationInfo.model.attributes[assoc.via].attribute)}_${
destinationInfo.model.attributes[assoc.via].column
}`,
`${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 if (assoc.nature === 'manyWay') {
const joinTableAlias = generateAlias(assoc.tableCollectionName);
qb.leftJoin(
`${originInfo.model.databaseName}.${assoc.tableCollectionName} AS ${joinTableAlias}`,
`${joinTableAlias}.${singular(originInfo.alias)}_id`,
`${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
}`,
`${destinationInfo.alias}.${destinationInfo.model.primaryKey}`
);
} 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 +222,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 +270,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;

View File

@ -0,0 +1,140 @@
// 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,
},
},
};
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],
},
});
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]);
});
});
});