---
term: 'Campi Pointer vs. Relation nella modellazione dei dati BaaS'
seoTitle: 'Campi Pointer vs. Relation: modellazione dei dati in BaaS'
headline: 'Campi Pointer vs. Relation nella modellazione dei dati BaaS'
slug: pointer-vs-relation
category: database
shortDefinition: 'Un campo Pointer è un riferimento tipizzato a un singolo oggetto; un campo Relation è un join gestito che collega molti oggetti a molti altri.'
relatedTerms:
  - data-modeling
  - relational-queries-document-databases
  - database-schema
  - auto-generated-database-apis
contrastsWith:
  - relational-queries-document-databases
aboutTerms:
  - 'Pointer Fields'
  - 'Relation Fields'
faq:
  - question: 'Qual è la differenza fra un Pointer e una Relation in un modello di dati BaaS?'
    answer: 'Un Pointer memorizza un solo riferimento dentro l''oggetto stesso – una chiave esterna con un tipo – così ogni oggetto punta esattamente a un bersaglio. Una Relation memorizza un insieme illimitato di riferimenti in una struttura di join nascosta che la piattaforma gestisce. I Pointer modellano l''uno-a-uno e l''uno-a-molti; le Relation modellano il molti-a-molti, dove le liste di appartenenza crescono senza limite.'
  - question: 'Come si modella una relazione uno-a-molti con i Pointer?'
    answer: 'Mettendo il Pointer sul lato "molti": ogni Comment porta un Pointer al suo Post, esattamente come una chiave esterna. Per elencare i commenti di un post, si interroga la classe Comment dove il Pointer è uguale a quel post. Ogni oggetto resta piccolo, le scritture restano economiche e il pattern funziona a qualsiasi scala – il numero di figli non gonfia mai il genitore.'
  - question: 'Quando usare un array di Pointer invece di una Relation?'
    answer: 'Quando la lista è piccola, limitata e di solito letta insieme al genitore – i dieci ingredienti di una ricetta, le righe di un ordine. Gli array viaggiano dentro l''oggetto, quindi una sola chiamata include() recupera tutto; ma ogni elemento fa crescere l''oggetto, e oltre qualche centinaio di voci letture e salvataggi rallentano. Le liste illimitate o condivise appartengono alle Relation.'
  - question: 'Si può interrogare attraverso un Pointer senza recuperare i due oggetti separatamente?'
    answer: 'Sì – è proprio ciò che fa include(). Interrogando Post con include(''author''), la piattaforma risolve ogni Pointer lato server e restituisce gli oggetti autore completi in una sola risposta; la notazione a punti arriva più in profondità, come in include(''author.company''). Il filtro funziona anche nell''altra direzione: matchesQuery() seleziona i genitori in base a condizioni sull''oggetto puntato.'
  - question: 'Come funzionano i campi Relation sotto il cofano?'
    answer: 'La piattaforma mantiene una tabella di join nascosta per ogni campo Relation, in cui memorizza coppie di ID di oggetti – la stessa struttura che uno schema relazionale chiamerebbe tabella di collegamento, senza che nessuno debba progettarla o darle un nome. Le query di appartenenza colpiscono direttamente quella struttura, così nessuno dei due lati della relazione cresce di dimensione per quanti collegamenti si accumulino.'
  - question: 'I campi Pointer sono indicizzati automaticamente?'
    answer: 'Meglio non darlo per scontato – i campi Pointer vanno trattati come qualsiasi altro filtro di query, indicizzando quelli che le query attraversano. Una ricerca uno-a-molti scansiona la classe figlia alla ricerca di un Pointer corrispondente, e senza indice è una scansione completa della collection. La regola fissa dell''indicizzazione dei database vale invariata: indicizzare ciò che si interroga, soprattutto i percorsi di join.'
  - question: 'Qual è più veloce, un Pointer o una Relation?'
    answer: 'In genere i Pointer – si risolvono dentro la stessa query tramite include(), senza nessuna struttura di join da consultare. Le Relation costano una query in più o un join interno sulla tabella nascosta; è il prezzo della cardinalità illimitata. La regola pratica: scegliere lo strumento più economico che la cardinalità consente – prima il Pointer, poi l''array, per ultima la Relation.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Parse SDK guide — relational data'
    url: 'https://docs.parseplatform.org/js/guide/'
  - name: 'One-to-many data model (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/One-to-many_(data_model)'
  - name: 'MongoDB — model relationships between documents'
    url: 'https://www.mongodb.com/docs/manual/applications/data-models-relationships/'
  - name: 'Cardinality in data modeling (Wikipedia)'
    url: 'https://en.wikipedia.org/wiki/Cardinality_(data_modeling)'
