Has anyone encountered the issue of being able to extract additional columns via a linked type for an object table? Typically this is done via clicking on “Configure Columns” on the column name of the object table.
Ideally we would like to reduce data duplication and have smaller, logical, connected object types. However, since this is not possible, we end up needing to create large tables made up of joins with various datasets so users can opt for the columns they need on this object table.
It would be interesting to see if anyone has faced this issue and what they did. We have explored derived properties, and function-backed columns but it does not work.
Configure Columns (native) → only works for 1:1 links, with native properties of the linked object. Doesn’t work for 1:many links or indirect links (2+ hops).
Derived Properties → same limitation: only 1:1 links, only native properties. If your link is 1:many or goes through more than one hop, it won’t show up, you can only aggregate, not pull the raw column.
For your case (reducing duplication, multiple join-heavy tables), the right fix is a function-backed column, but it only works if the function is set up correctly:
Parameter: ObjectSet<YourObjectType> (not a single object)
Return type: a map per object — Record<ObjectSpecifier<YourObjectType>, CustomType> (TS v2) or dict[ObjectType, CustomType] (Python)
The function must be published (not just saved) to show up in the column config
If the property is 2+ hops away, the function has to traverse the link manually (e.g., A → B → C) — Workshop won’t do that automatically
Use one function with a custom type containing multiple fields, instead of one function per column, that exposes several columns at once with better performance.
One limit to know: sorting on derived/function-backed columns is capped (200 rows for derived properties, up to 1,000 for function-backed). If you need to sort beyond that, you’d still need a materialized dataset for that specific view.
Felipe Montes
Development Advisor
Follow me @ linkedin.com/in/felipemontesl/ or GitHub: Brazillianerd
Hi @montes thanks for the response. I think this approach could work though I think it still has the limitation that the properties will not be configure-able at runtime, if I understood your approach correctly. We are probably going to with this approach where we use variable-backed column visibility to retrieve columns from object type A and retrieve additional properties from the linked type with object type B. This will allow us “configure columns” at run-time or in the “view” mode like what was suggested here: https://community.palantir.com/t/state-saving-for-column-configurations-in-workshop/2552 .
But now even with this variable backed column visibility, the challenge would be related to charts, the dropdowns will no longer show the “linked” properties, but even if I somehow show them in the dropdown, the current function which is backing the charts will not support the linked properties.
In a typescript function, we are creating two or three dimensional output using foundry’s API to groupBy, segmentBy or aggregation, and we check if we could refer the linked property from an object type B, but unfortunately the IDE suggests only available properties from object type A.
There might be solution without using foundry’s API for these group by and aggregations but I assume it will be slow since the aggregation will happen in browser side and not in the server. Do you know of a way to deal with linked properties for charts?
Hi @clarissa.a for charts with linked properties, the trick is to use .pivotTo("linkName") on your ObjectSet<A> to traverse to B (this runs server-side in the Function, not the browser — so that performance concern doesn’t apply either way), then build the TwoDimensionalAggregation/ThreeDimensionalAggregation object manually instead of relying on the native .groupBy().segmentBy() chain, which only works on properties of the current objectSet’s own type.
@Function()
public async chartByDateAndLinkedCategory(
aSet: ObjectSet<ObjectTypeA>
): Promise<ThreeDimensionalAggregation<string, string>> {
// Pull linked B property (server-side join via pivotTo)
const bByKey: Record<string, string> = {};
for await (const b of aSet.pivotTo("linkToB").asyncIter({ "$select": ["category"] })) {
bByKey[b.$primaryKey as string] = b.category!;
}
// Combine A's native property with B's linked one
const buckets: Record<string, Record<string, number>> = {};
for await (const a of aSet.asyncIter({ "$select": ["date", "linkedBKey"] })) {
const dateBucket = a.date!.toISOString().slice(0, 7);
const category = bByKey[a.linkedBKey!] ?? "unknown";
buckets[dateBucket] ??= {};
buckets[dateBucket][category] = (buckets[dateBucket][category] ?? 0) + 1;
}
return {
buckets: Object.entries(buckets).map(([date, segMap]) => ({
key: date,
value: Object.entries(segMap).map(([cat, count]) => ({ key: cat, value: count })),
})),
};
}
Workshop’s chart widget just needs this shape returned: it doesn’t care whether it came from .groupBy()/.segmentBy() or was assembled manually. This is the same pattern used for function-backed columns, so it can share the input ObjectSet<A> with your column functions. For very large volumes, you’d still want to pre-materialize via a pipeline rather than aggregating at request time.
Clarissa, hope this helps, let me know if anything comes up!