Fix sort added by offset not removed for MSSQL (#17343)

This commit is contained in:
ian
2023-02-13 17:39:11 +08:00
committed by GitHub
parent f39b029b5a
commit ea91c40733
4 changed files with 490 additions and 8 deletions

View File

@@ -287,6 +287,9 @@ async function getDBQuery(
}
if (sortRecords) {
// Clears the order if any, eg: from MSSQL offset
dbQuery.clear('order');
if (needsInnerQuery) {
let orderByString = '';
const orderByFields: Knex.Raw[] = [];
@@ -326,9 +329,6 @@ async function getDBQuery(
);
}
} else {
// Clears the order if any, eg: from MSSQL offset
dbQuery.clear('order');
sortRecords.map((sortRecord) => {
if (sortRecord.column.includes('.')) {
const [alias, field] = sortRecord.column.split('.');

View File

@@ -39,10 +39,6 @@ export default function applyQuery(
const aliasMap: AliasMap = options?.aliasMap ?? Object.create(null);
let hasMultiRelationalFilter = false;
if (query.sort && !options?.isInnerQuery && !options?.hasMultiRelationalSort) {
applySort(knex, schema, dbQuery, query.sort, collection, aliasMap);
}
applyLimit(knex, dbQuery, query.limit);
if (query.offset) {
@@ -50,7 +46,11 @@ export default function applyQuery(
}
if (query.page && query.limit && query.limit !== -1) {
dbQuery.offset(query.limit * (query.page - 1));
applyOffset(knex, dbQuery, query.limit * (query.page - 1));
}
if (query.sort && !options?.isInnerQuery && !options?.hasMultiRelationalSort) {
applySort(knex, schema, dbQuery, query.sort, collection, aliasMap);
}
if (query.search) {
@@ -306,6 +306,9 @@ export function applySort(
if (returnRecords) return { sortRecords, hasMultiRelationalSort };
// Clears the order if any, eg: from MSSQL offset
rootQuery.clear('order');
rootQuery.orderBy(sortRecords);
}

View File

@@ -811,6 +811,343 @@ describe.each(common.PRIMARY_KEY_TYPES)('/items', (pkType) => {
expect(gqlResponse.body.data[localCollectionArtists].length).toEqual(count - offset);
});
});
describe('retrieves offset with limit and sort correctly', () => {
it.each(vendors)('%s', async (vendor) => {
// Setup
const count = 9;
const offset = 4;
const limit = 3;
const sort = 'name';
const artistName = 'offset-limit-sort-test';
const artists = [];
const expectedResultAsc = Array.from(Array(count).keys()).slice(offset, offset + limit);
const expectedResultDesc = Array.from(Array(count).keys())
.sort((v) => -v)
.slice(offset, offset + limit);
for (let i = 0; i < count; i++) {
const artist = createArtist(pkType);
artist.name = `${i}-${artistName}`;
artists.push(artist);
}
await CreateItem(vendor, { collection: localCollectionArtists, item: artists });
// Action
const responseAsc = await request(getUrl(vendor))
.get(`/items/${localCollectionArtists}`)
.query({
filter: JSON.stringify({
name: { _contains: artistName },
}),
offset,
limit,
sort,
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const gqlResponseAsc = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[localCollectionArtists]: {
__args: {
filter: {
name: { _contains: artistName },
},
offset,
limit,
sort,
},
id: true,
name: true,
},
},
});
const responseDesc = await request(getUrl(vendor))
.get(`/items/${localCollectionArtists}`)
.query({
filter: JSON.stringify({
name: { _contains: artistName },
}),
offset,
limit,
sort: `-${sort}`,
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const gqlResponseDesc = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[localCollectionArtists]: {
__args: {
filter: {
name: { _contains: artistName },
},
offset,
limit,
sort: `-${sort}`,
},
id: true,
name: true,
},
},
});
// Assert
expect(responseAsc.statusCode).toBe(200);
expect(responseAsc.body.data.length).toBe(limit);
expect(responseAsc.body.data.map((v: any) => parseInt(v.name.split('-')[0]))).toEqual(expectedResultAsc);
expect(gqlResponseAsc.statusCode).toBe(200);
expect(gqlResponseAsc.body.data[localCollectionArtists].length).toEqual(limit);
expect(
gqlResponseAsc.body.data[localCollectionArtists].map((v: any) => parseInt(v.name.split('-')[0]))
).toEqual(expectedResultAsc);
expect(responseDesc.statusCode).toBe(200);
expect(responseDesc.body.data.length).toBe(limit);
expect(responseDesc.body.data.map((v: any) => parseInt(v.name.split('-')[0]))).toEqual(expectedResultDesc);
expect(gqlResponseDesc.statusCode).toBe(200);
expect(gqlResponseDesc.body.data[localCollectionArtists].length).toEqual(limit);
expect(
gqlResponseDesc.body.data[localCollectionArtists].map((v: any) => parseInt(v.name.split('-')[0]))
).toEqual(expectedResultDesc);
});
});
describe('retrieves offset in aggregation with limit correctly', () => {
it.each(vendors)('%s', async (vendor) => {
// Setup
const count = 10;
const offset = 3;
const limit = 3;
const groupBy = ['id', 'name'];
const artistName = 'offset-aggregation-limit-test';
const artists = [];
for (let i = 0; i < count; i++) {
const artist = createArtist(pkType);
artist.name = `${i}-${artistName}`;
artists.push(artist);
}
await CreateItem(vendor, { collection: localCollectionArtists, item: artists });
// Action
const response = await request(getUrl(vendor))
.get(`/items/${localCollectionArtists}`)
.query({
aggregate: {
count: 'id',
},
filter: JSON.stringify({
name: { _contains: artistName },
}),
offset,
limit,
groupBy,
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const queryKey = `${localCollectionArtists}_aggregated`;
const gqlResponse = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[queryKey]: {
__args: {
filter: {
name: { _contains: artistName },
},
offset,
limit,
groupBy,
},
count: {
id: true,
},
group: true,
},
},
});
const response2 = await request(getUrl(vendor))
.get(`/items/${localCollectionArtists}`)
.query({
aggregate: {
count: 'id',
},
filter: JSON.stringify({
name: { _contains: artistName },
}),
offset: offset * 2,
limit,
groupBy,
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const gqlResponse2 = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[queryKey]: {
__args: {
filter: {
name: { _contains: artistName },
},
offset: offset * 2,
limit,
groupBy,
},
count: {
id: true,
},
group: true,
},
},
});
// Assert
expect(response.statusCode).toBe(200);
expect(response.body.data.length).toBe(limit);
expect(gqlResponse.statusCode).toBe(200);
expect(gqlResponse.body.data[queryKey].length).toEqual(limit);
expect(response2.statusCode).toBe(200);
expect(response2.body.data.length).toBe(limit);
expect(gqlResponse2.statusCode).toBe(200);
expect(gqlResponse2.body.data[queryKey].length).toEqual(limit);
for (const item of response.body.data) {
expect(response2.body.data).not.toContain(item);
}
const gqlResults = gqlResponse.body.data[queryKey].map((v: any) => v.group.id);
const gqlResults2 = gqlResponse2.body.data[queryKey].map((v: any) => v.group.id);
for (const item of gqlResults) {
expect(gqlResults2).not.toContain(item);
}
});
});
describe('retrieves offset in aggregation with limit and sort correctly', () => {
it.each(vendors)('%s', async (vendor) => {
// Setup
const count = 10;
const offset = 3;
const limit = 6;
const sort = 'name';
const groupBy = ['id', 'name'];
const artistName = 'offset-aggregation-limit-sort-test';
const artists = [];
const expectedResultAsc = Array.from(Array(count).keys()).slice(offset, offset + limit);
const expectedResultDesc = Array.from(Array(count).keys())
.sort((v) => -v)
.slice(offset, offset + limit);
for (let i = 0; i < count; i++) {
const artist = createArtist(pkType);
artist.name = `${i}-${artistName}`;
artists.push(artist);
}
await CreateItem(vendor, { collection: localCollectionArtists, item: artists });
// Action
const responseAsc = await request(getUrl(vendor))
.get(`/items/${localCollectionArtists}`)
.query({
aggregate: {
count: 'id',
},
filter: JSON.stringify({
name: { _contains: artistName },
}),
offset,
limit,
sort,
groupBy,
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const queryKey = `${localCollectionArtists}_aggregated`;
const gqlResponseAsc = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[queryKey]: {
__args: {
filter: {
name: { _contains: artistName },
},
offset,
limit,
sort,
groupBy,
},
count: {
id: true,
},
group: true,
},
},
});
const responseDesc = await request(getUrl(vendor))
.get(`/items/${localCollectionArtists}`)
.query({
aggregate: {
count: 'id',
},
filter: JSON.stringify({
name: { _contains: artistName },
}),
offset,
limit,
sort: `-${sort}`,
groupBy,
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const gqlResponseDesc = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[queryKey]: {
__args: {
filter: {
name: { _contains: artistName },
},
offset,
limit,
sort: `-${sort}`,
groupBy,
},
count: {
id: true,
},
group: true,
},
},
});
// Assert
expect(responseAsc.statusCode).toBe(200);
expect(responseAsc.body.data.length).toBe(limit);
expect(responseAsc.body.data.map((v: any) => parseInt(v.name.split('-')[0]))).toEqual(expectedResultAsc);
expect(gqlResponseAsc.statusCode).toBe(200);
expect(gqlResponseAsc.body.data[queryKey].length).toEqual(limit);
expect(gqlResponseAsc.body.data[queryKey].map((v: any) => parseInt(v.group.name.split('-')[0]))).toEqual(
expectedResultAsc
);
expect(responseDesc.statusCode).toBe(200);
expect(responseDesc.body.data.length).toBe(limit);
expect(responseDesc.body.data.map((v: any) => parseInt(v.name.split('-')[0]))).toEqual(expectedResultDesc);
expect(gqlResponseDesc.statusCode).toBe(200);
expect(gqlResponseDesc.body.data[queryKey].length).toEqual(limit);
expect(gqlResponseDesc.body.data[queryKey].map((v: any) => parseInt(v.group.name.split('-')[0]))).toEqual(
expectedResultDesc
);
});
});
});
});
});

