Custom Sorting
Learn how to create custom sorting for user defined models
- how to create custom sort enums
- how to resolve custom sort in DynamoDB system
- how to resolve custom sort in DynamoDB+Elasticsearch system
Overview
Creating the New Sorter for the GraphQL Schema
The first thing users need to do is to add a CmsEntryFieldSortingPlugin
:
// there is a class and a function so users can use whatever suits their way of writing code
import {
CmsGraphQLSchemaSorterPlugin,
createCmsGraphQLSchemaSorterPlugin
} from "@webiny/api-headless-cms";
export const customSorterPlugin = createCmsGraphQLSchemaSorterPlugin(({ sorters, model }) => {
// we only want to add the sorter when generating a certain model GraphQL Schema
if (model.modelId !== "yourTargetModelId") {
return sorters;
}
return [...sorters, "myCustomSorting_ASC", "myCustomSorting_DESC"];
});
And then add the plugin into the plugins array of the createHandler()
:
const handler = createHandler({
plugins: [
// other plugins,
customSorterPlugin
]
});
Creating the Sort Plugin to Handle New Custom Sorters
DynamoDB Systems
As the sorting in the DynamoDB systems is basic, let’s show the example on how to sort via a nested object value:
// there is a class and a function so users can use whatever suits their way of writing code
import {
CmsEntryFieldSortingPlugin,
createCmsEntryFieldSortingPlugin
} from "@webiny/api-headless-cms-ddb";
const customSortingPlugin = createCmsEntryFieldSortingPlugin({
canUse: ({ fieldId, model }) => {
return model.modelId === "yourTargetModelId" && fieldId === "myCustomSorting";
},
createSort: ({ fields, order }) => {
// let's find the field we want to sort by
// you can find the field by joining all its parents fieldId + the fieldId you want to sort by via a dot (.)
// this is the information user must know (possibly they can create a finder for the field)
const fieldId = "nestedObject.anotherNestedObject.numberField"; // the fieldId is later on used in error report - if any
const field = fields[fieldId];
// we can create a field path via the built-in method
const valuePath = field.createPath({
field
});
// or manually
// note that all entry values are stored in the values object - thats why it is in the beginning of the string
const valuePath = `values.nestedObject.anotherNestedObject.numberField`;
return {
field,
fieldId,
valuePath,
reverse: order === "DESC"
};
}
});
And then add the plugin into the plugins array of the createHandler()
:
const handler = createHandler({
plugins: [
// other plugins,
customSortingPlugin
]
});
This plugin matches the myCustomSorting
value from myCustomSorting_ASC
and then it creates sort variables which are used in our built-in sorting, which uses lodash.orderBy
library.
Variables are:
- field - check out the interface
- fieldId - full path to the field in the
fields
object - it is used for error reporting if it happens - valuePath - full path to the field in the database record - it is used to get the value by which we are going to sort
- reverse - in which order will the records go to the API response
Although we gave an example to sort by nested objects, please do not do that in production as it will get quite slow at some point.
Elasticsearch Systems
// there is a class and a function so users can use whatever suits their way of writing code
import {
CmsEntryElasticsearchBodyModifierPlugin,
createCmsEntryElasticsearchBodyModifierPlugin
} from "@webiny/api-headless-cms-ddb-es";
const customBodyModifier = createCmsEntryElasticsearchBodyModifierPlugin({
modelId: "yourTargetModelId",
modifyBody: ({ model, body, where }) => {
// as we filter the models by setting the modelId in the plugin configuration there is no need to do it again
// we need to check if there is a custom sorting in the body
// we always generate a object sorting, so if there is no myCustomSorting key - there is no custom sorting
if (!body.sort.myCustomSorting) {
return;
}
body.sort = {
_script: {
type: "number",
script: {
lang: "painless",
inline: "some script"
},
order: "asc"
}
};
}
});
And then add the plugin into the plugins array of the createHandler()
:
const handler = createHandler({
plugins: [
// other plugins,
customBodyModifier
]
});
With this plugin, we replace the sort completely as it would fail. There is no myCustomSorting
field and the Elasticsearch query would fail when it would hit the server.
Note that we used the CmsEntryElasticsearchBodyModifierPlugin
in the example because we needed to replace whole sort
object on the body
. If you need to modify the sort (add or remove something), feel free to use the CmsEntryElasticsearchSortModifierPlugin
.
Conclusion
While custom sorters are a powerful functionality, use them carefully as it might degrade the system performance - especially in the DynamoDB systems.