Document structure:
Plan {
chapter: {
title: string,
attachments: string[] // Should be derived (computed)
// [...]
}
}
How to compute nested fields?
collection Plan {
compute chapter.attachments {
doc => { }
}
}
Document structure:
Plan {
chapter: {
title: string,
attachments: string[] // Should be derived (computed)
// [...]
}
}
How to compute nested fields?
collection Plan {
compute chapter.attachments {
doc => { }
}
}
You can’t inject computed fields into nested arrays or objects. You can create new objects from existing ones, though.
collection Plan {
history_days 0
// defining schema fields requires early access at the moment (*)
// a field for "internal use only"
_chapter: { title: String, /* ... */ }
compute chapter = doc => Object.assign(doc.chapter, {
attachments: Attachment.by_chapter(doc.chapter.title).toArray().map(.fileName)
/*... more fields */
})
}
Are you trying to handle relationships to a nested object (like my example assumes), or trying to effectively backfill part of your schema, or what are you trying to accomplish otherwise? Why must the attachments be nested under the chapter field?
(*)
To migrate from a schema like this
collection Plan {
history_days 0
chapter: { title: String }
}
could be update like this
collection Plan {
history_days 0
// a field for "internal use only"
_chapter: { title: String }
compute chapter = doc => Object.assign(doc.chapter, {
attachments: Attachment.by_chapter(doc.chapter.title).toArray().map(.fileName)
})
migration m2024_04_25_new_chapter_format {
rename chapter _chapter
}
}
I try to explain the use case simplified.
We have a questionaire that the user answers. So we storing the questions and answers as dedicated collections(Question
& Answer
). As part of the questionaire the user also gets asked to upload different type of attachments. The Questions and Answers are assigned to different MasterChapters.
Then we generate from the questionaire a document (Collection: Plan
) that includes chapters that are based on the MasterChapters. And inside the chapter we want to derive the attachments from the questionaire. So if the attachments will be updated as part of the questionare the attachments as part of the document will be every time in sync thanks to the computed value.
Collections (Simplified):
Question
Answer
MasterChapter
Plan
(Chapter
embedded)We decided to embedd Chapter
in Plan
, because:
Another place where a nested computed field would help @ptpaterson :
Collection Answer
question: Question,
content: {
select: Boolean | null,
text: String | null,
number: Number | null,
formula: Number | null
}
compute content.formula = (
doc => (
if (doc.masterAnswer.formula != null){
useFormula(doc.masterAnswer.formula, doc)
}
)
)
The Use Case is related with the case that we discussed in Privilege elevation in computed fields
I’ll pass on the use case. I wonder if we can include computed fields on Structs, on or after release.
// proposal, not real FSL
struct Content {
select: Boolean | null,
text: String | null,
number: Number | null,
compute formula = obj =>...
}
collection Answer {
content: Content
}
But that wouldn’t let you access fields outside of the struct.
In general, if you want to be able to query/read a field with a different shape than what is persisted, then I recommend the pattern where you store an internal field with the actual data, and use a computed field for how you want to query it, e.g. the _chapter
field I shared above. Alternatively, you can project an additional field when querying it, potentially wrapping that projection in a UDF.
We need to be able to unambiguously parse your schema in order to provide typechecking as well as safe, deterministic migrations from one schema to another. Having a persisted field that is altered by a computed field works against that.
I’ve identified another use case @ptpaterson:
We have a questionnaire where we ask the user hundreds of questions. Based on that, we want to extract master data that should be updated once something changes in the Questionnaire
collection. The master data is like a big nested object inside the Organization
collection. With the option to have computed fields in structs, we could create the master data to be fully reactive based on the questionnaire data.
We would combine the computed field with a self-introduced formula
field that can store custom functions and will be called from the computed field (See our discussion about the useFormula function) - With that, we would get a fully reactive database - game-changing.