查询语言

Realm JavaScript SDK 支持基于受NSPredicate启发的语言进行查询。

Collection.filtered()方法用于查询 Realm:

let contacts = realm.objects('Contact');
let friendsPage2 = contacts.filtered('type == "friend" AND name BEGINSWITH "B"');

可以按具有键路径的链接对象或子对象进行筛选。

示例:

let johnsChildren = realm.Object('Contact').filtered('father.name == "John"');

查询字符串可以使用编号( $0$1 ……)占位符。 后续参数包含值。 尚不支持命名占位符。

示例:

let merlots = wines.filtered('variety == $0 && vintage <= $1', 'Merlot', maxYear);

条件操作符

您可以对所有属性类型使用相等比较: ==!=

此外,以下内容可用于数字类型: <<=>>=

示例:

let oldContacts = realm.objects('Contact').filtered('age > 2');

请注意,对于布尔属性,您应该针对truefalse进行测试。

示例:

let women = realm.objects('Contact').filtered('isMale == false');

此外,您可以使用IN操作符来查询属性。

示例:

let favoriteNames = realm.objects('Contact').filtered("name IN {'John', 'Mary'}");

字符串操作符

对于字符串属性,支持使用BEGINSWITHENDSWITHCONTAINSLIKE操作符进行前缀、后缀和子字符串查询。

对于任何字符串操作,您都可以将[c]附加到操作符,使其不区分大小写。

示例:

let peopleWhoseNameContainsA = realm.objects('Contact').filtered('name CONTAINS[c] "a"');
let Johns = realm.objects('Contact').filtered('name ==[c] "john"');

您可以使用LIKE进行简单的通配符匹配,它支持使用?匹配单个字符,使用*匹配零个或多个字符。

示例:

// Matches "John" and "Johnny"
let Johns = realm.objects('Contact').filtered('name LIKE "John*"');

组合

使用括号()以及&& / AND|| / OR操作符组成查询。 您可以使用! / NOT否定谓词。

时间戳

通常可以使用变量替换来编写查询,以便 Realm 处理日期语法。 但是,有时更适合编写完全基于字符串的查询。 在这种情况下,语法遵循YYYY-MM-DD@HH:MM:SS:NYYYY-MM-DDTHH:MM:SS:N格式,其中:N后缀指定纳秒,但可以省略(默认为 0)。 如果愿意,编程格式还支持TSS:NS ,它是一个字面量T ,后跟自Unix纪元以来的秒数和纳秒修饰符。 在这两种格式中,负纳秒都被视为无效语法。 时间戳不像字符串那样被引号括起来。 由于平台限制,不支持对窗口上Unix纪元之前的日期和其他平台上 1901 之前的日期使用第一个语法。

realm.objects('Person').filtered('birthday == 2017-12-04@0:0:0') // readable date omitting nanoseconds (assumes 0)
realm.objects('Person').filtered('birthday == 2015-7-2@14:23:17:233') // readable date including nanoseconds
realm.objects('Person').filtered('birthday == T1435846997:233') // equivalent to above
realm.objects('Person').filtered('birthday == 1970-1-1@0:0:0:0') // epoch is the default non-null Timestamp value

限制结果集的大小

为了限制结果集中的对象数量,可以使用LIMIT

示例:

realm.objects('Person').filtered('age >= 20 LIMIT(2)')  // at most two objects which fulfil the condition

对集合的查询

当对象包含列表时,您可以使用collection操作符ANYALLNONE查询这些列表。

示例:

// Find contacts with one or more teenage friends
let teens = realm.objects('Contact').filtered('ANY friends.age < 14');

// Find contacts where all friends are older than 21
let adults = realm.objects('Contact').filtered('ALL friends.age > 21');

您可以使用聚合操作符.@count.@avg.@min.@max.@sum查询列表中属性的聚合。

示例:

// Find contacts without friends
let lonely = realm.objects('Contact').filtered('friends.@count == 0');

// Find contacts where the average age of their friends is above 40
let adults = realm.objects('Contact').filtered('friends.@avg.age > 40');

使用SUBQUERY操作符的子查询允许您在查询列表时跨多个参数筛选列表。

示例:

// Find contacts with friends above 21 in SF
let teens = realm.objects('Contact').filtered('SUBQUERY(friends, $friend, $friend.age > 21 AND $friend.city = "SF").@count > 0');

对基元列表的查询

基元值列表的查询语法与通过链接或列表查询对象列表的语法基本相同。 但是,存在一些细微差别。 为了进行说明,让我们构建一个电影数据库,其中每部电影都有一个匿名星级评分和一个字符串标签列表。