View File

@@ -1702,6 +1702,148 @@ describe.each(common.PRIMARY_KEY_TYPES)('/items', (pkType) => {
expect(gqlResponse.body.data[localCollectionCountries][0].states.length).toEqual(count - offset);
});
});
describe('retrieves offset with limit and sort correctly', () => {
it.each(vendors)('%s', async (vendor) => {
// Setup
const count = 8;
const offset = 3;
const limit = 4;
const sort = 'name';
const country = createCountry(pkType);
const states = [];
const expectedResultAsc = Array.from(Array(count).keys()).slice(offset, offset + limit);
const expectedResultDesc = Array.from(Array(count).keys())
.sort((v) => -v)
.slice(offset, offset + limit);
for (let i = 0; i < count; i++) {
const state = createState(pkType);
state.name = `${i}-${state.name}`;
states.push(state);
}
await CreateItem(vendor, {
collection: localCollectionCountries,
item: {
...country,
states: {
create: states,
update: [],
delete: [],
},
},
});
// Action
const responseAsc = await request(getUrl(vendor))
.get(`/items/${localCollectionCountries}`)
.query({
fields: '*.*',
filter: JSON.stringify({
name: { _eq: country.name },
}),
deep: JSON.stringify({
states: {
_offset: offset,
_limit: limit,
_sort: sort,
},
}),
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const gqlResponseAsc = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[localCollectionCountries]: {
__args: {
filter: {
name: { _eq: country.name },
},
},
states: {
__args: {
offset,
limit,
sort,
},
id: true,
name: true,
},
},
},
});
const responseDesc = await request(getUrl(vendor))
.get(`/items/${localCollectionCountries}`)
.query({
fields: '*.*',
filter: JSON.stringify({
name: { _eq: country.name },
}),
deep: JSON.stringify({
states: {
_offset: offset,
_limit: limit,
_sort: sort,
},
}),
})
.set('Authorization', `Bearer ${common.USER.ADMIN.TOKEN}`);
const gqlResponseDesc = await requestGraphQL(getUrl(vendor), false, common.USER.ADMIN.TOKEN, {
query: {
[localCollectionCountries]: {
__args: {
filter: {
name: { _eq: country.name },
},
},
states: {
__args: {
offset,
limit,
sort: `-${sort}`,
},
id: true,
name: true,
},
},
},
});
// Assert
expect(responseAsc.statusCode).toBe(200);
expect(responseAsc.body.data.length).toBe(1);
expect(responseAsc.body.data[0].states.length).toBe(limit);
expect(responseAsc.body.data[0].states.map((v: any) => parseInt(v.name.split('-')[0]))).toEqual(
expectedResultAsc
);
expect(gqlResponseAsc.statusCode).toBe(200);
expect(gqlResponseAsc.body.data[localCollectionCountries].length).toEqual(1);
expect(gqlResponseAsc.body.data[localCollectionCountries][0].states.length).toEqual(limit);
expect(
gqlResponseAsc.body.data[localCollectionCountries][0].states.map((v: any) => parseInt(v.name.split('-')[0]))
).toEqual(expectedResultAsc);
expect(responseDesc.statusCode).toBe(200);
expect(responseDesc.body.data.length).toBe(1);
expect(responseDesc.body.data[0].states.length).toBe(limit);
expect(responseDesc.body.data[0].states.map((v: any) => parseInt(v.name.split('-')[0]))).toEqual(
expectedResultAsc
);
expect(gqlResponseDesc.statusCode).toBe(200);
expect(gqlResponseDesc.body.data[localCollectionCountries].length).toEqual(1);
expect(gqlResponseDesc.body.data[localCollectionCountries][0].states.length).toEqual(limit);
expect(
gqlResponseDesc.body.data[localCollectionCountries][0].states.map((v: any) =>
parseInt(v.name.split('-')[0])
)
).toEqual(expectedResultDesc);
});
});
});
});
});