Realm JavaScript
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/11166081.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
入门
安装
按照下面的安装说明通过npm安装Realm JavaScript ,或者在GitHub上查看源代码。
先决条件
- 确保您的环境已设置为运行React Native应用程序。按照React Native说明开始使用。
- 使用Realm的应用可以同时针对iOS和Android。
- 支持React Native 0.31.0及更高版本。
安装
-
创建一个新的React Native项目:
react-native init <project-name>
-
将目录更改为新项目(
cd <project-name>
)并添加realm
依赖项:npm install --save realm
-
接下来,将项目链接到本
realm
机模块。react-native link realm
Android警告:根据版本的不同,react-native link
可能会生成无效配置,正确更新Gradle(android/settings.gradle
和android/app/build.gradle
)但无法添加Realm模块。确认react-native link
已添加Realm模块; 如果没有,请使用以下步骤手动链接到库:
-
将以下行添加到
android/settings.gradle
:include ':realm' project(':realm').projectDir = new File(rootProject.projectDir, '../node_modules/realm/android')
-
将Realm添加到依赖项中
android/app/build.gradle
:// When using Android Gradle plugin 3.0 or higher dependencies { implementation project(':realm') } // When using Android Gradle plugin lower than 3.0 dependencies { compile project(':realm') }
-
添加导入并链接包
MainApplication.java
:import io.realm.react.RealmReactPackage; // add this import public class MainApplication extends Application implements ReactApplication { @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new RealmReactPackage() // add this line ); } }
你现在准备好了。要查看Realm的运行情况,请将class <project-name>
以下内容替换为以下内容App.js
:
const Realm = require('realm');
class <project-name> extends Component {
constructor(props) {
super(props);
this.state = { realm: null };
}
componentWillMount() {
Realm.open({
schema: [{name: 'Dog', properties: {name: 'string'}}]
}).then(realm => {
realm.write(() => {
realm.create('Dog', {name: 'Rex'});
});
this.setState({ realm });
});
}
render() {
const info = this.state.realm
? 'Number of dogs in this Realm: ' + this.state.realm.objects('Dog').length
: 'Loading...';
return (
<View style={styles.container}>
<Text style={styles.welcome}>
{info}
</Text>
</View>
);
}
}
然后,您可以在设备和模拟器中运行您的应用程序。
请注意,世博会不支持Realm,create-react-native-app
不起作用。
介绍
Realm JavaScript使您能够以安全,持久和快速的方式有效地编写应用程序的模型层。它旨在与React Native和Node.js一起使用。
这是一个简单的例子:
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);
});
请注意,如果要将Realm用于服务器端/节点,则可以在Realm Object Server文档中找到其他信息。
Realm Studio
Realm Studio是我们的首选开发人员工具,可以轻松管理Realm数据库和Realm平台。使用Realm Studio,您可以打开和编辑本地和同步的域,并管理任何Realm Object Server实例。它支持Mac,Windows和Linux。
三界
开放的领域
只需open
在Realm
类上调用静态方法即可打开Realm 。传递配置对象。我们已经看到这已经在示例中使用了包含schema
密钥的配置对象:
// 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
});
有关配置对象的完整详细信息,请参阅API参考以获取配置。除此之外schema
,该对象的一些更常见的键包括:
path
:指定另一个Realm的路径migration
:迁移功能sync
:一个同步对象,用于打开与Realm Object Server同步的RealminMemory
:Realm将在内存中打开,并且对象不会被持久化; 一旦最后一个Realm实例关闭,所有对象都会消失deleteRealmIfMigrationNeeded
:如果需要迁移,删除领域; 这在开发中很有用,因为数据模型可能经常更改
默认领域
您可能已经注意到在前面的所有示例中都省略了path参数。在这种情况下,使用默认的Realm路径。您可以使用Realm.defaultPath
全局属性访问和更改默认的Realm路径。
打开同步领域
您是否希望使用Realm Mobile Platform同步所有Realm数据库?所有与同步相关的文档已移至我们的平台文档中
其他领域
在不同位置持有多个领域有时很有用。例如,除了主Realm之外,您可能希望将一些数据与应用程序捆绑在Realm文件中。您可以通过path
在初始化领域时指定参数来完成此操作。所有路径都相对于应用程序的可写文档目录:
// Open a realm at another path
Realm.open({
path: 'anotherRealm.realm',
schema: [CarSchema]
}).then(/* ... */);
架构版本
打开Realm时可用的另一个选项是schemaVersion
属性。省略时,schemaVersion
属性默认为0
。您需要指定schemaVersion
何时使用包含与先前规范不同的对象的模式初始化现有Realm。如果架构已更新且架构未更新,schemaVersion
则会引发异常。
const PersonSchema = {
name: 'Person',
properties: {
name: 'string'
}
};
// schemaVersion defaults to 0
Realm.open({schema: [PersonSchema]});
如果你稍后再做这样的事情:
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});
如果要检索Realm的当前架构版本,可以使用该Realm.schemaVersion
方法执行此操作。
const currentVersion = Realm.schemaVersion(Realm.defaultPath);
同步开放的领域
您可以通过简单地调用构造函数并将配置对象传递给它来创建领域实例。通常不推荐这样做,因为它会阻塞并且可能是一个耗时的操作,特别是如果要运行迁移或者域同步并且您不希望在数据完全下载之前冒险修改数据。
如果您仍想这样做,模式很简单:
const realm = new Realm({schema: [PersonSchema]});
// You can now access the realm instance.
realm.write(/* ... */);
楷模
领域数据模型由初始化期间传递到Realm的架构信息定义。对象的模式由对象name
和一组属性组成。每个属性具有名称和由或包含属性的类型的字符串,或与对象描述name
,type
,objectType
,optional
,default
,和indexed
字段。
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',
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
})
类
此时,通过类定义模型的支持是有限的。它在React Native中工作,但在Node中不工作。
如果要使用ES2015类(并且可能希望继承现有功能),则只需在构造函数上定义模式:
class Person {
get fullName() {
return this.firstName + ' ' + this.lastName;
}
}
Person.schema = {
name: 'Person',
properties: {
firstName: 'string',
lastName: 'string'
}
};
您现在可以将类本身传递给schema
打开配置的属性:
Realm.open({schema: [Person]})
.then( /* ... */ );
您可以一直访问属性:
realm.write(() => {
const john = realm.create('Person', {
firstName: 'John',
lastName: 'Smith'
});
john.lastName = 'Peterson';
console.log(john.fullName); // -> 'John Peterson'
});
支持的类型
域支持下列基本类型:bool
,int
,float
,double
,string
,data
,和date
。
bool
属性映射到JavaScriptboolean
值int
,,float
和double
属性映射到JavaScriptnumber
值。内部'int'和'double'存储为64位,而float
存储为32位。string
属性映射到string
data
属性映射到ArrayBuffer
date
属性映射到Date
将基本属性指定为速记时,您可以仅指定类型,而不必指定具有单个条目的字典:
const CarSchema = {
name: 'Car',
properties: {
// The following property types are equivalent
make: {type: 'string'},
model: 'string',
}
}
可选属性
默认情况下,基本类型是非可选的,不支持存储null
或undefined
。通过optional
在属性定义中指定指示符或使用简写语法,通过?
在类型名称后附加a ,可以使属性成为可选:
const PersonSchema = {
name: 'Person',
properties: {
realName: 'string', // required property
displayName: 'string?', // optional property
birthday: {type: 'date', optional: true}, // optional property
}
};
let realm = new Realm({schema: [PersonSchema, CarSchema]});
realm.write(() => {
// optional properties can be set to null or undefined at creation
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 property to null will throw `TypeError`
// charlie.realName = null;
});
列表属性
除了存储单个值之外,还可以将属性声明为任何支持的基本类型的列表。这是通过附加[]
类型名称来完成的:
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 excused absense for the second test and was allowed to skip it
charlie.testScores.push(null);
// And then he didn't do so well on the third test
charlie.testScores.push(70.0);
});
访问列表属性时,将List
返回一个对象。List
具有与常规JavaScript数组非常相似的方法。最大的区别在于对a所做的任何更改都会List
自动保存到底层Realm,因此它们只能在写入事务中进行修改。此外,List
s属于从中获取的基础对象 - 您只能List
通过从拥有对象访问属性来获取实例,并且无法手动创建它们。
虽然list属性中的值可以是可选的,但list属性本身不能。optional
使用longhand语法(values: {type: 'int[]', optional: true}
)指定list属性将使列表中的值可选。
关系
一对一的关系
对于一对一关系,您可以将name
要引用的对象模式的属性指定为属性的类型:
const PersonSchema = {
name: 'Person',
properties: {
// The following property definitions are equivalent
car: {type: 'Car'},
van: 'Car',
}
};
使用对象属性时,需要确保所有引用的类型都存在于用于打开Realm的模式中:
// CarSchema is needed since PersonSchema contains properties of type 'Car'
Realm.open({schema: [CarSchema, PersonSchema]})
.then(/* ... */);
访问对象属性时,可以使用常规属性语法访问嵌套属性:
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;
});
Realm中的对象属性始终是可选的,不必明确指定,也不能使其成为必需属性。
多对多关系
与基本属性一样,您也可以使用对象列表来形成多对多关系。这可以通过附加[]
到目标对象模式的名称,或者通过将属性类型设置为list
并指定objectType
:
const PersonSchema = {
name: 'Person',
properties: {
// The following property definitions are equivalent
cars: {type: 'list', objectType: 'Car'},
vans: 'Car[]'
}
}
let carList = person.cars;
// 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
与其他列表和一对一关系不同,多对多关系不能是可选的。
反向关系
链接是单向的。因此,如果to-many属性Person.dogs
链接到Dog
实例并且Dog.owner
链接到一个属性Person
,则这些链接彼此独立。将a 的属性附加Dog
到Person
实例的dogs
属性不会自动将dog的owner
属性设置为this Person
。因为手动同步关系对是容易出错,复杂和重复的信息,所以Realm提供链接对象属性来表示这些反向关系。
通过链接对象属性,您可以从特定属性获取链接到给定对象的所有对象。例如,一个Dog
对象可以拥有一个名为的属性owners
,该属性包含在其属性Person
中具有此确切Dog
对象的所有对象dogs
。这是通过创建owners
类型的属性linkingObjects
然后指定它与Person
对象的关系来完成的。
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'}
}
}
甲linkingObjects
属性可以指向到一个List
属性(一对多的关系)或Object
属性(对一的关系):
const ShipSchema = {
name: 'Ship',
properties: {
captain: 'Captain'
}
}
const CaptainSchema = {
name: 'Captain',
properties: {
ships: {type: 'linkingObjects', objectType: 'Ship', property: 'captain'}
}
}
访问linkingObjects
属性时,将Results
返回一个对象,因此完全支持进一步的查询和排序。linkingObject
属性属于从中获取的对象,无法直接设置或操作。它们在提交事务时自动更新。
访问linkingObjects
没有模式:如果你已经打开了一个域文件没有指定一个模式,例如在一个领域的功能回调,你可以得到一个linkingObjects
通过调用属性linkingObjects(objectType, property)
上的Object
实例:
let captain = realm.objectForPrimaryKey('Captain', 1);
let ships = captain.linkingObjects('Ship', 'captain');
链接对象属性不能是可选的。
默认属性值
可以通过default
在属性定义中设置指示符来指定默认属性值。要使用默认值,请在对象创建期间保留未指定的属性。
const CarSchema = {
name: 'Car',
properties: {
make: {type: 'string'},
model: {type: 'string'},
drive: {type: 'string', default: 'fwd'},
miles: {type: 'int', default: 0}
}
};
realm.write(() => {
// Since `miles` is left out it defaults to `0`, and since
// `drive` is specified, it overrides the default value
realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
});
索引属性
您可以indexed
向属性定义添加指示符以使该属性编入索引。这是支持的int
,string
,bool
,和date
物业类型:
var BookSchema = {
name: 'Book',
properties: {
name: { type: 'string', indexed: true },
price: 'float'
}
};
索引属性将极大地加速查询,其中以较慢的插入为代价比较属性的相等性。
主键
您可以primaryKey
在对象模型string
和int
属性中指定属性。声明主键可以有效地查找和更新对象,并为每个值强制实现唯一性。将具有主键的对象添加到Realm后,无法更改主键。
const BookSchema = {
name: 'Book',
primaryKey: 'id',
properties: {
id: 'int', // primary key
title: 'string',
price: 'float'
}
};
主键属性会自动编入索引。
写
对领域中对象的更改 - 创建,更新和删除 - 必须在write()
事务块中进行。请注意,写入事务具有不可忽略的开销; 您应该尽量减少write
代码中的块数。
创建对象
使用以下create
方法创建对象:
try {
realm.write(() => {
realm.create('Car', {make: 'Honda', model: 'Accord', drive: 'awd'});
});
} catch (e) {
console.log("Error on creation");
}
请注意,抛出的任何异常都write()
将取消该事务。该try/catch
块不会在所有示例中显示,但这是一种很好的做法。
嵌套对象
如果对象具有对象属性,则可以通过为每个子属性指定JSON值来递归地创建这些属性的值:
realm.write(() => {
realm.create('Person', {
name: 'Joe',
// nested objects are created recursively
car: {make: 'Honda', model: 'Accord', drive: 'awd'},
});
});
更新对象
键入的更新
您可以通过在写入事务中设置其属性来更新任何对象。
realm.write(() => {
car.miles = 1100;
});
使用主键创建和更新对象
如果模型类包含主键,则可以让Realm根据其主键值智能地更新或添加对象。这是通过将true
第三个参数传递给create
方法来完成的:
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);
});
在上面的示例中,由于对象已经存在id
,1
并且我们已经true
为第三个参数传入了值,因此更新price属性而不是尝试创建新对象。由于title
省略了该属性,因此该对象保留此属性的原始值。请注意,在创建或更新具有主键属性的对象时,必须指定主键。
删除对象
可以通过delete
在写入事务中调用方法来删除对象。
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
});
查询
查询允许您从Realm获取单个类型的对象,并可选择过滤和排序这些结果。所有查询(包括查询和属性访问)在Realm中都是惰性的。只有在访问对象和属性时才会读取数据。这允许您以高效的方式表示大量数据。
执行查询时,会返回一个Results
对象。结果只是您的数据视图,并且不可变。
从Realm中检索对象的最基本方法是使用a上的objects
方法Realm
获取给定类型的所有对象:
let dogs = realm.objects('Dog'); // retrieves all Dogs from the Realm
过滤
您可以Results
通过filtered
在a List
或a Results
上使用查询字符串调用方法来进行过滤。
例如,以下内容将更改我们之前的示例,以检索所有颜色为棕褐色且名称以“B”开头的狗:
let dogs = realm.objects('Dog');
let tanDogs = dogs.filtered('color = "tan" AND name BEGINSWITH "B"');
Realm支持的查询语言受到Apple的NSPredicate的启发。让我们简要总结一下支持的功能和语法:
- 比较操作数可以是属性名称或常量。至少有一个操作数必须是属性名称。特殊常量
false
,true
和null
。 - 时间戳可以以格式指定,
YYYY-MM-DD@HH:MM:SS:NANOSECONDS
并且YYYY-MM-DDTHH:MM:SS:NANOSECONDS
可以省略纳秒。 - 比较运算符
=
/==
,<=
,<
,>=
,>
,和!=
/<>
都支持int
,float
,double
,和Date
物业类型,例如age = 45
。 - boolean()属性支持比较运算符
=
/==
和!=
/ 。<>
bool
- 对于串和数据(
ArrayBuffer
)的属性,所述=
(和==
),!=
(和<>
), ,BEGINSWITH
,CONTAINS
和ENDSWITH
运营商都支持,例如name CONTAINS 'Ja'
。 - 对于带有
LIKE
运算符的字符串,可以进行通配符比较,例如name LIKE '*an?'
匹配“Jane”,“Dan”,“Shane”等。 - 使用
[c]
例如字符串的字符串不区分大小写的比较CONTAINS[c] 'Ja'
。请注意,只有字符“AZ”和“az”才会被忽略。 - Realm支持以下复合运算符:
AND
/&&
,OR
/||
和NOT
/!
,例如name BEGINSWITH 'J' AND age >= 32
。 - 该集合表达式
@count
/@size
,@min
,@max
,@sum
并且@avg
都支持列表属性,如employees.@count > 5
发现有更多的5个元素员工的清单。 - 字符串和二进制属性的聚合表达式
@count
/@size
,例如name.@size = 5
,查找名称为5个字母的所有字符串。 - 关键路径可以遵循列表属性关系,例如
child.age >= 13
和cars.@avg.milage > 1000
。 - 关键路径也可以跟随链接对象(反向链接),例如
parents.age > 25
和parents.@count == 2
。 - 的
$
操作者可用于替代参数,例如child.age >= $0
(见下面的示例)。 - 排序和查找不同的值是可能的功能
SORT
和DISTINCT
如age > 20 SORT(name ASC, age DESC) DISTINCT(name)
。- 用于排序的排序可以是以下不区分大小写的文字中的一个:
ASC
,ASCENDING
,DESC
,DESCENDING
。 - 括号内的逗号分隔列表中可以显示任意数量的属性。
- 可以指示任意数量的排序/不同条件,它们将按指定的顺序应用。
- Sort或distinct不能独立运行,这些条件必须附加到至少一个查询过滤器。
- 用于排序的排序可以是以下不区分大小写的文字中的一个:
关于如何查询Realm的一个非常重要的例子是:
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'},
}
};
// 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});
});
// 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('cars.@sum.miles > $0', average);
console.log(`Longer than average (${average})`)
for (let p of longerThanAverage) {
console.log(` ${p.name}: ${p.cars.sum('miles')}`);
}
realm.close();
});
代码段的输出是:
Car owners
John
Joan
Jack
Longer than average (1288.3333333333333)
John: 4280
Joan: 2485
排序
Results
允许您根据单个或多个属性指定排序条件和顺序。例如,以下调用按上述数字按里程对上述示例中返回的汽车进行排序:
let hondas = realm.objects('Car').filtered('make = "Honda"');
// Sort Hondas by mileage
let sortedHondas = hondas.sorted('miles');
// Sort in descending order instead
sortedHondas = hondas.sorted('miles', true);
// Sort by price in descending order and then miles in ascending
sortedHondas = hondas.sorted([['price', true], ['miles', false]]);
Results
也可以对您正在排序的对象链接的对象的值进行排序:
let people = realm.objects('Person');
// Sort people by the milage of their cars
let sortedPeople = people.sorted('car.miles');
List
基本类型的s可以通过调用它们的值进行排序,sorted()
而无需指定属性:
let person = realm.objects('Person')[0];
let sortedTestScores = person.testScores.sorted();
请注意,Results
仅在查询排序时保证顺序保持一致。出于性能原因,不保证保留插入顺序。
自动更新结果
Results
实例是实时的,自动更新基础数据的视图,这意味着永远不必重新获取结果。修改影响查询的对象将立即反映在结果中。对此的一个例外是当使用for...in
或时for...of
,它将始终迭代在迭代开始时与查询匹配的对象,即使它们中的一些被删除或修改以在迭代期间被过滤器排除。
let hondas = realm.objects('Car').filtered('make = "Honda"');
// hondas.length == 0
realm.write(() => {
realm.create('Car', {make: 'Honda', model: 'RSX'});
});
// hondas.length == 1
这适用于所有Results
情况,包括那些被返回objects
,filtered
和sorted
方法。
这种属性Results
不仅使Realm快速高效,而且使您的代码更简单,更具反应性。例如,如果您的视图依赖于查询的结果,则可以将其存储Results
在属性中并对其进行访问,而无需确保在每次访问之前刷新其数据。
您可以订阅通知以了解Realm数据何时更新,指示应该刷新应用程序的UI的时间,而无需重新获取Results
。
限制结果
大多数其他数据库技术提供了从查询中“分页”结果的能力(例如SQLite中的'LIMIT'关键字)。这通常是为了避免从磁盘中读取太多内容,或者一次将太多结果拉入内存中。
由于Realm中的查询是惰性的,因此根本不需要执行这种分页行为,因为Realm只会在显式访问后从查询结果中加载对象。
如果出于UI相关或其他实现原因,您需要查询中特定的对象子集,那么就像获取Results
对象一样简单,只读取您需要的对象。
let cars = realm.objects('Car');
// get first 5 Car objects
let firstCars = cars.slice(0, 5);
迁移
使用数据库时,您的数据模型很可能会随时间而变化。例如,假设我们有以下Person
模型:
const PersonSchema = {
name: 'Person',
properties: {
firstName: 'string',
lastName: 'string',
age: 'int'
}
}
我们希望更新数据模型以要求name
属性,而不是分隔名和姓。为此,我们只需将架构更改为以下内容:
const PersonSchema = {
name: 'Person',
properties: {
name: 'string',
age: 'int'
}
}
此时,如果您使用以前的型号版本保存了任何数据,则新代码与Realm存储在磁盘上的旧数据之间将不匹配。发生这种情况时,除非运行迁移,否则在尝试使用新架构打开现有Realm时将引发异常。
执行迁移
您可以通过更新schemaVersion并定义可选migration
功能来定义迁移和关联的模式版本。您的迁移功能提供将数据模型从先前模式转换为新模式所需的任何逻辑。打开Realm
时,Realm
仅当需要迁移时,才会应用迁移功能来更新给定的模式版本。
如果未提供迁移功能,则会自动添加任何新属性,并在更新到新属性时从数据库中删除旧属性schemaVersion
。如果在升级版本时需要更新旧属性或填充新属性,可以在迁移功能中执行此操作。例如,假设我们想要迁移Person
先前声明的模型。您可以name
使用旧的firstName
和lastName
属性填充新架构的属性:
Realm.open({
schema: [PersonSchema],
schemaVersion: 1,
migration: (oldRealm, newRealm) => {
// only apply this change if upgrading to schemaVersion 1
if (oldRealm.schemaVersion < 1) {
const oldObjects = oldRealm.objects('Person');
const newObjects = newRealm.objects('Person');
// loop through all objects and set the name property in the new schema
for (let i = 0; i < oldObjects.length; i++) {
newObjects[i].name = oldObjects[i].firstName + ' ' + oldObjects[i].lastName;
}
}
}
}).then(realm => {
const fullName = realm.objects('Person')[0].name;
});
成功完成迁移后,您的应用程序可以像往常一样访问Realm及其所有对象。
线性迁移
使用上述迁移模式,迁移多个版本时可能会遇到问题。如果用户跳过应用更新并且在跳过的版本中多次更改属性,则可能会发生这种情况。在这种情况下,您可能需要编辑旧的迁移代码,以便将数据从旧架构正确更新到最新架构。
通过顺序运行多个迁移可以避免此问题,确保将数据库升级到每个先前版本并运行关联的迁移代码。遵循此模式时,永远不必修改旧的迁移代码,尽管您需要保留所有旧的模式和迁移块以供将来使用。这看起来像一个例子:
const schemas = [
{ schema: schema1, schemaVersion: 1, migration: migrationFunction1 },
{ schema: schema2, schemaVersion: 2, migration: migrationFunction2 },
...
]
// the first schema to update to is the current schema version
// since the first schema in our array is at
let nextSchemaIndex = Realm.schemaVersion(Realm.defaultPath);
while (nextSchemaIndex < schemas.length) {
const migratedRealm = new Realm(schemas[nextSchemaIndex++]);
migratedRealm.close();
}
// open the Realm with the latest schema
Realm.open(schemas[schemas.length-1]);
通知
和Realm
,Results
和List
对象提供addListener
了注册通知回调的方法。每当更新对象时,都将调用更改通知回调。
有两种通知,“领域通知”(提交写入事务时通知的简单回调)和“收集通知”(在插入,删除和更新时接收更改元数据的更复杂的回调)。
在某些情况下,可以在事务开始时调用侦听器- 如果Realm进入最新版本,或者以触发通知的方式修改或删除正在观察的Realm实体。在这些情况下,侦听器在当前写入事务的上下文中运行,因此尝试在通知处理程序中开始新的写入事务将引发异常。您可以使用该Realm.isInTransaction
属性来确定您的代码是否在写入事务中执行。
领域通知
每次提交写入事务时,Realm实例都会向其他实例发送通知。要注册通知:
function updateUI() {
// ...
}
// Observe Realm Notifications
realm.addListener('change', updateUI);
// ..later remove the listener
realm.removeListener('change', updateUI);
// ..or unregister all listeners
realm.removeAllListeners();
收集通知
收集通知包含描述在细粒度级别上发生了哪些更改的信息。这包括自上次通知以来已插入,删除或修改的对象的索引。集合通知是异步传递的:首先是初始结果,然后是在修改集合中任何对象的任何写入事务之后,从集合中删除对象,或者将新对象添加到集合中。
给出的通知回调函数addListener
在发生这些更改时接收两个参数。第一个是更改的集合,第二个是changes
具有关于受删除,插入和修改影响的集合索引的信息的对象。
前两个,删除和插入,只要对象开始和停止成为集合的一部分,就会记录索引。这会将对象添加到Realm或从Realm中删除它们时考虑在内。为此,Results
当您筛选特定值并更改对象以使其现在与查询匹配或不再匹配时也适用。对于基于的集合List
,这适用于在关系中添加或删除对象时。
每当对象的属性发生更改时,您的应用程序都会收到有关修改的通知,这些属性以前是该集合的一部分,并且仍然是其中的一部分。当to-one和to-many关系发生变化时,也会发生这种情况,但不会考虑反向关系的变化。
class Dog {}
Dog.schema = {
name: 'Dog',
properties: {
name: 'string',
age: 'int',
}
};
class Person {}
Person.schema = {
name: 'Person',
properties: {
name: {type: 'string'},
dogs: {type: 'list', objectType: 'Dog'},
}
};
我们假设您正在观察上面的模型代码给出的狗主人名单。在下列情况下,您将收到有关匹配Person
对象的修改的通知:
- 你修改
Person
的name
属性。 - 您添加或删除
Dog
到Person
的dogs
财产。 - 您修改属于该
age
属性的属性。Dog
Person
这使得可以离散地控制对UI内容进行的动画和视觉更新,而不是每次发生通知时任意重新加载所有内容。
collectionListenerRetainer = realm.objects('Dog').filtered('age < 2');
// Observe Collection Notifications
function listener(puppies, changes) {
// Update UI in response to inserted objects
changes.insertions.forEach((index) => {
let insertedDog = puppies[index];
...
});
// Update UI in response to modified objects
changes.modifications.forEach((index) => {
let modifiedDog = puppies[index];
...
});
// Update UI in response to deleted objects
changes.deletions.forEach((index) => {
// Deleted objects cannot be accessed directly
// Support for accessing deleted objects coming soon...
...
});
});
collectionListenerRetainer.addListener(listener);
// Unregister all listeners
realm.removeAllListeners();
// OR Unregister this listener
collectionListenerRetainer.removeListener(listener);
使用同步领域
您是否希望使用Realm Mobile Platform同步所有Realm数据库?所有与同步相关的文档已移至我们的平台文档中
加密
请注意我们许可证的出口合规部分,因为如果您位于有美国出口限制或禁运的国家/地区,它会对使用Realm进行限制。
Realm支持在创建Realm时通过提供64字节加密密钥,使用AES-256 + SHA2加密磁盘上的数据库文件。
var key = new Int8Array(64); // pupulate with a secure key
Realm.open({schema: [CarObject], encryptionKey: key})
.then(realm => {
// Use the Realm as normal
var dogs = realm.objects('Car');
});
这使得存储在磁盘上的所有数据都可以根据需要使用AES-256进行透明加密和解密,并使用SHA-2 HMAC进行验证。每次获得Realm实例时都必须提供相同的加密密钥。
使用加密领域时,性能受到很小影响(通常低于10%)。
例子
可以在realm-js存储库中的GitHub上找到示例。
请注意,在Android上,您需要安装NDK并且必须设置ANDROID_NDK
环境变量。
export ANDROID_NDK=/usr/local/Cellar/android-ndk/r10e
故障排除
缺少领域构造函数
如果您的应用程序崩溃,告诉您未找到Realm构造函数,您可以尝试以下几种方法:
首先,跑 react-native link realm
如果这没有帮助,并且您的问题出在Android上,请尝试:
在您的MainApplication.java
文件中添加以下内容:
import io.realm.react.RealmReactPackage;
并添加RealmReactPackage
到包列表:
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),
new RealmReactPackage() // add this line
);
}
在以下两行中添加settings.gradle
:
include ':realm'
project(':realm').projectDir = new File(settingsDir, '../node_modules/realm/android')
如果您的问题出现在iOS上,请尝试:
- 关闭所有模拟器/设备构建
- 停止在终端中运行的包管理器(或者更好的是,只需重新启动终端)
- 在Finder中打开app根目录中的ios文件夹
- 进入build文件夹(注意:你不会在atom中看到这个build文件夹,所以只需右键单击ios并在finder中单击open)
- 删除构建文件夹内的所有内容(只需移动到垃圾箱并保留垃圾,以防您担心)
- 运行
react-native run-ios
以重建整个事物
Chrome调试很慢
我们意识到这一点。这样做的原因是,由于Realm是用C ++编写的并且运行本机代码,因此它必须在设备/模拟器上运行。但是考虑到零复制体系结构,每次检查存储在Realm中的对象时,我们都需要通过RPC线在realm对象中发送值。
我们正在研究解决此问题的各种潜在解决方案。如果您想跟踪它,可以按照GitHub问题进行操作。
无法下载 realm-sync-cocoa
我们已经看到一些报告,由于下载问题,用户无法构建他们的应用程序。症状是您看到类似的错误消息Error: unexpected end of file at Zlib.zlibOnError [as onerror] (zlib.js:142:17) errno: -5, code: 'Z_BUF_ERROR' }
。
可以手动下载所需的文件,然后构建应用程序。步骤是:
- 找到您的项目目录,然后找到
node_modules/realm/vendor/realm-ios
。它会是空的。 download-realm.lock
使用以下命令创建文件:echo SYNC_SERVER_FOLDER=sync SYNC_ARCHIVE=realm-sync-cocoa-3.7.0.tar.gz SYNC_ARCHIVE_ROOT=core > download-realm.lock
。版本号(此处为3.7.0)必须与更改日志中的Realm Sync版本匹配。- 找到下载的文件,
realm-sync-cocoa-3.7.0.tar.gz
执行此命令tar -xzvf realm-sync-cocoa-3.7.0.tar.gz -C yourProjectDirectory/node_modules/realm/vendor/realm-ios
。 - 你会发现该目录不再是空的。复制目录下的所有文件
core
并将其粘贴到目录下yourProjectDirectory/node_modules/realm/vendor/realm-ios
。
崩溃报告
我们鼓励您在应用程序中使用崩溃报告器。许多Realm操作可能在运行时失败(与任何其他磁盘IO一样),因此从应用程序收集崩溃报告将有助于确定您(或我们)可以改进错误处理和修复崩溃错误的区域。
大多数商业崩溃记者都可以选择收集日志。我们强烈建议您启用此功能。在抛出异常和不可恢复的情况时,Realm会记录元数据信息(但没有用户数据),这些消息可以在出现问题时帮助调试。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具