2. Corso Couchbase Lite di Beniamino Ferrari 2
Data Modeling
● Couchbase è un document database
● Diversamente dai database tradizionali, i dati
vengono memorizzati in documenti invece di
usare righe di tabelle
● Un documento è un oggetto JSON contenente
un certo numero di coppie chiave valore
● Le entità e le relazioni tra di esse sono
mantenute all'interno di documenti
3. Corso Couchbase Lite di Beniamino Ferrari 3
JSON Overview (1)
● JSON, che sta per JavaScript Object Notation,
è formato per la rappresentazione di dati:
– Leggero
– Easily Parsed
– Cross-Platform
– Ampiamente diffuso in ogni linguaggio, framework e
nel Javascript di ogni browser
4. Corso Couchbase Lite di Beniamino Ferrari 4
JSON Overview (2)
● JSON supporta gli stessi valori e tipe supporati
da JavaScript. Questi sono:
– Number: un number rappresenta un numero che
può essere un tipo intero i decimale a virgola
mobile. Javascript supporta un massimo di 2^53. Se
vengono utilizzati numeri a 64 bit dovrebbero
essere memorizzati come stringhe.
– String: una stringa deve essere chiusa fra doppi
apici e supportare i caratteri Unicode e i caratteri di
escape. Per esempio: "A String"
5. Corso Couchbase Lite di Beniamino Ferrari 5
JSON Overview (3)
– Boolean: un boolean è true o false. Per esempio: { "value": true}
– Array: un array è un lista di valori chiusi tra parentesi quadre. Per esempio:
["one", "two", "three"]
– Object: un object è un insieme di coppie chiave valore (array associativo o hash).
La chiave deve essere una stringa, ma il valore può essere uno qualsiasi dei
valori rappresentati in JSON. Per esempio:
{
"servings" : 4,
"subtitle" : "Easy to make in advance, and then cook when ready",
"cooktime" : 60,
"title" : "Chicken Coriander
}
6. Corso Couchbase Lite di Beniamino Ferrari 6
Da tabelle a JSON (1)
● Il punto di partenza per modellare un database NoSQL
è di osservare come viene memorizzata una entity
denormalizzata memorizzata in un singolo documento:
ID FIRS_NAME LAST_NAME EMAIL
100 John Smith johnsmith@libero.it
L'equivalente JSON corrisponde al documento:
{
"id": “contact100”,
“type”:”contact”,
"first_name":"John",
"last_name ":"Smith",
"email": "johnsmith@libero.it"
}
7. Corso Couchbase Lite di Beniamino Ferrari 7
Da tabelle a JSON (2)
● Se volessimo memorizzare una entità con relazione 1 a 1 per es l'indirizzo del contatto potremmo
modellare il json nel seguente modo:
{
"id": “contact100”,
“type”:”contact”,
"first_name":"John",
"last_name ":"Smith",
"email": "johnsmith@libero.it",
"address": {
"address_line": "123 Main Street",
"city": "Mountain View",
"country": "US"
}
}
8. Corso Couchbase Lite di Beniamino Ferrari 8
Relazioni uno a molti
● La cosa diventa interessante quando il nostro
contatto possiede più record nella relazione,
creando una relazione uno a molti
● Ci sono due modi per modellare una relazione
uno a molti in un document database:
1)Embedded Document
2)Related Document
9. Corso Couchbase Lite di Beniamino Ferrari 9
Embedded Documents (1)
● In un database relazionale, quando un contatto può
avere più indirizzi, gli indirizzi vengono comunemente
memorizzati una tabella separata (ADRESSES table)
ID CONTCT_ID ADDRESS_LINE CITY COUNTRY
200 100 123 Main Street Mountain View US
201 100 123 Market San Francisco US
● In un document database, gli indirizzi potrebbero essere memorizzati
in un array di documenti embedded all'interno del contact document
● L'approccio document embedded riduce il lavoro necessario per
utilizzare con l'oggetto contact, perché non servono informazioni
aggiuntive per risalire alle informazioni embedded
10. Corso Couchbase Lite di Beniamino Ferrari 10
Embedded Documents (2)
{
"id": “contact100”,
“type”:”contact”,
"first_name":"John",
"last_name ":"Smith",
"email": "john.smith@couchbase.com",
"addresses": [
{
"address_line": "123 Main Street",
"city": "Mountain View",
"country": "US"
},
{
"address_line": "123 Market",
"city": "San Francisco",
"country": "US"
}
]
}
11. Corso Couchbase Lite di Beniamino Ferrari 11
Embedded Documents (1)
● In un database relazionale, quando un contatto può
avere più indirizzi, gli indirizzi vengono comunemente
memorizzati una tabella separata (ADRESSES table)
ID CONTCT_ID ADDRESS_LINE CITY CO§UNTRY
200 100 123 Main Street Mountain View US
201 100 123 Market San Francisco US
● In un document database, gli indirizzi potrebbero essere memorizzati
in un array di documenti embedded all'interno del contact document
● L'approccio document embedded riduce il lavoro necessario per
utilizzare con l'oggetto contact, perché non servono informazioni
aggiuntive per risalire alle informazioni embedded
12. Corso Couchbase Lite di Beniamino Ferrari 12
Related Documents (1)
● Ci sono scenari in cui l'approccio embedded document
non è adatto:
– Grande numero di entità nella relazione: inserire un
grande numero di entità embendded formerebbe un grande
documento. Questo formerebbe un documento più lento da
gestire, per via del fatto che dovrebbe essere parsato
interamente per eseguire operazioni di aggiornamento.
– Concorrenza: quando più utenti lavorano un singolo
documento c'è un più alto rischio di conflitti. I Related
Document possono essere usati per isolare aggiornamenti
eseguiti da utenti differenti
13. Corso Couchbase Lite di Beniamino Ferrari 13
Related Documents (2)
● Nelle implementazioni più comuni vengono utilizzati per rispondere alla domanda “Appartiene a?”.
● Considerando lo scenario dove ogni utente possa assegnare uno o più “task” a un contatto e un
contatto possa arrivare ad avere un grande numero estremamente variabile di “task”.
{
"id": "task300",
“type”:”task” ,
"contact_id": "contact100"
"description": "Task details",
"status": "complete"
}
● Con questo tipo di implementazione, gli utenti possono modificare record
concorrentemente senza introdurre casi di conflitto in record di contatti collegati
● Questo tipo di approccio supporta un grande numero di records per contatto senza
impattare alle dimensioni del record del contatto collegato
14. Corso Couchbase Lite di Beniamino Ferrari 14
Couchbase Lite (1)
● Couchbase Lite è un database JSON embedded che può essere usato in
maniera standalone, in una rete P2P o come end point remoto di per un
Couchbase Server.
● Rappresenta dati in in maniera flessibile. I dati hanno una
rappresentazione libera e i record chiamati documenti possono avere
struttura differente.
● Un sofisticato query engine permette di eseguire query in maniera
efficiente su data set di grandi dimensioni a prescindere da come sia
composta la struttura dei dati.
● Dispone API native per C#, IOS e Android, con le quali è possibile
mappare direttamente database a oggetti.
● Le api sono compatibili con Web App che fanno uso di JavaScript o con
app ibride.
15. Corso Couchbase Lite di Beniamino Ferrari 15
Couchbase Lite (2)
● Supporta la replica con database compatibili. Questo predispone la
vostra app alla funzionalità di sincronizzazione efficiente.
● Gli utenti possono mantenere la stessa app sincronizzai su diversi device
e con gli altri utenti
● Supporta la replica peer-to-peer. Aggiungendo un componente HTTP, la
nostra app accetterà connessioni dal altri dispositivi che dispongono di
Couchbase Lite e lo scambio di dati con essi
● Supporta accesso a dati offline. In contrasto con le tradizionali
applicazioni web tradizionali dove si fa uso spesso di richieste e risposte
qui si fa prima accesso ai dati locali. Questo vuol dire che l'applicazione
rimarrà responsiva sia in WIFI, che su reti basate su celle oppure offline.
Gli utenti potranno modificare i propri dati che mentre sono offline e
saranno sincronizzati con il server non appena possibile.
16. Corso Couchbase Lite di Beniamino Ferrari 16
Api Nativa
● I principali componenti dell'API nativa sono:
– Manager
– Database
– Document
– Revision
– Attachment
– View
– Query
– Replication
17. Corso Couchbase Lite di Beniamino Ferrari 17
Manager
● Il Manager è l'oggetto al primo livello che
gestisce una collection di istanze di Couchbase
lite Database.
● È necessario creare un'istanza della classe
Manager prima che sia possibile lavorare con
oggetti Couchbase Lite all'interno della nostra
applicazione
18. Corso Couchbase Lite di Beniamino Ferrari 18
Creare un Manager
● Un oggetto Manager può essere creato chiamando un costruttore o l'inizializzatore della classe Manager
public class Application extends android.app.Application {
private Manager manager;
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
try {
manager = new Manager(new AndroidContext(mContext), Manager.DEFAULT_OPTIONS);
} catch (IOException e) {
Log.e(TAG, "Cannot create Manager instance", e);
return;
}
}
}
19. Corso Couchbase Lite di Beniamino Ferrari 19
Dove si trova il database
● Il Manager crea una directory nel filesystem e
memorizza all'interno di esso il database. Normalmente,
non c'è bisogno di sapere dove si trova.
● In Android il percorso di questa cartella è dato dal
metodo getFilesDir() dell'oggetto Android Context
● In Android è possibile cambiare il percorso di questa
cartella creando una sottoclasse di
com.couchbase.lite.Context e un override del metodo
getFilesDir
20. Corso Couchbase Lite di Beniamino Ferrari 20
Manager Options
● Per default il Manager apre il database con accesso lettura/scrittura. Se si vuole
assicurare che esso non venga modificato è possibile restringere l'accesso al
database in readonly passando un oggetto ManagerOptions al costruttore o
inizializzatore di Manager.
try {
ManagerOptions customOptions = new ManagerOptions();
customOptions.setReadOnly(true);
manager = new Manager(new AndroidContext(mContext), customOptions);
} catch (IOException e) {
Log.e(TAG, "Cannot create Manager instance with custom options", e);
return;
}
21. Corso Couchbase Lite di Beniamino Ferrari 21
Supporto della concorrenza
● In Java tutti gli Oggetti Couchbase Lite possono
essere condivisi liberamente tra thread.
22. Corso Couchbase Lite di Beniamino Ferrari 22
Classe Database
● Un Database è sia un contenitore e che un namespace
per documenti, lo scope delle query e il target della
replica.
● I database sono rappresentati della classe Database
● La maggior parte delle applicazioni hanno bisogno di un
solo database, ma è possibile usare il Manager per
crearne altri.
● Ogni database è indipendente, l'approccio multi
database potrebbe essere preso in considerazione nel
caso di un'applicazione multi utente.
23. Corso Couchbase Lite di Beniamino Ferrari 23
Database
● Un database possiede i seguenti elementi:
– Nome: deve consistere di solo lettere lowercase
ASCII, cifre, e/o da caratteri speciali _$()+-/
– Documenti: ogni documento è definito
univocamente dal suo ID
– Views: ogni view ha un nome univoco e un indice
persistente
– Filter function
– Replications
24. Corso Couchbase Lite di Beniamino Ferrari 24
Document (1)
● In un document database come Couchbase Lite, l'entità
memorizzata in un database è chiamato documento
invece di “righa” o “record”
● In Couchbase Lite (come in Couchbase Server e
CouchDB) il corpo di un documento ha la forma di un
oggetto JSON
● Un oggetto JSON è collezione una coppie chiave/calore
nella quale i valori possono essere di tipo differente di
dati: numeri, stringhe, array o oggetti nidificati
25. Corso Couchbase Lite di Beniamino Ferrari 25
Document (1)
● In un document database come Couchbase Lite, l'entità
memorizzata in un database è chiamato documento
invece di “righa” o “record”
● In Couchbase Lite (come in Couchbase Server e
CouchDB) il corpo di un documento ha la forma di un
oggetto JSON
● Un oggetto JSON è collezione una coppie chiave/calore
nella quale i valori possono essere di tipo differente di
dati: numeri, stringhe, array o oggetti nidificati
26. Corso Couchbase Lite di Beniamino Ferrari 26
Document (2)
● Ogni documento è identificato da un document ID, che può
essere generato automaticamente come UUID o
determinato all'interno dell'applicazione. L'unico vincoli sono
che debba essere unico all'interno del database che non
possa essere cambiato
● In aggiunta, un documento può contenere allegati binari
chiamati blobs che sono utili per contenere file multimediali
o altri dati non testuali
● Couchbase Lite supporta gli allegati di dimensioni illimitate,
anche se il Gateway di sincronizzazione attualmente impone
un limite di 10 MB.
27. Corso Couchbase Lite di Beniamino Ferrari 27
Document (3)
● Couchbase lite tiene traccia dello storico di ogni
documento, come una serie di revisioni
● Funziona come sistema di versioning simile a
GIT o Subversion
● Ogni volta che un documento viene creato o
aggiornato gli viene assegnato un nuovo e
unico revision ID
28. Corso Couchbase Lite di Beniamino Ferrari 28
Document (4)
● Un documento è composto da i seguenti
attributi:
– Document ID
– Current Version ID che viene aggiornato ad ogni
document update
– Uno storico delle revisioni passate
– Un corpo formato da un Oggetto JSON
– Da zero o più allegati
29. Corso Couchbase Lite di Beniamino Ferrari 29
CRUD di un documento
● Couchbase Lite supporta le tipiche operazioni
CRUD su documenti:
– Create
– Read
– Update
– Delete.
30. Corso Couchbase Lite di Beniamino Ferrari 30
Creare Documenti
● È possibile creare un documento assegnando
ad esso un ID. Se non è necessario definire un
proprio ID chiamando la il metodo
createDocument della classe Database l'ID
viene generato casualmente nella forma
Universally Unique ID (UUID)
31. Corso Couchbase Lite di Beniamino Ferrari 31
Esempio Creare Documenti (1)
● Map<String, Object> properties = new HashMap<String, Object>();
properties.put("type", "list");
properties.put("title", title);
properties.put("created_at", currentTimeString);
properties.put("owner", "profile:" + userId);
properties.put("members", new
ArrayList<String>());
Document document = database.createDocument();
document.putProperties(properties);
● Questo esempio mostra come creare un documento con UUID assegnato
automaticamente
32. Corso Couchbase Lite di Beniamino Ferrari 32
Creazione con ID Assegnato
● Se volessimo creare un nuovo documento e assegnare
ad esso un document ID, dovremo:
– chiamare il metodo getDocument, come se dovessimo
recuperare un documento esistente;
– se il documento non esiste ancora otterremo comunque un un
oggetto Document valido, esso non avrà nessun contenuto ne
revisione;
– la prima volta che viene impostato il documento viene
memorizzato in maniera persistente nel database;
– se esiste un documento con lo stesso ID a database
memorizzandolo verrà prodotto un errore;
33. Corso Couchbase Lite di Beniamino Ferrari 33
Creazione con ID assegnato dal
programmatore
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("title", "Little, Big");
properties.put("author", "John Crowley");
properties.put("published", 1982);
Document document = database.getDocument("978-0061120053");
try {
document.putProperties(properties);
} catch (CouchbaseLiteException e) {
Log.e(TAG, "Cannot save document", e);
}
● Questo esempio mostra come creare un documenti assegnandogli un ID
● Questo è l'ideale nei casi in cui esista già un ID univoco come il codice fiscale
34. Corso Couchbase Lite di Beniamino Ferrari 34
Leggere un Documento
● Per ottenere un Documento dato il suo ID è
necessario utilizzare getDocument
● Gli oggetti document sono unici come gli ID dei
documenti
● Istanziando due documenti con lo stesso ID si
ottiene sempre il riferimento allo stesso oggetto
35. Corso Couchbase Lite di Beniamino Ferrari 35
Esempio di lettura
Document doc = database.getDocument(myDocId);
// Possiamo accedere alle proprietà attraverso
l'oggetto document:
doc.getProperty("title");
// o attraverso le proprietà del suo dizionario
Map<String, Object> properties = doc.getProperties();
String owner = (String) properties.get("owner");
36. Corso Couchbase Lite di Beniamino Ferrari 36
Aggiornare un documento (1)
● Ci sono due metodi per aggiornare un
documento:
– putProperties
– update
● putProperties è più semplice, dato un nuovo
oggetto JSON, sostituisce con esso il corpo del
documento. Con le proprietà del nuovo oggetto
viene creata una nuova revisione che diviene la
revisione corrente del documento.
37. Corso Couchbase Lite di Beniamino Ferrari 37
Esempio di putProperty
Document doc = database.getDocument(myDocID);
Map<String, Object> properties = new HashMap<String, Object>();
properties.putAll(doc.getProperties());
properties.put("title", title);
properties.put("notes", notes);
try {
doc.putProperties(properties);
} catch (CouchbaseLiteException e) {
e.printStackTrace();
}
38. Corso Couchbase Lite di Beniamino Ferrari 38
Aggiornare un documento (2)
● Da notare che multiple update usando
putProperties all'interno di una singola transazione
non sono supportate.
● L'update è un metodo riceve in ingresso un
DocumentUpdater, che possiede un metodo callback
(update) che riceve in ingresso della classe
UnsavedRevision le quali proprietà sono una copia
mutabili delle proprietà correnti.
● La callback può modificare l'oggetto UnsavedRevision
come meglio crede
39. Corso Couchbase Lite di Beniamino Ferrari 39
Esempio di Update
Document doc = database.getDocument(myDocId);
doc.update(new Document.DocumentUpdater() {
@Override
public boolean update(UnsavedRevision newRevision) {
Map<String, Object> properties = newRevision.getUserProperties();
properties.put("title", title);
properties.put("notes", notes);
newRevision.setUserProperties(properties);
return true;
}
});
40. Corso Couchbase Lite di Beniamino Ferrari 40
Conflitti
● In entrambe i modi in cui memorizziamo i cambiamenti,
è necessario prendere in considerazione della
possibilità di conflitti di aggiornamento
● Couchbase Lite utilizza un Multiversion Concurrency
Control (MVCC) a guardia di aggiornamenti simultanei
di un documento
● Anche nel caso di un'applicazione single thread esiste
un thread replicator che gira in background che
potrebbe creare delle revisioni mentre stiamo
eseguendo delle modifiche
41. Corso Couchbase Lite di Beniamino Ferrari 41
Conflitti
● La tipica sequenza che crea conflitti è:
1)il nostro thread legge le proprietà del documento corrente e
costruisce una copia modificata che deve essere memorizzata;
2)un altro thread aggiorna il documento, creando una nuova
revisione con proprietà differenti;
3)il nostro thread aggiorna il documento con le proprietà
modificate;
● Chiaramente se il nostro thread avesse il permesso di
procedere i cambiamenti dello step 2 verrebbero
sovrascritti e persi. Invece, la modifica fallirà con un conflict
error.
42. Corso Couchbase Lite di Beniamino Ferrari 42
Gestione Conflitti
● Le due modalità hanno un comportamento
diverso:
– putProperties semplicemente restituisce un errore
da gestire. E' necessario intercettare questo tipo di
errore e gestirlo ad es. ritentando la modifica
– update è più intelligente gestisce autonomamente
gli errori di conflitto e ritentando la modifica fino a
che non ci sono conflitti
● È raccomandabile usare a seconda, tenendo
presente che possa essere chiamata più volte.
43. Corso Couchbase Lite di Beniamino Ferrari 43
Cancellazione di Documenti
● La cancellazione di un Documento crea un
nuova revisione, chiamata tombstone, che
possiede la proprietà _delete impostata a true.
Questo assicura che l'eliminazione venga
replicata sul server e poi su tutti gli altri client
che leggono dati dal database
Document task = (Document)
database.getDocument("task1");
task.delete();
44. Corso Couchbase Lite di Beniamino Ferrari 44
Preservare proprietà dopo la
cancellazione
● Nel caso fosse necessario preservare uno o più campi all'interno di un documento, ad esempio, chi ha
eliminato e quando è stato eliminato, sarà necessario modificare il documento e impostare il campo
deletion di UnsavedRevision a true
Document doc = database.getDocument(myDocId);
doc.update(new Document.DocumentUpdater() {
@Override
public boolean update(UnsavedRevision newRevision) {
newRevision.setIsDeletion(true);
Map<String, Object> properties = newRevision.getUserProperties();
properties.put("deleted_at", currentTimeString);
newRevision.setUserProperties(properties);
return true;
}
})
45. Corso Couchbase Lite di Beniamino Ferrari 45
Notifiche di modifica a un
Documento (1)
● È possibile intercettare gli eventi di modifica o
eliminazione di un particolare documento
● Può essere utile per le seguenti motivazioni:
– notificare quando una nuova revisione viene
aggiunta a un documento;
– notificare quando un documento viene eliminato;
– notificare quando il documento entra nello stato di
conflitto;
46. Corso Couchbase Lite di Beniamino Ferrari 46
Notifiche di modifica a un
Documento (2)
Document doc = database.createDocument();
doc.addChangeListener(new Document.ChangeListener() {
@Override
public void changed(Document.ChangeEvent event) {
DocumentChange docChange = event.getChange();
String msg = "New revision added: %s. Conflict: %s";
msg = String.format(msg,
docChange.getAddedRevision(), docChange.isConflict());
Log.d(TAG, msg);
}
});
doc.createRevision().save();
47. Corso Couchbase Lite di Beniamino Ferrari 47
Purge di un documento
● Eseguire il purge di un documento è differente
della eliminazione, il metodo purge rimuove
ogni traccia del documento locale, non ha
nessun effetto sulla replica nei database remoti
● Purge è un modo per recuperare spazio, ad es.
se il eseguo il purge di un documento e
successivamente viene modificato su di un
server remoto, alla prossima replica il
documento verrà ripristinato.
48. Corso Couchbase Lite di Beniamino Ferrari 48
Proprietà Speciali
● Il corpo di un documento contiene alcune proprietà
speciali che contengono metadati
– _id: ID del documento.
– _rev: ID revisione.
– _attachments: metadati degli allegati
– _deleted: appare solamente durante una revisione di
eliminazione (tombstone) , dove ha valore true.
● Un underscore iniziale denota sempre una proprietà
speciale e perciò non dovrebbe essere usato come
prefisso
49. Corso Couchbase Lite di Beniamino Ferrari 49
Revisioni (1)
● Couchbase Lite utilizza le revisioni per risolvere
conflitti durante la replica
● Una delle differenze significative rispetto agli
altri database è il versioning dei documenti
(MVCC Multiversioning Concurrency
Control) che gestisce conflitti tra più scrittori
● Ogni documento ha un campo speciale _rev
che con contiene ID delle revisione, che viene
assegnato ad ogni aggiornamento
50. Corso Couchbase Lite di Beniamino Ferrari 50
Revisioni (2)
● Quando dobbiamo memorizzare un documento
aggiornandone uno esistente, è obbligatorio
fornire l'ID della versione corrente.
● Se la versione corrente non coincide con quella
effettiva, l'aggiornamento viene interrotto,
questo sta indicare che un altri cliente ha fatto
una modifica prima.
● Sarà necessario creare una nuova versione e
ritentare l'aggiornamento
51. Corso Couchbase Lite di Beniamino Ferrari 51
Revisioni (3)
● In Couchbase lite non è possibile utilizzare le
versioni per memorizzare e recuperare lo
storico dei cambiamenti di una pagina, come in
un SVN, ma le versioni vengono memorizzate e
utilizzate solo per risolvere conflitti
● Le versioni non vengono mai replicate e
periodicamente eliminate nel processo di
compattazione
52. Corso Couchbase Lite di Beniamino Ferrari 52
Allegati
● Gli allegati memorizzano dati associati al documento,
ma non sono parte dell'oggetto JSON
● Il loro compito principale è quello di rendere efficiente
le memorizzazioni di grandi dati binari in un
documento
● Un documento può avere zero o più allegati con nome
differente e un MIME type
● Rendono la replica efficiente, nel caso un documento
possedesse degli allegati solamente quelli che sono
stati modificati verranno trasferiti attraverso la rete
53. Corso Couchbase Lite di Beniamino Ferrari 53
Leggere allegati
● La classe Revision possiede alcuni metodi per accedere agli allegati
– attachmentNames restituisce i nomi di tutti gli allegati.
– attachmentNamed restituisce un oggetto Attachment dato il nome.
– attachments restituisce tutti gli oggetti Attachment.
// Carica un allegato JPEG in un Drawable:
Document doc = database.getDocument("Robin");
Revision rev = doc.getCurrentRevision();
Attachment att = rev.getAttachment("photo.jpg");
if (att != null) {
InputStream is = att.getContent();
Drawable d = Drawable.createFromStream(is, "src name");
}
54. Corso Couchbase Lite di Beniamino Ferrari 54
Memorizzare un allegato
● Per creare un allegato, è necessario:
– creare prima un oggetto UnsavedRevision mutabile,
attraverso, il metodo createRevision della versione corrente
del documento;
– utilizzare il metodo setAttachment sulla nuova revisione per
aggiungere il nuove allegato;
– e per ultimo, chiamare il metodo save sulla nuova revisione;
● L'aggiornamento funziona esattamente alla stessa
maniera chiamando il metodo setAttachment si
sostituisce l'allegato mantenendo lo stesso nome
55. Corso Couchbase Lite di Beniamino Ferrari 55
Es. Aggiunta allegato
// Aggiungo un'immagine come allegato dopo che viene invocato la camera activity di
Android
protected void onActivityResult(int requestCode, int resultCode, Intent data){
InputStream stream = null;
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
InputStream stream = getContentResolver().openInputStream(data.getData());
// Aggiungo o aggiorno un'immagine al documento come allegato
Document doc = database.getDocument("Robin");
UnsavedRevision newRev = doc.getCurrentRevision().createRevision();
newRev.setAttachment("photo.jpg", "image/jpeg", stream);
newRev.save();
}
}
56. Corso Couchbase Lite di Beniamino Ferrari 56
Rimozione di un allegato
● Per la rimozione dell'allegato è necesarrio chiamarre il
metodo removeAttachment, invece, di setAttachment
// Rimuovo l'allegato dal documento:
Document doc = database.getDocument("Robin");
UnsavedRevision newRev =
doc.getCurrentRevision().createRevision();
newRev.removeAttachment("photo.jpg");
57. Corso Couchbase Lite di Beniamino Ferrari 57
Lo storage degli Allegati
● Generalmente, non è necessario pensare come
Couchbase Lite memorizzi i dati, ma dato che gli
allegati possono occupare molto spazio può essere
utile sapere dove e come viegono gestititi
● Gli allegati non sono memorizzati nel database stesso,
ma invece in singoli file.
● Ogni allegato ha un nome criptico che corrisponde al
digest del contenuto usando SHA1, di conseguenza,
vengono deduplicati perché a più nomi uguali
corrisponde lo stesso contenuto
58. Corso Couchbase Lite di Beniamino Ferrari 58
Eliminazione allegati
● Aggiornando o eliminando un allegando un
documento non corrisponde ad una immediata
eliminazione del file corrispondente
● Il file allegato rimane memorizzato fino a quando
esiste una revisione del documento collegato
● Verrà eliminato quando avviene il processo di
compattazione dopo che è avvenuta un'operazione di
modifica o eliminazione su di esso.
59. Corso Couchbase Lite di Beniamino Ferrari 59
View (1)
● Una view è un indice persistente all'interno dei
documenti del database
● Couchbase Lite non ha un linguaggio per le richieste
come SQL, ma usa una tecnica chiamata
map/reduce per generare indici (views) secondo un
criterio definito all'interno dell'applicazione
● Il principale componente è la sua map function
che viene scritta nello stesso linguaggio utilizzato
dalla nostra applicazione
60. Corso Couchbase Lite di Beniamino Ferrari 60
View (2)
● La view genera una qualsiasi numero di coppie chiave/valore
che si desidera indicizzare
● Per esempio potrebbe cercare all'interno di un database
“rubrica” e produrre un insieme di nomi associati a numeri di
telefono
● L'indice ottenuto è persistente e viene aggiornato quando
viene aggiornato il documento
● Una view potrà avere una reduce function che può essere
usata per combinare più righe in una. Può essere usata per
compiere funzioni aggregative come totali, media o
raggruppare righe secondo un criterio comune
61. Corso Couchbase Lite di Beniamino Ferrari 61
Ordinamento dei risultati
● null
● false, true (in questo ordine)
● Numbers, in ordine numerico
● Strings, case-insensitive. In ordine definito dal Unicode Collation
Algorithm.
● Arrays, confrontati elemento per elemento come descritto prima.
● Maps/dictionaries, confrontati elemento per elemento come descritto
prima. Sfortunatamente l'ordine degli elementi al loro interno è
ambiguo (dato che JSON non specifica nessun ordinamento delle
chiavi, e nella maggior parte delle implementazioni delle hash table
l'ordine delle chiavi è casual) perciò l'uso di queste chiavi è
sconsigliato.
62. Corso Couchbase Lite di Beniamino Ferrari 62
Creare e inizializzare una View
● Un oggetto view appartiene a un Database
● È possibile creare o recuperare chiamando il metodo
getView di database, che creerà e restituirà una nuova
view se non ne esiste già una
● Nonostante la view sia persistente, la sua map o reduce
function non lo sono, non sono nient'altro che metodi
vengono registrati a runtime, prima di venire invocati.
● È buona norma impostare le view quando l'applicazione
viene avviata dopo l'apertura del database
63. Corso Couchbase Lite di Beniamino Ferrari 63
Esempio creazione di una View
// Creazione di una view e registrazione della sua map function
// Questa funzione permette di conoscere il nome della persona dato uno dei suoi numeri di telefono
View phoneView = database.getView("phones");
phoneView.setMap(new Mapper() {
@Override
public void map(Map<String, Object> document, Emitter emitter) {
List<String> phones = (List) document.get("phones");
for (String phone : phones) {
emitter.emit(phone, document.get("name"));
}
}
}, "2");
● Il secondo parametro di setMap sta ad indicare la versione, se lo sviluppatore dovesse cambiare il
comportamento della map function dovrebbe incrementare il suo valore, in modo tale che al prossimo
avvio gli indici vengano rigenerati
64. Corso Couchbase Lite di Beniamino Ferrari 64
Map Function
● Il compito di una map function è di cercare all'interno del contenuto JSON di un
documento e da quello produrre (emit) zero o più coppie chiave/valore che devono essere
indicizzate
function(doc) {
if (doc["type"] == "person")
emit(doc["name"], doc["phone"]);
}
● Questa funzione JavaScript, agisce in database che contengono documenti rappresentanti
persone, che vengono taggati come “person”, usando proprietà type
● L'uso della proprietà type è di uso comune
● La map function semplicemente controlla se il documento rappresenta una “person”, e se
vero chiama la emit per aggiungere “name” e “phone” all'indice
● Sarà possibile inoltre richiedere range di nomi o richiederli in ordine alfabetico
● Le map function sono chiamate dagli indexer e devono rispettare determinati requisiti
65. Corso Couchbase Lite di Beniamino Ferrari 65
Regole da rispettare in map function
1) Devono essere puramente funzioni: questo significa
che se chiamate con un certo input devono produrre
sempre lo stesso output, non possono usare perciò uno
stato esterno ma solamente il JSON in input
2) Non possono avere “effetti collaterali”, non devono
cambiare valori all'esterno della funzione, è imprevedibile
quando o quanto spesso vengano chiamate
3) Devono essere thread-safe, potrebbero essere
chiamate in background da thread indexer o da diversi
thread in parallelo
66. Corso Couchbase Lite di Beniamino Ferrari 66
Chiavi e valori
● Entrambi chiave e valore passati alla emit possono essere di
qualsiasi tipo compatibile con oggetti JSON: strings, numbers,
booleans, arrays, dichitionary/maps e il JSON null (differente da
un null reference).
● I valori prodotti dalla emit possono essere prodotti valori del tipo
null reference, ma non per le chiavi
● Le chiavi sono comunemente stringhe, ma ci sono casi in cui può
essere comodo averli come array ad es. quando si vuole ottenere
un ordinamento multilivello.
● Se la emit della map function avesse per chiave [lastname,
firstname] otterremmo l'equivalente di ORDER BY lastname,
firstname
67. Corso Couchbase Lite di Beniamino Ferrari 67
Simulare Join
● Eseguire Join SQL di documenti non è possibile
all'interno di View
● La Join SQL può essere simulata facendo uso
del campo usato per il link.
68. Corso Couchbase Lite di Beniamino Ferrari 68
Esempio simulazione Join (1)
● Considerando due record delle tabelle 'post' e 'comment':
● post con _id 1000
{
"type" : "post",
"title" : "Blog post"
"author" : "Blog author"
}
comment con _id 1002
{
"type" : "comment",
"post_id" : "post_1000"
"author" : "Comment author",
"created_at" : 123498235
}
69. Corso Couchbase Lite di Beniamino Ferrari 69
Esempio simulazione Join (2)
● Creare una view che mostri i post di un blog e tutti records collegati è ottenibile con la
seguente map function:
function(doc, meta)
{
if (doc.post_id && doc.type && doc.type == "post")
{
emit([doc.post_id, null], null);
}
else if (doc.post_id && doc.created_at && doc.type && doc.type == "comment")
{
emit([doc.post_id, doc.created_at], null);
}
}
70. Corso Couchbase Lite di Beniamino Ferrari 70
Reduce function
● Un reduce function post processa le coppie
chiave/valore indicizzate dalla map function,
aggregandone i valori
● Le reduce function sono simili agli operatori di
aggregazione COUNT o AVERAGE dell'SQL, solo
che il programmatore ha l'onere di definirli
● Un reduce function prende in ingresso una lista
ordinata di coppie chiave/valore, aggregandola in un
singolo oggetto che viene restituito da essa
71. Corso Couchbase Lite di Beniamino Ferrari 71
Esempio di reduce function
// Creo una view e reigstro la sua map e reduce function
View phoneView = database.getView("phones");
phoneView.setMapReduce(new Mapper() {
@Override
public void map(Map<String, Object> document, Emitter emitter) {
List<String> phones = (List) document.get("phones");
for (String phone : phones) {
emitter.emit(phone, document.get("name"));
}
}
}, new Reducer() {
@Override
public Object reduce(List<Object> keys, List<Object> values, boolean rereduce) {
return new Integer(values.size());
}
}, "2");
● Il corpo di questa reduce function stampa il numero totale di tutti i numeri di telefono presenti in rubrica
72. Corso Couchbase Lite di Beniamino Ferrari 72
Rereducing
● Il parametro rereduce serve a indicare la reduce
function deve essere chiamata in modalità re-reduce
● In modalità re-reduce non ci sono chiavi e valori
contenuti nel db, ma i valori sono sostituiti con i risultati
delle chiamate delle reduce function svolte in
precedenza
● Questo permette all'indexer di non utilizzare a una view
composta da centinaia di milioni di righe, che porterebbe
la nostra applicazione crash, ma di utilizzare solamente i
risultati ottenuti in precedenza
73. Corso Couchbase Lite di Beniamino Ferrari 73
Rereducing Javascript
function(key, values, rereduce) {
var result = {total: 0, count: 0};
for(i=0; i < values.length; i++) {
if(rereduce) {
result.total = result.total + values[i].total;
result.count = result.count + values[i].count;
} else {
result.total = sum(values);
result.count = values.length;
}
}
return(result);
}
74. Corso Couchbase Lite di Beniamino Ferrari 74
Rereducing Android
// Creo una view e reigstro la sua map e reduce function con parametro rereduce
View phoneView = database.getView("phones");
phoneView.setMapReduce(new Mapper() {
@Override
public void map(Map<String, Object> document, Emitter emitter) {
List<String> phones = (List) document.get("phones");
for (String phone : phones) {
emitter.emit(phone, document.get("name"));
}
}
}, new Reducer() {
@Override
public Object reduce(List<Object> keys, List<Object> values, boolean rereduce) {
if (rereduce) {
return View.totalValues(values);
} else {
return new Integer(values.size());
}
}
}, "2");
75. Corso Couchbase Lite di Beniamino Ferrari 75
Regole per la reduce function
● La reduce function ha le stesse restrizioni della
map function: dev'essere puramente una
funzione che produce sempre lo stesso output
dato un input. Non deve avere side effect ed
essere thread safe. In aggiunta:
– Se il suo output, dovesse essere composto da
un array o un dizionario, allora, dovrà essere
composto da meno elementi del suo input. Se
accadesse il contrario la nostra applicazione
potrebbe andare in crash per carenza di memoria
76. Corso Couchbase Lite di Beniamino Ferrari 76
Query
● Una query è la ricerca di risultati dato l'indice di una view.
● Couchbase Lite le query sono oggetti della classe Query
● Per eseguire una query è necessario creare un oggetto
della Classe query e personalizzarne gli attributi come
range e massimo numero di righe e poi eseguirla
● Il risultato è contenuto in un oggetto di tipo
QueryEnumerator, che dispone di una lista di oggetti
QueryRow ciascuno dei quali descrive una riga attraverso
indice di una view
77. Corso Couchbase Lite di Beniamino Ferrari 77
All-docs e live query
● All-doc query non è associata a nessuna view, con
questo tipo di query si può immaginare di interrogare una
view che contiene una riga per ogni documento
contenuto nel database. Queste vengono utili per
conoscere dati comuni a tutti i documenti come ad es. i
documenti in conflitto
● Live query una volta create rimangono attive e
monitorano i cambiamenti agli indici delle view,
notificando agli observer quando i risultati della query
sono cambiati. Live query sono molto utili per gestire
componenti UI come tabelle.
78. Corso Couchbase Lite di Beniamino Ferrari 78
Creare e Configurare Query
● Gli oggetti query sono creati chiamando il metodo createQuery su
di una view e dal metodo createAllDocumentsQuery della classe
Database
● Ci sono diverse proprietà che si possono configurare per gestire le
query, ma le più importanti e comuni sono:
– startKey: la chiave iniziale, per default null che significa iniziare dalla prima
– endkey: l'ultima chiave da restituire, per default null che significa arrivare
fino alla fine
– desceding: se true le chiavi vengono restituite nell'ordine inverso
– limit: se diverso da zero indica il numero di righe da restituire
– skip: se diverso da zero indica il numero di righe che devono essere
saltate
79. Corso Couchbase Lite di Beniamino Ferrari 79
Proprietà avanzate delle query (1)
● Altre proprietà avanzate
– Keys: impostando questa proprietà (una lista di chiavi) verranno
restituite solo le righe associate alle chiavi contenute in essa
(startkey e endkey saranno ignorate)
– StartKeyDocID: se più chiavi corrispondano alla startKey è
possibile impostare la prima tramite ID
– EndKeyDocID: se più chiavi corrispondano alla endKey è possibile
impostare l'ultima tramite ID
– IndexUpdateMode: cambia il comportamento dell'indicizzazione,
per default l'indice verrà aggiornato se necessario prima di
eseguire la query. È possibile saltare l'indicizzazione con diverse
modalità
80. Corso Couchbase Lite di Beniamino Ferrari 80
Proprietà avanzate delle query (2)
● Ci sono altri proprietà avanzate che
personalizzano il grouping e il reducing
– MapOnly: se impostata a true non la reduce
function non verrà eseguita
– GroupLevel: se maggiore di zero, abilità il
raggruppamento di righe. Il valore indica il numero
di elementi che verranno raggruppati (equivale a
GROUP BY).
81. Corso Couchbase Lite di Beniamino Ferrari 81
Esempio di una query
● Imposto una query per una view che indiciazza
i post di un blog e ottenendo gli ultimi 20
Query query =
database.getView("postsByDate").createQuery(
);
query.setDescending(true);
query.setLimit(20);
82. Corso Couchbase Lite di Beniamino Ferrari 82
All-documents query
● All-doc query non sono associate a una view
● Supportano tutte le opzioni standard delle Query, come creare
range e invertire ordine
● Le query All-docs hanno delle proprietà speciali per personalizzare
il loro comportamento:
– allDocs: modalità di default che ritorna tutti i documenti non eliminati
– IncludeDeleted: include i documenti eliminati
– ShowConflicts: in questa modalità ogni oggetto QueryRow può essere
usato per sapere è in conflitto e quali sono gli ID delle revisioni in conflitto
– OnlyConflicts: si comporta come showConflicts ma vengono restituiti
solo i documenti in conflitto
83. Corso Couchbase Lite di Beniamino Ferrari 83
es. di all- docs query
// Trovo tutti i documenti i conflitto
Query query = database.createAllDocumentsQuery();
query.setAllDocsMode(Query.AllDocsMode.ONLY_CONFLICTS);
QueryEnumerator result = query.run();
for (Iterator<QueryRow> it = result; it.hasNext(); ) {
QueryRow row = it.next();
if (row.getConflictingRevisions().size() > 0) {
Log.w("MYAPP", "Conflict in document: %s", row.getDocumentId());
}
}
84. Corso Couchbase Lite di Beniamino Ferrari 84
Eseguire Query
● Una volta impostato un oggetto delle classe Query è
sufficiente chiamare il metodo run che restituirà i risultati
dell'interrogazione
● I risultati sono contenuti in un oggetto che fa da contenitore
QueryEnumerator che servirà per ottenere un collection di
oggetti QueryRow
● Ogni QueryRow ha due proprietà che contengono chiave e
valore, che corrispondono a quelle che vengono inserite
nell'indice tramite emit
● In una QueryRow contiene inoltre il documentID del
documento da dove sono stati presi chiave e valore
85. Corso Couchbase Lite di Beniamino Ferrari 85
Esempio di interrogazione
// Interrogo una view che mappa nomi prodotto in prezzi,
// che partono dalla chiave "m" fino a 100 prodotti:
Query query = database.getView("widgetsByName").createQuery();
query.setStartKey("m");
query.setLimit(100);
QueryEnumerator result = query.run();
for (Iterator<QueryRow> it = result; it.hasNext(); ) {
QueryRow row = it.next();
Log.w("MYAPP", "Widget named %s costs $%f", row.getKey(),
((Double)row.getValue()).doubleValue());
}
86. Corso Couchbase Lite di Beniamino Ferrari 86
Aggiornare query
● Se volessimo aggiornare ciclicamente il risultato della
query potremmo controllare la proprietà stale
● Se stale è false indica che la view non è cambiata ed
eseguendo nuovamente la query otterremo gli stessi
risultati
● Se stale è true significa che gli indici sono cambiati ma
le righe potrebbero essere le stesse. Per evitare di
aggiornare dati quando non c'è ne bisogno basterà
confrontare l'oggetto QueryEnumerator ottenuto con
l'attuale usando il metodo equals
87. Corso Couchbase Lite di Beniamino Ferrari 87
Esempio di verifica che i dati siano
cambiati
// Verifica che dati della query siano cambiati:
if (queryResult == null || queryResult.isStale()) {
QueryEnumerator newResult = query.run();
if (!queryResult.equals(newResult)) {
queryResult = newResult;
updateMyUserInterface();
}
}
88. Corso Couchbase Lite di Beniamino Ferrari 88
LiveQuery
● Una live query rimane attiva e monitora il
database e indici delle view in attesa di
cambiamenti.
● Quando c'è un cambiamento si esegue
nuovamente in autonomia e notifica gli observer
● LiveQuery è un metodo intelligente per creare
interfacce utente reattive, specialmente per
tabelle o liste che si aggiornino ad ogni
cambiamento del database
89. Corso Couchbase Lite di Beniamino Ferrari 89
Esempio di live query
private void initializeQuery() {
// Set up my live query during view initialization:
Query query = database.getView("widgets").createQuery();
query.setLimit(100);
LiveQuery liveQuery = query.toLiveQuery();
this.liveQuery = liveQuery;
liveQuery.addChangeListener(new LiveQuery.ChangeListener() {
@Override
public void changed(LiveQuery.ChangeEvent event) {
if (event.getSource().equals(this.liveQuery)) {
this.displayRows(event.getRows());
}
}
});
this.liveQuery.start();
}
90. Corso Couchbase Lite di Beniamino Ferrari 90
Reducing
● Se la view ha una reduce function, viene
chiama per default quando viene chiamata la
una query della view.
● Questo significa che tutte le righe in output
saranno aggregate in una singola riga senza
chiave, il quale valore corrisponde all'output
della reduce function
● Secondo il l'ordinamento di default l'output della
reduce function è contenuto nella prima riga
91. Corso Couchbase Lite di Beniamino Ferrari 91
Esempio di reducing
// In questa view chiavi sono le date degli ordini, e i valori sono i prezzi.
// La reduce function è la media dei valori.
Query query = database.getView("ordersByDateView").createQuery();
query.setStartKey("2014-01-01");
query.setEndKey("2014-02-01");
// Eseguo la query senza reduce function e mostro gli ordini del Gennaio 2014:
query.setMapOnly(true);
QueryEnumerator result = query.run();
for (Iterator<QueryRow> it = result; it.hasNext(); ) {
QueryRow row = it.next();
Log.w("MYAPP", "On %s: order for $%f", row.getKey(), ((Double)row.getValue()).doubleValue());
}
// Eseguo la query senza reduce function per ottenere la media dei prezzi di Gennaio 2014:
query.setMapOnly(false);
QueryEnumerator result = query.run();
QueryRow aggregate = result.next();
Log.w("MYAPP", "Average order was $%f", ((Double)aggregate.getValue()).doubleValue());
92. Corso Couchbase Lite di Beniamino Ferrari 92
Raggruppamento per chiave
● La proprietà groupLevel permette di collassare righe avente
stessa chiave o stessa lista di chiavi prefisso
● La reduce function può essere usata per ottenere statistiche
sulle righe raggruppate
● Quando il groupLevel va da 2 a N, la query raggruppa i valore
dei primi N elementi degli array chiave creando una singola
riga con chiave l'array composto dal prefisso di n elementi
● Quando groupLevel è 1 supporta le chiavi che non sono
array
● I valori di ogni riga sarà il risultato della reduce function
93. Corso Couchbase Lite di Beniamino Ferrari 93
Esempio di Grouping (1)
● Dato un database libreria musicale, contenente
per ogni traccia una riga
● Creiamo un view con chiave l'array [genre,
artist, album, trackname] e valore durata della
traccia in secondi. Questa view ha reduce
function la somma degli ingressi
● Vogliamo creare una lista degli album di genere
“MopeRock” dei “Radiohead” e la lista delle
durate.
94. Corso Couchbase Lite di Beniamino Ferrari 94
Esempio di Grouping (2)
Query query = database.getView("hierarchyView").createQuery();
query.setGroupLevel(3);
query.setStartKey(Arrays.asList("Mope-Rock", "Radiohead"));
query.setEndKey(Arrays.asList("Mope-Rock", "Radiohead", new HashMap<String, Object>()));
// groupLevel=3 restituirà l'array con contenente le chiavi [genre, artist, album] .
List<String> albumTitles = new ArrayList<String>();
List<String> albumTimes = new ArrayList<String>();
QueryEnumerator result = query.run();
for (Iterator<QueryRow> it = result; it.hasNext(); ) {
QueryRow row = it.next();
List<String> key = (List) row.getKey();
albumTitles.add(key.get(2)); // titolo è il 3° elemento della chiave
albumTimes.add((String)row.getValue()); // il valore è la durata dell'album
}
95. Corso Couchbase Lite di Beniamino Ferrari 95
Replica
● La replica o sincronizzazione viene eseguita tramite un oggetto
di classe Replication, che trasferisce cambiamenti da un
database locale a uno remoto
● Per eseguire una replica, è necessario ottenere un oggetto
Replication da un oggetto Database, configurarne alcuni
parametri e avviarne l'esecuzione
● La replica viene eseguita in background da un thread asincrono
● È possibile monitorare il progresso controllando le notifiche
generate dall'oggetto Replication ad ogni suo cambiamento di
stato, oppure, utilizzando e notifiche generate dal database
quando i documenti vengono modificato dal replicator
96. Corso Couchbase Lite di Beniamino Ferrari 96
Pull e Push
● Una tipica applicazione crea due di repliche pull e
push, entrambe puntano all'URL del server
● Queste rimangono attive per tutto il cliclo di vita
dell'applicazione, caricando e scaricando documenti
ad ogni cambiamento e quando la rete è disponibile
● Il codice dell'applicazione non dovrebbe fare
attenzione al replicator, ma conoscere solamente che
i documenti modificati verranno inviati al server e che
i cambiamenti sul server verranno scaricati sul server
locale
97. Corso Couchbase Lite di Beniamino Ferrari 97
Tipi di Replica
● Pull – Push: una replica push carica cambiamenti da un
database locale a uno remoto; una replica pull scarica modifiche
da database remoto a uno locale
● One-shot – Continua: da default, una replica continua fino a
quando tutti cambiamenti sono stati trasferiti da sorgente a
destinazione. Un replica continua, rimane attiva
indefinitivamente, controllando e scaricando le nuove modifiche.
● Filtered: la replica può avere dei filtri che restringono quali
documenti inclusi nel trasferimento. Questo può essere utile per
limitare il numero di documenti che vengono scaricati sul device
o per mantenerne una parte privata
98. Corso Couchbase Lite di Beniamino Ferrari 98
Creare e configurare le repliche
● È possibile creare un oggetto Replication
utilizzando il metodo createPullReplication o il
metodo createPushReplication di un oggetto
Database. Entrambi accettano come parametro
l'url del database remoto su cui eseguire la
sincronizzazione
● Una volta creato un oggetto Replication è
possibile customizzare la property continuos a
true o ad es il tipo di autnticazione
99. Corso Couchbase Lite di Beniamino Ferrari 99
Esempio di creazione di Replica
URL url = new URL("https://example.com/mydatabase/");
Replication push = database.createPushReplication(url);
Replication pull = database.createPullReplication(url);
pull.setContinuous(true);
push.setContinuous(true);
Authenticator auth = new BasicAuthenticator(username,
password);
push.setAuthenticator(auth);
pull.setAuthenticator(auth);
100. Corso Couchbase Lite di Beniamino Ferrari 100
Iniziare una replica
● Inoltre, è possibile monitorare il progresso della
sincronizzazione per mostrare l'avanzamento
all'utente o mostrare eventuali errori
● Una volta che tutto è impostato andrà chiamato
il metodo start per avviare la replica, che
terminerà quando tutte le modifiche saranno
trasferiti, mentre se continua continuerà
perpetuamente.
101. Corso Couchbase Lite di Beniamino Ferrari 101
Esempio di avvio di una replica
push.addChangeListener(new Replication.ChangeListener() {
@Override
public void changed(Replication.ChangeEvent event) {
// verrà chiamato quando cambia lo stato della replica in push
}
});
pull.addChangeListener(new Replication.ChangeListener() {
@Override
public void changed(Replication.ChangeEvent event) {
// verrà chiamato quando cambia lo stato della replica in pull
}
});
push.start();
pull.start();
this.push = push;
this.pull = pull;
102. Corso Couchbase Lite di Beniamino Ferrari 102
Filtrare repliche in push
● Durante la replica in push il documento candidato
risiede localmente, perciò la filter function verrà
eseguita localmente.
● Il programmatore dovrà definirla in linguaggio nativo,
assegnarle un nome e registrarla all'interno
dell'oggetto Database
● Il replicator passerà al nostro filtro un oggetto
SavedRevision, e attraverso di esso sarà possibile
accettare o rifiutare il documento, restituendo true o
false rispettivamente.
103. Corso Couchbase Lite di Beniamino Ferrari 103
Esempio di Filtro in push
// Definisco un filtro che lascia passare solo i documenti con determinato "owner" .
// Il parametro da controllare è quello con chiave “name”
db.setFilter("byOwner", new ReplicationFilter() {
@Override
public boolean filter(SavedRevision revision, Map<String, Object> params) {
String nameParam = (String) params.get("name");
return nameParam != null && nameParam.equals(revision.getProperty("owner"));
}
});
// Imposto una replica filtrata in push che usa la precedente filter fuction,
// così invieremo in push solo i documenti che hanno "owner" uguale a "Pippo":
Replication push = db.createPushReplication(url);
push.setFilter("byOwner");
Map<String, Object> params = new HashMap<String, Object>();
params.put("name", "Pippo");
push.setFilterParams(params);
104. Corso Couchbase Lite di Beniamino Ferrari 104
Filtrare replica in pull da un Sync
Gateway
● Sistema che viene utilizzato per filtrare documenti in pull è
quello dei Canali
● Ogni documento che viene memorizzato nel Sync Gateway
viene taggato con un insieme di canali, attraverso una
funzione app-defined risiedente sul gateway (sync function)
● Ogni replica in pull da un Gateway è implicitamente
filtrata da un set di canali che corrispondono ai documenti
a cui un utente ha il permesso permesso accedere
● È possibile creare un filtro in pull aggiuntivo creando un array
di canali che ci permetterà di ottenere tutti i documenti
etichettati con i canali contenuti in esso.
105. Corso Couchbase Lite di Beniamino Ferrari 105
Esempio di filtro in pull usando
un Sync Gateway
// Imposto una replica in pull filtrata in modo da
// ottenere solo i documenti etichettati come
// “sales” dal Sync Gateway:
db.createPullReplication(url);
List<String> channels = new
ArrayList<String>();
channels.add("sales");
pull.setChannels(channels);