const MovieSchema = {
  name: 'Movie',
  properties: {
    name: 'string',
    ratings: 'int[]',
    tags: 'string[]',
  }
};
let realm = new Realm({schema: [MovieSchema]});
realm.write(() => {
  let m0 = realm.create('Movie', {
    name: 'The Matrix',
    ratings: [5, 5, 3, 4, 5, 1, 5],
    tags: ['science fiction', 'artificial reality'],
  });
  let m1 = realm.create('Movie', {
    name: 'Inception',
    ratings: [3, 5, 3, 4, 5, 5],
    tags: ['dream', 'science fiction', 'thriller'],
  })
});

就像使用对象列表一样,我们可以使用聚合: .@count.@avg.@min.@max.@sum 。 也可以使用collection操作符( ANYALLNONE )。如果未指定任何内容,则暗示为ANY 。 让我们看一下电影数据库中的一些查询示例:

// Find movies which have a "science fiction" tag, [c] marks a case insensitive string comparison
realm.objects('Movie').filtered('tags =[c] "science fiction"')
// Find movies which have any tag that begins with the text "science" (string operators: LIKE, CONTAINS, BEGINSWITH, ENDSWITH are also available)
realm.objects('Movie').filtered('tags BEGINSWITH[c] "science"')
// Find movies that have only single word tags by filtering out any words with a space
realm.objects('Movie').filtered('NONE tags CONTAINS " "')
// Find movies that have an average rating of more than 4 stars
realm.objects('Movie').filtered('ratings.@avg >= 4')
// Find movies that do not have any one star ratings
realm.objects('Movie').filtered('ALL ratings > 1')
// Find movies that have a tag that is also the title (multi-property comparison of the same types is allowed)
realm.objects('Movie').filtered('ANY tags = name')

与对象列表相比,对基元列表有一个独特的操作,即.length 。 此操作符将比较字符串或二进制类型的每个单独元素的长度。 这是因为.@size操作符已用于指定列表中的元素数量。

// Find movies that have over 100 tags
realm.objects('Movie').filtered('tags.@size > 100')
// Find movies that have a tag (ANY is implied) that is over 100 characters in length
realm.objects('Movie').filtered('tags.length > 100')

反向链接查询

由于反向链接是一个间接概念,因此让我们考虑一个使用以下模型的运行示例:

const CarSchema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    price: 'double',
    owner: 'Person',
  }
};
const PersonSchema = {
  name: 'Person',
  properties: {
    name: 'string',
    cars: { type: 'linkingObjects', objectType: 'Car', property: 'owner' }, // backlinks of Car.owner, this property is optional
  }
};

链接是Realm 对象类型的属性,例如Car.owner是前向链接。您可以按照链接属性名称查询正向链接链(请参阅以下示例中的查询 1)。 该查询具有自描述性。 如果我们想知道哪些人拥有某种类型的汽车怎么办? 我们可以使用反向链接找到它。 反向链接是一个术语,用于描述向后跟踪关系。 这种向后关系对于每个链接属性始终存在,并且不需要在模型中命名。 我们可以使用命名的反向链接(以下示例中的查询 2),就像使用正向关系一样。 不过,您也可以使用@links.ClassName.PropertyName语法完整描述正向关系,以查询没有名称的反向链接(下例中的查询 3)。 无论是否命名,反向链接都可以被视为一个collection。这意味着您可以照常使用所有collection操作符(以下示例中的查询 4-7)。除了collection操作符外,还有特殊语法允许查询所有反向链接关系的计数(以下示例中的查询 8),这与查询特定关系的反向链接计数(以下示例中的查询 5)不同,除非总共只有一个传入链接。虽然使用命名与未命名反向链接在功能上没有区别,但查询语法的可读性会受到影响,因此通常首选尽可能使用linkingObject属性来命名反向链接。

例子:

// Query 1) Find all cars which have an owner named 'bob' (case insensitive equality)
realm.objects('Car').filtered('owner.name ==[c] "bob"')
// Query 2) Find which people own a certain type of car by using a named backlink (any is implied)
realm.objects('Person').filtered('cars.make ==[c] "honda"')
// Query 3) Find which people own a certain type of car by using the unnamed backlink syntax
realm.objects('Person').filtered('@links.Car.owner.make ==[c] "honda"')
// Query 4) collection aggregate operator example using the unnamed backlink syntax
realm.objects('Person').filtered('@links.Car.owner.@avg.price > 30000')
// Query 5) Find people who have 3 cars using the named backlink syntax
realm.objects('Person').filtered('cars.@count == 3')
// Query 6) Find people who own a Honda which has a price > 30000 using the unnamed backlink syntax with SUBQUERY
realm.objects('Person').filtered('SUBQUERY(@links.Car.owner, $x, $x.make ==[c] "honda" && $x.price > 30000).@count > 1')
// Query 7) Find people who own only a specific type of car
realm.objects('Person').filtered('ALL @links.Car.owner.make ==[c] "honda"')
// Query 8) Find people with no incoming links (across all linked properties)
realm.objects('Person').filtered('@links.@count == 0')