Hi everyone,
With Appian 25.3, a new version of a!queryRecordType() was introduced, and the previous version is still available as a!queryRecordType_25r2(). This evolution brings more control and clarity over which fields are retrieved.
a!queryRecordType()
a!queryRecordType_25r2()
25.2 - a!queryRecordType_25r2()
25.3 - a!queryRecordType()
a!selectionFields()
In our applications, most queries are wrapped in a reusable rule that takes a single fields input. Callers of these wrappers don’t (and shouldn’t) need to worry about the function version or its internal implementation.
To align with the new function, the wrapper needs to:
This way:
ri!record
ri!fields
if( and( a!isNotNullOrEmpty(ri!fields), a!isNotNullOrEmpty(ri!record) ), rule!LGTCP_RejectNulls( input: a!forEach( items: a!keys( a!update( ri!record, ri!fields, a!forEach(ri!fields, null) ) ), expression: if( typeof(fv!item) = 284, /* Record field */ fv!item, null ) ) ), null )
ri!input
cast( /*Record Relationship*/ a!listType(298), if( a!isNotNullOrEmpty(ri!input), rule!LGTCP_RejectNulls( input: a!forEach( items: ri!input, expression: if(typeof(fv!item) = 298, fv!item, null) ) ), {} ) )
a!queryRecordType( recordType: <RECORD_TYPE>, fields: a!selectionFields( allFieldsFromRecordType: { if( a!isNullOrEmpty( rule!ExtractBaseRecordFields( record: <RECORD_TYPE>(), fields: ri!fields ) ), <RECORD_TYPE>, {} ), rule!ExtractRecordRelationships(input: ri!fields) }, selectFields: ri!fields, includeRealTimeCustomFields: true ),
Discussion posts and replies are publicly visible
We follow a different approach.
We have very clear patterns to query individual record items or lists of items of a specific record. To avoid any problems in the long run, we always return all fields including extra long text and custom record fields. And, we create separate expressions for each type of record and or query. The hugely improved readability of the code easily pays of the added effort.
And then, we have other queries, which are typically not reused and very specific to a use case. We typically do not create separate expressions for such cases.
We do not use any kind of wrapper expressions for queryRecordType.
_25r2
Avoid regression testing on the existing implementation
Eliminate the need for custom logic to support both old and new versions
Fully leverage the capabilities of the new implementation
I’d still appreciate hearing everyone’s thoughts on this.
Hi Stefan Helzle , thanks for your reply.
Just to clarify and avoid any confusion: our “wrapper rule” approach is also one rule per record type, similar to your pattern of having clear, dedicated expressions per record.
Where our use case differs a bit is that we were relying on these rules not only to fetch all fields, but also for cases where we wanted to retrieve specific subsets of fields (e.g. only a few base fields, or a mix of base and relationship fields).
Now, with the new a!queryRecordType(), our wrapper needs a reliable way to detect whether the fields input contains any base-record fields (as opposed to only relationships or relationship fields). If there are base-record fields present, we should pass those as the selection; if there are none, we want the wrapper to automatically include the base record in allFieldsFromRecordType (and include any relationships) so callers that expect the “all base fields when none specified” behavior don’t need to change.We also understand that we could simply preserve the old versioned function, like Harsh Kumar Agarwal suggested, and introduce the new one with a slightly different contract (for example, separate parameters for fields vs all-record fields). However, staying aligned with Appian’s latest version of such an important function is valuable for us. At the same time, we’d like to avoid always querying every field by default, since for the same reasons Appian introduced this new standard, it’s not best practice to always select everything.
fields
Our proposed solution works as intended, but we were just wondering if there is an official or better approach to achieve the same behavior.
Peter Lewis hope you are doing fine. It would be possible to get also your insights about this discussion? Thank you
I'll leave the first two open questions from the OP to the community to answer. However, in terms of best practices, Andrea correctly identified the intent of the function changes and that the recommendation is to always only select the fields you need, which also extends to any wrapper functions you build.
Yeah I agree with Aaron but to add even more context - I expect the new behavior of explicitly selecting the fields you need to be the main approach going forward for a!queryRecordType(). While it may be beneficial temporarily (for backwards compatibility reasons) to make your wrapper rules to behave like the old version, I think this is a good point to start creating new wrapper rules that incorporate the new behavior and "deprecating" the old ones (maybe by renaming like Harsh Kumar Agarwal suggested).
Be extremely cautious with includeRealTimeCustomFields defaulted to true in generic query rules. Without an existing rigorous performance testing regimen, it will not be obvious that the custom record field is the root cause of performance issues since it will appear that "every" query to the record type (via the generic rule) is performing poorly. It's the equivalent of doing SELECT * in a complex SQL query / view, but without the benefits of an explain plan.
includeRealTimeCustomFields
true
SELECT *
If the rule is purpose-built, then this is less of a concern since the use cases are more specific . However, in generic rules that are used everywhere, later introductions of real-time record field(s) to the record type will likely cause unexplained performance issues in the data fabric across every part of your application that uses the generic query rule - even for a dataset of only 100k rows, and even after Data Fabric 2.0 (25.1). Don't ask me how I know .