cta:
  title: 'Modella le relazioni senza progettare tabelle di join'
  text: 'Back4app ti dà i campi Pointer e Relation come tipi di colonna di prima classe: collega uno-a-molti e molti-a-molti nella dashboard, interrogali con include() da qualsiasi SDK e lascia che la piattaforma mantenga le strutture di join.'
  linkText: 'Inizia gratis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-10-23'
translationKey: pointer-vs-relation-fields
---

**Un campo Pointer è un riferimento tipizzato a un singolo oggetto; un campo Relation è un join gestito che collega molti oggetti a molti altri.** Entrambi rispondono alla domanda che ogni schema affronta – come si referenziano i record fra loro? – ma a cardinalità e costi diversi. Scegliere fra i due è la decisione centrale della [modellazione dei dati](/glossary/it/modellazione-dei-dati/) su un BaaS, e la buona notizia è che la decisione si comprime in una sola domanda: quanti, da ciascun lato?

## Punti chiave

| Domanda | Risposta |
| --- | --- |
| Pointer | Un riferimento tipizzato memorizzato *nell'*oggetto – una chiave esterna con una classe |
| Relation | Un insieme illimitato di riferimenti in una tabella di join gestita dalla piattaforma |
| Uno-a-molti | Pointer sul lato "molti", sempre |
| Molti-a-molti | Relation – oppure un array di Pointer quando la lista è piccola e limitata |
| Gli strumenti di attraversamento | `include()` risolve i Pointer; una query sulla Relation recupera l'insieme del join |

## Le due forme, in codice

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Pointer: one query, one hop — include() resolves the reference server-side
const posts = new Parse.Query('Post');
posts.equalTo('status', 'published');
posts.include('author');                  // pointer → full Author object
const page = await posts.find();
const name = page[0].get('author').get('displayName');

// Relation: the unbounded join set gets its own query
const tags = await page[0].relation('tags').query().find();
// tags is a plain array of Tag objects — the join table stays invisible
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Pointer: one query, one hop — includeObject resolves the reference server-side
final posts = QueryBuilder<ParseObject>(ParseObject('Post'))
  ..whereEqualTo('status', 'published')
  ..includeObject(['author']);            // pointer → full Author object
final response = await posts.query();
final post = response.results!.first as ParseObject;
final author = post.get<ParseObject>('author');

// Relation: the unbounded join set gets its own query
final relation = post.getRelation('tags');
final tags = await relation.getQuery().query();
// tags.results is a plain list of Tag objects — the join table stays invisible
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Pointer: one query, one hop — include() resolves the reference server-side
let posts = Post.query("status" == "published")
  .include("author")                      // pointer → full Author object
posts.find { result in
  if case .success(let page) = result {
    print(page.first?.author?.displayName ?? "")
  }
}

