Armazenamento Persistente com Realm

Prof. Fellipe Aleixo ([email protected]) Banco de Dados Realm

• Alternativa open source ao uso do SQLite – Boa performance e facilidade de usar

• Instalação via npm

• Pré-requisitos: – Pode endereçar as plataformas Android e iOS – 0.31.0 e posterior é suportado Instalação

• Criar um novo projeto React Native: – react-native init

• No diretório do projeto: – npm install --save realm

• Vincular o projeto ao módulo Realm – react-native link realm Introdução

• Realm Javascript funciona tanto com React Native quanto com Node.js • Exemplo: const Realm = require('realm'); // Define your models and their properties const CarSchema = { name: 'Car', properties: { make: 'string', model: 'string', miles: {type: 'int', default: 0}, } }; const PersonSchema = { name: 'Person', properties: { name: 'string', birthday: 'date', cars: 'Car[]', picture: 'data?' // optional property } };

Realm.open({schema: [CarSchema, PersonSchema]}) .then(realm => { // Create Realm objects and write to local storage realm.write(() => { const myCar = realm.create('Car', { make: 'Honda', model: 'Civic', miles: 1000, }); myCar.miles += 20; // Update a property value }); // Query Realm for all cars with a high mileage const cars= realm.objects('Car').filtered('miles > 1000');

// Will return a Results object with our 1 car cars.length // => 1

// Add another car realm.write(() => { const myCar = realm.create('Car', { make: 'Ford', model: 'Focus', miles: 2000, }); });

// Query results are updated in realtime cars.length // => 2 }) .catch(error => { console.log(error); }); Introdução

• Exemplos podem ser encontrados no GitHub – Repositório realm-js https://github.com/realm/realm-js/tree/master/examples

• Realm Studio – Plataforma para o gerenciamento de bancos de dados Realm – https://realm.io/products/realm-studio/ Configurando uma Aplicação

• Método estático open da classe Realm – Passando um objeto de configuração // Get the default Realm with support for our objects Realm.open({schema: [Car, Person]}) .then(realm => { // ...use the realm instance here }) .catch(error => { // Handle the error here if something went wrong }); Configurando uma Aplicação

• Elementos do objeto de configuração – schema – tabelas do banco – path – caminho para outro Realm – migration – função de migração – sync – objeto sync para acesso sincronizado – inMemory – objetos carregados na memória – deleteRealmIfMigrationNeeded – apaga o banco, caso seja necessária a realização de uma migração • https://realm.io/docs/javascript/latest/api/Realm.html#~Configuration Versão do Banco

• Propriedade schemaVersion const PersonSchema = { name: 'Person', properties: { name: 'string' } };

// schemaVersion defaults to 0 Realm.open({schema: [PersonSchema]}); – Definida em caso de atualização do banco Versão do Banco const UpdatedPersonSchema = { // The schema name is the same, so previous `Person` object // in the Realm will be updated name: 'Person', properties: { name: 'string', dog: 'Dog' // new property } };

// this will throw because the schema has changed // and `schemaVersion` is not specified Realm.open({schema: [UpdatedPersonSchema]});

// this will succeed and update the Realm to the new schema Realm.open({schema: [UpdatedPersonSchema], schemaVersion: 1}); Modelos

• Os modelos (de dados) são definidos pela propriedade schema na inicialização do Realm – Consiste no nome do modelo (name) e – Uma lista de propriedades (properties) Modelos const Realm = require('realm'); const CarSchema = { name: 'Car', properties: { make: 'string', model: 'string', miles: {type: 'int', default: 0}, } }; Modelos const PersonSchema = { name: 'Person', properties: { name: 'string', birthday: 'date', cars: 'Car[]' picture: 'data?', // optional property } };

// Initialize a Realm with Car and Person models Realm.open({schema: [CarSchema, PersonSchema]}) .then(realm => { // ... use the realm instance to read and modify data }) Classes de Modelo

• Podem ser utilizadas classes – Necessário definir o esquema no construtor class Person { get fullName() { return this.firstName + ' ' + this.lastName; } } Person.schema = { name: 'Person', properties: { firstName: 'string', lastName: 'string' } }; Classes de Modelo

• Dessa forma, a classe pode ser passada para a propriedade schema na configuração Realm.open({schema: [Person]}) .then( /* ... */ ); – As propriedades são acessadas normalmente realm.write(() => { const john = realm.create('Person', { firstName: 'John', lastName: 'Smith' }); john.lastName = 'Peterson'; console.log(john.fullName); // -> 'John Peterson' }); Tipos Suportados

• bool: valores booleanos JavaScript • int, float e double: valores numéricos JavaScript. ‘int’ e ‘double’ são armazenados em 64 bits, enquanto ‘float’ em 32 bits • string: valores de texto • data: mapeados em ArrayBuffer • date: mapeados em Date Tipos Suportados const CarSchema = { name: 'Car', properties: { // The following property types are equivalent make: {type: 'string'}, model: 'string', } } Propriedades Opcionais

• Por padrão os tipos básicos são obrigatórios const PersonSchema = { name: 'Person', properties: { realName: 'string', // required property displayName: 'string?', // optional property birthday: {type: 'date', optional: true}, // optional } }; let realm = new Realm({schema: [PersonSchema, CarSchema]}); Propriedades Opcionais realm.write(() => { // optional properties can be set to null or undefined let charlie = realm.create('Person', { realName: 'Charlie', displayName: null, // could also be omitted entirely birthday: new Date(1995, 11, 25), });

// optional properties can be set to `null`, `undefined`, // or to a new non-null value charlie.birthday = undefined; charlie.displayName = 'Charles';

// Setting a non-optional to null will throw `TypeError` // charlie.realName = null; }); Propriedades de Lista const PersonSchema = { name: 'Person', properties: { name: 'string', testScores: 'double?[]' } }; let realm = new Realm({schema: [PersonSchema, CarSchema]}); realm.write(() => { let charlie = realm.create('Person', { name: 'Charlie', testScores: [100.0] }); // Charlie had an absense for the second test charlie.testScores.push(null); // And then he didn't do so well on the third test charlie.testScores.push(70.0); }); Chave Primária

• Definida através da propriedade primaryKey – Referenciando propriedades do tipo string ou int const BookSchema = { name: 'Book', primaryKey: 'id', properties: { id: 'int', // primary key title: 'string', price: 'float' } }; RELACIONAMENTOS Relacionamentos To-One

• Podem ser referenciados outros esquemas como tipo de uma propriedade const PersonSchema = { name: 'Person', properties: { // The following property definitions are equivalent car: {type: 'Car'}, van: 'Car', } };

// CarSchema is needed since contains properties of type 'Car' Realm.open({schema: [CarSchema, PersonSchema]}) .then(/* ... */); Relacionamentos To-One

• Ao acessar propriedades que referenciam outros esquemas a sintaxe é normal realm.write(() => { const nameString = person.car.name; person.car.miles = 1100;

// create a new Car by setting the property to an object // with all of the required fields person.van = {make: 'Ford', model: 'Transit'};

// set both properties to the same car instance person.car = person.van; }); Relacionamentos To-Many

• Semelhante às propriedades básicas, pode ser referenciada uma lista de objetos const PersonSchema = { name: 'Person', properties: { // The following property definitions are equivalent cars: {type: 'list', objectType: 'Car'}, vans: 'Car[]' } } let carList = person.cars; Relacionamentos To-Many

• Semelhante às propriedades básicas, pode ser referenciada uma lista de objetos // Add new cars to the list realm.write(() => { carList.push({make: 'Honda', model: 'Accord', miles: 100}); carList.push({make: 'Toyota', model: 'Prius', miles: 200}); }); let secondCar = carList[1].model; // access using an array index Relacionamentos Inversos

• Os links são unidirecionais

• Ex. são propriedades independentes: – propriedade To-Many – Person.dogs – propriedade To-One – Dog.owner à Person

• Definição de uma propriedade do tipo linkingObjects Relacionamentos Inversos const PersonSchema = { name: 'Person', properties: { dogs: 'Dog[]' } } const DogSchema = { name:'Dog', properties: { // No shorthand syntax for linkingObjects properties owners: {type: 'linkingObjects’, objectType: 'Person’, property: 'dogs'} } } Relacionamentos Inversos const ShipSchema = { name: 'Ship', properties: { captain: 'Captain' } } const CaptainSchema = { name: 'Captain', properties: { ships: {type: 'linkingObjects’, objectType: 'Ship’, property: 'captain'} } } MANIPULAÇÃO E CONSULTAS Manipulação de Objetos

• Criação, atualização e remoção de objetos no Realm acontecem em um bloco de transação write() • Criando objetos: try { realm.write(() => { realm.create('Car', {make: 'Honda’, model: 'Accord’, drive: 'awd'}); }); } catch (e) { console.log("Error on creation"); } Manipulação de Objetos

• Atualizando objetos: realm.write(() => { car.miles = 1100; }); – Referenciando um objeto através da chave realm.write(() => { // Create a book object realm.create('Book', {id: 1, title: 'Recipes', price: 35});

// Update book with new price keyed off the id realm.create('Book', {id: 1, price: 55}, true); }); Manipulação de Objetos

• Removendo objetos: realm.write(() => { // Create a book object let book = realm.create('Book', {id: 1, title: 'Recipes’, price: 35});

// Delete the book realm.delete(book);

// Delete multiple books by passing in a `Results`, `List`, // or JavaScript `Array` let allBooks = realm.objects('Book'); realm.delete(allBooks); // Deletes all books }); Consultas

• Recuperação de objetos: let dogs = realm.objects('Dog'); // retrieves all Dogs • Aplicação de filtros: let dogs = realm.objects('Dog'); let tanDogs = dogs.filtered( 'color = "tan" AND name BEGINSWITH "B"'); Exemplo de Consulta const Realm = require('realm'); const CarSchema = { name: 'Car', properties: { make: 'string', model: 'string', miles: {type: 'int', default: 0}, } }; const PersonSchema = { name: 'Person', properties: { name: 'string', cars: {type: 'list', objectType: 'Car'}, } }; Exemplo de Consulta

// Initialize a Realm with Car and Person models Realm.open({schema: [CarSchema, PersonSchema]}) .then(realm => {

// Add persons and their cars realm.write(() => { let john = realm.create('Person', {name: 'John', cars: []}); john.cars.push({make: 'Honda', model: 'Accord', miles: 1500}); john.cars.push({make: 'Toyota', model: 'Prius', miles: 2780});

let joan = realm.create('Person', {name: 'Joan', cars: []}); joan.cars.push({make: 'Skoda', model: 'Octavia', miles: 1120}); joan.cars.push({make: 'Ford', model: 'Fiesta', miles: 95}); joan.cars.push({make: 'VW', model: 'Golf', miles: 1270});

let jill = realm.create('Person', {name: 'Jill', cars: []});

let jack = realm.create('Person', {name: 'Jack', cars: []}); jack.cars.push({make: 'Porche', model: '911', miles: 965}); }); Exemplo de Consulta

// Find car owners let carOwners = realm.objects('Person').filtered('cars.@size > 0'); console.log('Car owners') for (let p of carOwners) { console.log(` ${p.name}`); }

// Find who has been driver longer than average let average = realm.objects('Car').avg('miles'); let longerThanAverage = realm.objects('Person’). filtered('[email protected] > $0',average); console.log(`Longer than average (${average})`) for (let p of longerThanAverage) { console.log(` ${p.name}: ${p.cars.sum('miles')}`); }

realm.close(); }); Exemplo de Consulta

• Saída do exemplo: Car owners John Joan Jack Longer than average (1288.3333333333333) John: 4280 Joan: 2485 Referência

1. Banco de dados Realm https://realm.io/products/realm-database