Una guida pratica alla scrittura di espressioni CEL nel CMS.
Guida pratica alla scrittura di espressioni CEL nel CMS.
CEL (Common Expression Language) è un linguaggio di scripting leggero integrato nel nostro CMS. Consente di scrivere espressioni dinamiche che possono recuperare dati dai documenti, leggere i parametri URL e calcolare valori al volo.
Ecco cosa succede quando viene eseguito uno script CEL:
Il tuo script Il motore Risultato
| | |
v v v
documents.get("article", "intro") --> Recupera dal database --> { headline: "Welcome", body: "..." }
.headline --> Estrae il campo --> "Welcome"
Considera CEL come un linguaggio di query in sola lettura. Non può modificare nulla nel database: legge semplicemente i dati e restituisce un risultato calcolato. Questo lo rende sicuro da usare in qualsiasi area del CMS.
Ogni espressione CEL ha accesso a tre elementi:
| Oggetto | Che cos'è | Esempio |
|---|---|---|
documents | Recupera qualsiasi documento dal CMS | documents.get("country", "us") |
meta | Informazioni sulla richiesta corrente (locale, parametri URL) | meta.locale, meta.params.slug |
schema | Definizioni dei campi del documento corrente | schema.fields |
docQuando scrivi espressioni CEL all'interno di un editor di documenti, puoi accedere ai valori dei campi del documento corrente tramite l'oggetto doc. Questo consente di creare campi calcolati e riferimenti tra campi.
// Accede al campo price del documento corrente
doc.price
// Calcola il totale dai campi del documento corrente
doc.price * doc.quantity
// Condizione basata sullo stato del documento corrente
doc.status == "published" ? doc.title : "Bozza: " + doc.title
L'oggetto doc contiene tutti i valori dei campi del documento modificato. È utile per:
doc.price * doc.quantity)La funzionalità più potente di CEL è il recupero di documenti da qualsiasi punto del CMS.
Sintassi: documents.get(schemaName, identifier)
Supponiamo di avere un documento article memorizzato con l'identificatore "welcome-post":
// Memorizzato nel CMS come: article / welcome-post
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Per recuperare l'intero documento:
documents.get("article", "welcome-post")
Restituisce:
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Per recuperare solo il titolo:
documents.get("article", "welcome-post").headline
Restituisce: "Welcome to Our Platform"
Per recuperare l'autore:
documents.get("article", "welcome-post").author
Restituisce: "Sarah Chen"
Quando la pagina presenta route dinamiche (come /articles/[slug]), puoi usare meta.params per ottenere il parametro URL e recuperare il documento corretto.
Se qualcuno visita /articles/welcome-post:
documents.get("article", meta.params.slug).headline
Restituisce: "Welcome to Our Platform"
È così che si costruiscono pagine dinamiche: lo stesso script CEL funziona per qualsiasi articolo, usando semplicemente lo slug presente nell'URL.
Sintassi: documents.find(schemaName) oppure documents.find(schemaName, filter)
// Recupera tutti i paesi
documents.find("country")
Restituisce:
[
{ "code": "us", "name": "United States", "flag": "US" },
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
{ "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// Recupera i paesi applicando un filtro
documents.find("country", { "where": { "code": "us" } })
Restituisce:
[
{ "code": "us", "name": "United States", "flag": "US" }
]
CEL supporta il recupero di contenuti tradotti in due modi: traduzione automatica basata sul locale e ricerca esplicita della traduzione.
Quando meta.locale è impostato (ad esempio tramite parametri di route o preferenze dell'utente), documents.get() unisce automaticamente il contenuto tradotto:
// Se meta.locale è "fr", restituisce la traduzione francese unita al documento di base
documents.get("greeting", "welcome").headline
Come funziona:
meta.locale non è "en" o "en-US", cerca la traduzione nella tabella translations{ ...baseContent, ...translatedContent }Questo significa che i campi tradotti sostituiscono quelli di base, mentre i campi non tradotti usano il documento di base come fallback.
Quando devi recuperare una traduzione specifica indipendentemente dal locale corrente:
Sintassi: documents.translated(schemaName, identifier, locale)
// Recupera sempre la traduzione spagnola
documents.translated("greeting", "welcome", "es").headline
// Recupera la traduzione in base a un parametro URL
documents.translated("product", meta.params.id, meta.params.lang).description
// Confronta le traduzioni
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
Documenti greeting con traduzioni:
// Documento di base: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }
// Traduzione (lingua: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }
// Traduzione (lingua: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }
Script CEL:
// Con meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Restituisce: "Bienvenue"
// Traduzione spagnola esplicita
documents.translated("greeting", "welcome", "es").headline
// Restituisce: "Bienvenido"
// Pattern di fallback per traduzioni mancanti
documents.translated("greeting", "welcome", meta.params.lang) != null
? documents.translated("greeting", "welcome", meta.params.lang).headline
: documents.get("greeting", "welcome").headline
Hai un hero-block che deve visualizzare un titolo recuperato da un documento article.
Il documento article (identificatore: "homepage-hero"):
{
"headline": "Build Faster, Ship Smarter",
"subheadline": "The modern CMS for developers"
}
Script CEL nel campo titolo del blocco hero:
documents.get("article", "homepage-hero").headline
Risultato: il blocco hero visualizza "Build Faster, Ship Smarter"
Stai creando una pagina in /countries/[code] e vuoi visualizzare il nome completo del paese.
I documenti country:
// country / us
{ "code": "us", "name": "United States", "flag": "US", "languages": ["en", "es"] }
// country / sa
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA", "languages": ["ar", "en"] }
Script CEL:
documents.get("country", meta.params.code).name
Quando qualcuno visita /countries/us:
meta.params.code = "us""United States"Quando qualcuno visita /countries/sa:
meta.params.code = "sa""Saudi Arabia"Mostra titoli diversi in base al locale dell'utente.
meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"
Se il locale è "ar-SA": restituisce "Welcome, everyone"
Se il locale è diverso: restituisce "Welcome"
Il tuo article ha un campo countryCode e vuoi ottenere il nome completo del paese.
Documento article:
{ "headline": "News from the US", "countryCode": "us" }
Script CEL:
documents.get("country", documents.get("article", "us-news").countryCode).name
Cosa succede:
documents.get("article", "us-news") restituisce { "headline": "News from the US", "countryCode": "us" }.countryCode estrae "us"documents.get("country", "us") restituisce { "code": "us", "name": "United States", ... }.name estrae "United States"Risultato: "United States"
Se un documento potrebbe non esistere, puoi fornire un fallback:
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Article Not Found"
Oppure verifica se esiste un campo specifico:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "Unknown Author"
Il tuo articolo ha dei tag e vuoi verificare se esiste un tag specifico:
"featured" in documents.get("article", "welcome-post").tags
Restituisce: true se l'articolo ha il tag "featured"
Ottieni il primo tag:
documents.get("article", "welcome-post").tags[0]
Restituisce: "announcement" (il primo tag)
Conta i tag:
size(documents.get("article", "welcome-post").tags)
Restituisce: 2 (numero di tag)
Le route parametriche sono fondamentali per creare pagine dinamiche e localizzate. Quando definisci un pattern come /{lang}/landingPage, il CMS estrae i parametri dall'URL e li rende disponibili tramite meta.params.
Definizione del pattern della route:
Le route usano la sintassi :paramName o {paramName} per definire segmenti dinamici:
| Pattern | URL di esempio | Parametri estratti |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
Associazione dei parametri: Ogni parametro della route può essere associato a uno schema di documento per la validazione:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
Questa associazione indica al CMS di:
lang dall'URLlanguage (cerca un documento il cui content.code corrisponda)Configurazione della route:
/{lang}/landingPage/{lang}/landingPage{ "lang": "language" }I tuoi documenti greeting:
// greeting / ko
{ "code": "ko", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/ko/get-started" }
// greeting / en
{ "code": "en", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/en/get-started" }
// greeting / ja
{ "code": "ja", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Start", "ctaUrl": "/ja/get-started" }
Script CEL per recuperare il contenuto localizzato:
documents.get("greeting", meta.params.lang).headline
Come viene risolto:
| URL | meta.params.lang | Risultato |
|---|---|---|
/ko/landingPage | "ko" | "Welcome" |
/en/landingPage | "en" | "Welcome" |
/ja/landingPage | "ja" | "Welcome" |
Per route come /{country}/{lang}/products:
Configurazione della route:
{
"pattern": "/{country}/{lang}/products",
"param_bindings": {
"country": "country",
"lang": "language"
}
}
Script CEL:
// Ottieni il nome del paese
documents.get("country", meta.params.country).name
// Ottieni l'elenco localizzato dei prodotti in base al paese
documents.find("product", { "where": { "country": meta.params.country } })
// Combinato: mostra un saluto specifico per il paese nella lingua dell'utente
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name
Cascata di validazione:
Il CMS valida i parametri gerarchicamente. Per le route /{country}/{lang}:
country rispetto allo schema countrylang rispetto allo schema languagelang sia presente nell'array country.languages[] (validazione gerarchica)meta.segments fornisce il percorso URL grezzo come array, utile quando serve un accesso posizionale senza parametri nominati.
Come funziona:
| Percorso URL | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| Caso d'uso | Approccio migliore |
|---|---|
| Parametri nominati dal pattern della route | meta.params.lang |
| Accesso basato sulla posizione | meta.segments[0] |
| Ottenere la profondità del percorso | size(meta.segments) |
| Verificare se il percorso contiene un segmento | "admin" in meta.segments |
// Ottieni il primo segmento (spesso il codice della lingua)
meta.segments[0]
// Verifica la profondità del percorso
size(meta.segments) > 2 ? "deep" : "shallow"
// Verifica se siamo nella sezione amministrativa
"admin" in meta.segments ? "admin mode" : "public mode"
// Fallback: usa il segmento se il parametro non è associato
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
L'oggetto meta contiene tutto il contesto della richiesta corrente:
| Proprietà | Tipo | Descrizione | |
|---|---|---|---|
meta.locale | string | Codice del locale corrente (ad esempio "en-US", "ko-KR", "ar-SA") | |
meta.params | Record<string, string> | Parametri della route estratti dall'URL | |
meta.segments | string[] | Percorso URL suddiviso in segmenti | |
meta.docId | string | null | UUID del documento corrente (null per i nuovi documenti) | |
meta.title | string | Titolo del documento corrente |
Il codice del locale segue il formato BCP 47 (lingua-regione):
// Verifica il locale per le lingue RTL
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
// Ottieni solo la parte relativa alla lingua
meta.locale.split("-")[0] // Non supportato - usa invece meta.params.lang
I parametri delle route sono sempre stringhe. Prima della valutazione, il CMS li valida rispetto agli schemi associati:
// Accedi al parametro nominato
meta.params.lang // "ko"
meta.params.country // "us"
meta.params.slug // "welcome-post"
// Verifica se il parametro esiste
has(meta.params.category) // true/false
// Usa il parametro per recuperare un documento
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)
Segmenti URL grezzi come array:
// Accedi tramite indice (a partire da 0)
meta.segments[0] // Primo segmento
meta.segments[1] // Secondo segmento
// Verifica la lunghezza
size(meta.segments) // Numero di segmenti
// Verifica l'appartenenza
"products" in meta.segments // Il percorso include "products"?
L'UUID del documento corrente, utile per gli script autoreferenziali:
// Disponibile solo durante la modifica di documenti esistenti
meta.docId != null ? "editing" : "creating new"
// Usa nella logica condizionale
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
Il titolo del documento corrente:
// Usa per la visualizzazione
"Editing: " + meta.title
// Condizione basata sul titolo
meta.title.contains("Draft") ? "work in progress" : "published"
Per una sintassi più pulita quando lo schema è noto ma l'identificatore è dinamico:
// Approccio tradizionale
documents.get("airports", meta.params.code).name
// Utilizzo di ref() - lo schema è separato dall'identificatore dinamico
documents.ref("airports").get(meta.params.code).name
I due approcci sono equivalenti, ma ref() rende più chiara la parte dinamica.
documents.get("schema", "identifier") // Recupera un documento
documents.get("schema", "id").fieldName // Recupera un campo specifico
documents.find("schema") // Recupera tutti i documenti
documents.find("schema", { "where": {...}}) // Query filtrata
documents.ref("schema").get(identifier) // Recupero concatenato
documents.translated("schema", "id", "fr") // Recupera con locale esplicito
meta.locale // "en-US", "ar-SA", ecc.
meta.params.xyz // Parametro URL denominato "xyz"
meta.segments // Percorso URL come array: ["articles", "intro"]
meta.segments[0] // Primo segmento del percorso
meta.docId // ID del documento corrente (o null)
meta.title // Titolo del documento corrente
doc.fieldName // Valore del campo del documento corrente (nel contesto dell'editor)
// Confronto
== != < <= > >=
// Logica
&& || !
// Ternario (if-else)
condition ? valueIfTrue : valueIfFalse
// Appartenenza
"value" in listOrMap
size(list) // Conta gli elementi
size(string) // Lunghezza della stringa
"text".startsWith("te") // true
"text".endsWith("xt") // true
"text".contains("ex") // true
has(object.property) // Verifica se la proprietà esiste
hasProperty(obj, "key") // Verifica se l'oggetto contiene la chiave (sintassi alternativa)
Se qualcosa va storto, vedrai uno dei seguenti messaggi:
| Errore | Significato |
|---|---|
SYNTAX_ERROR | Errore di battitura nello script (virgolette mancanti, operatore errato) |
TYPE_ERROR | Stai combinando tipi incompatibili |
RUNTIME_ERROR | Lo script è stato eseguito, ma ha riscontrato un problema (variabile non definita) |
FETCH_LIMIT_EXCEEDED | Stai recuperando troppi documenti (massimo 50) |
TIMEOUT | Lo script ha impiegato troppo tempo (massimo 5 secondi) |
AST_DEPTH_EXCEEDED | L'espressione è annidata troppo profondamente (profondità massima: 50) |
SCRIPT_TOO_LONG | Lo script supera il limite di 5000 caratteri |
Il motore CEL è progettato per essere estensibile. Tra le funzionalità future previste figurano:
// Futuro: chiama servizi esterni tramite MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
// Futuro: generazione di contenuti basata sull'IA
ai.summarize(documents.get("article", meta.params.id).body, 100)
ai.translate(meta.params.text, meta.params.targetLang)
ai.classify(meta.params.input, ["positive", "negative", "neutral"])
Queste funzionalità verranno aggiunte tramite il sistema di funzioni registrate, mantenendo la compatibilità con gli script esistenti.
documents. o meta. e l'editor mostrerà le opzioni disponibilidocuments.get("schema", "id"), poi aggiungi .fieldName!= null ? ... : ...documents.get() o documents.find() viene conteggiato nel limite di 50 recuperihas(meta.params.category) prima di accederviQuesta sezione illustra pattern avanzati per collegare i documenti e creare strutture di contenuto relazionali.
La forma più semplice: un documento fa riferimento a un altro tramite identificatore.
// L'articolo memorizza l'ID dell'autore; recupera il nome dell'autore
documents.get("author", documents.get("article", "intro").authorId).name
Per una sintassi più pulita quando l'identificatore è dinamico:
// Approccio tradizionale
documents.get("country", documents.get("airport", meta.params.code).countryCode).name
// Utilizzo di ref() - più chiaro quando lo schema è noto ma l'identificatore è dinamico
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name
Crea relazioni profonde concatenando più recuperi:
// Aeroporto → Paese → Regione → Continente
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
Combina i riferimenti ai documenti con le traduzioni:
// Ottieni il nome localizzato del paese di un aeroporto
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
Il documento memorizza un ID che fa riferimento a un altro documento.
// article / tech-news
{ "title": "Tech Update", "authorId": "author-123", "categoryId": "cat-tech" }
// Risolvi il nome dell'autore
documents.get("author", documents.get("article", meta.params.slug).authorId).name
// Risolvi la categoria con fallback
documents.get("article", meta.params.slug).categoryId != null
? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
: "Uncategorized"
I documenti fanno riferimento tra loro tramite codici semantici anziché UUID.
// airport / JFK
{ "code": "JFK", "name": "John F. Kennedy International", "countryCode": "us" }
// country / us
{ "code": "us", "name": "United States", "currencyCode": "usd" }
// currency / usd
{ "code": "usd", "symbol": "$", "name": "US Dollar" }
// Catena Aeroporto → Paese → Valuta
documents.get("currency",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).currencyCode
).symbol
// Per JFK: restituisce "$"
Usa doc per i campi calcolati che fanno riferimento ad altri documenti in base ai valori del documento corrente.
// In un documento product, recupera i dettagli della categoria correlata
documents.get("category", doc.categoryId).description
// Costo di spedizione calcolato in base al paese di origine del prodotto
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight
Quando i documenti fanno riferimento l'uno all'altro, presta attenzione ai limiti di recupero.
// Recupera l'autore dell'articolo, poi gli altri articoli dell'autore (controlla il numero di recuperi!)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })
Quando un campo può fare riferimento a schemi diversi:
// content-block / hero-1
{ "type": "hero", "sourceType": "article", "sourceId": "welcome-post" }
// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// Ricerca dinamica dello schema in base a sourceType
documents.get("content-block", "hero-1").sourceType == "article"
? documents.get("article", documents.get("content-block", "hero-1").sourceId).headline
: documents.get("product", documents.get("content-block", "hero-1").sourceId).name
Ogni chiamata a documents.get(), documents.find() e documents.ref().get() viene tracciata per l'invalidazione della cache. Quando cambia un documento referenziato, il CMS sa quali espressioni CEL devono essere rivalutate.
Le dipendenze tracciate includono:
schema:identifier - Dipendenza da un documento specificoschema:identifier - Come get, tramite sintassi concatenataschema:* - Dipendenza a livello di schema (qualsiasi documento nello schema)Questa procedura crea una landing page multilingue accessibile all'indirizzo /{lang}/landingPage.
Nell'amministrazione del CMS, crea uno schema personalizzato chiamato greeting:
{
"name": "greeting",
"fields": [
{ "name": "code", "type": "string", "required": true },
{ "name": "headline", "type": "string", "required": true },
{ "name": "subheadline", "type": "string" },
{ "name": "ctaText", "type": "string" },
{ "name": "ctaUrl", "type": "string" }
]
}
Crea un documento per ogni lingua:
Documento: greeting/ko
{
"code": "ko",
"headline": "Welcome",
"subheadline": "Welcome to our platform",
"ctaText": "Get Started",
"ctaUrl": "/ko/get-started"
}
Documento: greeting/en
{
"code": "en",
"headline": "Welcome",
"subheadline": "Welcome to our platform",
"ctaText": "Get Started",
"ctaUrl": "/en/get-started"
}
Documento: greeting/ja
{
"code": "ja",
"headline": "Welcome",
"subheadline": "Welcome to our platform",
"ctaText": "Start",
"ctaUrl": "/ja/get-started"
}
Crea una pagina con la seguente configurazione:
/{lang}/landingPagelang al componente language {
"lang": "language"
}
Aggiungi un blocco hero alla route con questi script CEL per ciascun campo:
Campo Headline:
documents.get("greeting", meta.params.lang).headline
Campo Subheadline:
documents.get("greeting", meta.params.lang).subheadline
Campo CTA Text:
documents.get("greeting", meta.params.lang).ctaText
Campo CTA URL:
documents.get("greeting", meta.params.lang).ctaUrl
Aggiungi una route catch-all. ParametricRoutePage risolve la pagina, estrae meta.params dall'URL, valuta i binding CEL lato server e renderizza ogni blocco tramite il registro: non devi creare il contesto meta né chiamare direttamente il client di basso livello.
// app/[...slug]/page.tsx
import ParametricRoutePage from 'cms-renderer/lib/renderer';
import { registry } from '@/lib/registry';
import { cmsConfig } from '@/lib/cms-config';
export const dynamic = 'force-static';
interface PageProps {
params: Promise<{ slug: string[] }>;
}
export default async function Page({ params }: PageProps) {
const { slug } = await params;
return (
<ParametricRoutePage
registry={registry}
apiKey={cmsConfig.apiKey}
websiteId={cmsConfig.websiteId}
cmsUrl={cmsConfig.cmsUrl}
params={Promise.resolve({ slug })}
/>
);
}
Visita questi URL per visualizzare i contenuti localizzati:
| URL | Titolo previsto |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
Quando un utente visita /ko/landingPage:
/{lang}/landingPagemeta.params.lang = "ko"languagedocuments.get("greeting", meta.params.lang) vengono risolti nel contenuto coreanointerface CelMeta {
/** Codice del locale corrente (ad esempio 'en-US') */
locale: string;
/** Parametri della route estratti dall'URL */
params: Record<string, string>;
/** Segmenti del percorso URL */
segments: string[];
/** ID del documento corrente (se si modifica un documento esistente) */
docId: string | null;
/** Titolo del documento corrente */
title: string;
}
La funzione extractParams elabora i percorsi URL:
Pattern: /{country}/{lang}/products
Percorso: /us/en/products
Algoritmo:
1. Normalizza entrambi (rimuove le barre finali)
2. Divide in segmenti: ["us", "en", "products"] e ["{country}", "{lang}", "products"]
3. Confronta il numero di segmenti (deve essere uguale)
4. Per ogni coppia di segmenti:
- Se il pattern inizia con : o {}, estrae il parametro
- Altrimenti, deve corrispondere esattamente
5. Restituisce: { country: "us", lang: "en" }
// Associazione semplice (usa il campo "code" per la ricerca)
{ "lang": "language" }
// Associazione dettagliata (campo slug personalizzato)
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
Quando recuperi tramite documents.get(schema, identifier):
idcontent.codecontent.slugtitleQuesto consente di fare riferimento ai documenti in modo flessibile tramite qualsiasi identificatore univoco.