// Relation: the unbounded join set gets its own query
let tags = Tag.query(related(key: "tags", object: try post.toPointer()))
tags.find { result in
  if case .success(let tagList) = result { render(tagList) }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Pointer: one query, one hop — include() resolves the reference server-side
val posts = ParseQuery.getQuery<ParseObject>("Post")
posts.whereEqualTo("status", "published")
posts.include("author")                   // pointer → full Author object
posts.findInBackground { page, e ->
    val author = page?.firstOrNull()?.getParseObject("author")
    println(author?.getString("displayName"))
}

// Relation: the unbounded join set gets its own query
val relation = post.getRelation<ParseObject>("tags")
relation.query.findInBackground { tags, e ->
    if (e == null) render(tags)           // the join table stays invisible
}
```

L'equivalente in un database relazionale rende esplicita la corrispondenza – un Pointer è una chiave esterna tipizzata; una Relation è una tabella di collegamento che non serve mai creare:

```sql
-- Pointer: una colonna sulla riga figlia (uno-a-molti)
CREATE TABLE comment (
  id         serial PRIMARY KEY,
  post_id    integer REFERENCES post(id),   -- ← il "Pointer"
  body       text
);

-- Relation: una tabella di collegamento (molti-a-molti) – un BaaS la costruisce da sé
CREATE TABLE post_tags (
  post_id    integer REFERENCES post(id),
  tag_id     integer REFERENCES tag(id),
  PRIMARY KEY (post_id, tag_id)
);
```

## Come si risolve ciascuno al momento della query

```mermaid
flowchart TB
  accTitle: Risoluzione dei Pointer vs. attraversamento delle Relation
  accDescr: Una query con Pointer risolve l'oggetto referenziato inline tramite include, restituendo una sola risposta. Una query su una Relation consulta prima una tabella di join nascosta di coppie di ID, poi recupera gli oggetti corrispondenti sul lato opposto.
  Q1["Query Post<br/>include('author')"] --> P["Pointer risolto inline"]
  P --> R1["Una risposta:<br/>post + autori completi"]
  Q2["post.relation('tags')<br/>.query()"] --> J["Tabella di join nascosta<br/>coppie (postId, tagId)"]
  J --> R2["Seconda ricerca:<br/>oggetti Tag corrispondenti"]
```

Il percorso del Pointer è quello economico: [`include()`](https://docs.parseplatform.org/js/guide/) dice al server di sostituire ogni riferimento con l'oggetto completo prima di rispondere – un solo round trip, profondità arbitraria con la notazione a punti (`include('author.company')`), e la cura standard per il pattern N+1 che recupera i figli in un ciclo. Il filtro funziona attraverso lo stesso salto: `equalTo('author', pointer)` trova i commenti di un post, e `matchesQuery()` filtra una classe in base a condizioni su un'altra – l'intera cassetta degli attrezzi è coperta in [query relazionali nei database documentali](/glossary/it/join-nei-database-documentali/).

Il percorso della Relation offre qualcosa di diverso. Poiché l'appartenenza vive nella struttura di join e non in uno dei due oggetti, un utente può appartenere a diecimila gruppi e un gruppo può contenere un milione di utenti senza che nessuno dei due documenti cresca di un solo byte. Il costo è un salto in più: `include()` non attraversa le Relation – l'insieme del join richiede una query a parte.

Fra i due sta l'**array di Pointer**: un campo lista che contiene riferimenti tipizzati. È l'economia del Pointer applicata a un piccolo insieme – un solo `include()` recupera tutti gli elementi – ma l'array vive dentro l'oggetto, quindi ogni elemento rende l'oggetto più pesante da leggere, salvare e sincronizzare. Oltre qualche centinaio di voci l'oggetto contenitore diventa il collo di bottiglia, ed è il segnale che una Relation è stata modellata come array.

## Pointer vs. array di Pointer vs. Relation

### Quale strumento di relazione conviene usare?

| Dimensione | Pointer | Array di Pointer | Relation |
| --- | --- | --- | --- |
| Cardinalità | Un bersaglio | Pochi, limitati | Illimitata, molti-a-molti |
| Dove si memorizza | Nell'oggetto | Nell'oggetto | Tabella di join nascosta |
| Recupero con il genitore | `include()` | `include()` | Query separata sulla Relation |
| Crescita dell'oggetto | Nessuna | Per elemento | Nessuna, mai |
| Ordinamento | n/d | Preservato | Non garantito |
| Esempio canonico | Comment → Post | Ordine → righe d'ordine | Utenti ↔ Gruppi |

Due dettagli meritano attenzione. Gli array preservano l'ordine degli elementi – le Relation no – quindi una lista ordinata (le tracce di una playlist, i passi di un workflow) è una questione da array a prescindere dalla pressione sulla dimensione. E l'uno-a-molti ha due codifiche: il Pointer sul figlio scala indefinitamente, l'array sul genitore si legge più comodamente; decide il tetto di [cardinalità](https://en.wikipedia.org/wiki/Cardinality_(data_modeling)), non il gusto.

## Casi d'uso comuni

- **Proprietà e paternità.** `createdBy`, `author`, `owner` – Pointer uno-a-uno e uno-a-molti; il riferimento che ogni classe finisce per portare.
- **Thread di commenti e feed di attività.** Pointer sul figlio (`comment.post`), interrogato per genitore – il cavallo di battaglia dell'uno-a-molti illimitato.
- **Tag e categorizzazione.** Post ↔ tag, prodotti ↔ collezioni: molti-a-molti, entrambi i lati illimitati – Relation.
- **Follower e appartenenza ai gruppi.** Il classico grafo sociale – Relation, perché la lista dei follower di un account popolare non deve vivere dentro l'oggetto account.
- **Righe d'ordine e piccoli insiemi ordinati.** Limitati, letti con il genitore, l'ordine conta – qui la comodità degli array di Pointer è pienamente giustificata.

## Pointer o Relation? Matrice decisionale

| Scegliere un Pointer quando… | Scegliere una Relation quando… |
| --- | --- |
| Ogni oggetto referenzia esattamente un bersaglio | Entrambi i lati possono crescere senza limite |
| È un uno-a-molti (Pointer sul figlio) | È un molti-a-molti autentico |
| Serve che `include()` lo recuperi con il genitore | L'insieme si interroga da solo, non con il genitore |
| Il riferimento partecipa a filtri e ordinamenti | Dominano i controlli di appartenenza (X è nel gruppo Y?) |
| Una lista limitata e ordinata sta in un array | Un campo array sta visibilmente gonfiando l'oggetto |

L'euristica compressa: **prima il Pointer, poi l'array, per ultima la Relation** – si sale di livello solo quando la cardinalità lo impone. La maggior parte degli schemi finisce per essere in larghissima parte basata su Pointer, con una manciata di vere Relation a reggere gli archi sociali o tassonomici. Lo [schema](/glossary/it/schema-del-database/) va progettato attorno alle query che verranno eseguite davvero, indicizzando poi i campi Pointer che quelle query attraversano.

## Limiti e trade-off

- **Le Relation costano un salto in più.** Nessun `include()` attraverso una Relation – recuperare i membri è una seconda query, e contarli lato server è l'unico modo sano su larga scala.
- **Gli array si gonfiano in silenzio.** Il modo di fallimento è graduale: l'oggetto che conteneva 20 Pointer ne contiene 2.000 un anno dopo, e ogni lettura paga. Un tetto va fissato nel momento in cui si sceglie l'array.
- **I Pointer hanno bisogno di indici come qualsiasi filtro.** Interrogare i figli per Pointer al genitore senza indice è una scansione della collection travestita da API comoda.
- **Nessuna cancellazione a cascata.** Eliminare un post non elimina i suoi commenti né ripulisce le sue Relation – la gestione degli orfani resta in mano a chi sviluppa, di norma in un trigger di cancellazione lato server.
- **L'integrità referenziale non viene applicata.** Un Pointer può referenziare un oggetto eliminato; la piattaforma non lo impedisce. I riferimenti pendenti vanno trattati come uno stato che il codice può incontrare.

## Pointer e Relation su Back4app

Back4app è una piattaforma open-source di Backend as a Service (BaaS) che combina database gestito, API REST e GraphQL generate automaticamente, autenticazione, archiviazione di file e funzioni serverless con Cloud Code. Pointer e Relation sono tipi di colonna di prima classe nel suo schema: basta creare l'uno o l'altra nella dashboard, e le [API generate automaticamente](/glossary/it/api-generate-automaticamente/) supportano subito `include()`, query sulle Relation e filtri fra classi da ogni SDK – le schede di codice qui sopra girano senza modifiche. Le tabelle di join dietro le Relation vengono create, nominate e mantenute dalla piattaforma, e i trigger di Cloud Code sono la sede naturale della logica di cancellazione a cascata e di pulizia degli orfani che il modello in sé lascia a chi sviluppa.
