はじめに
前回はRealmにおけるモデルオブジェクトの作成方法とデータベースへの書き込み方法について説明しました。
第4回目となる今回は、クエリを使用してデータベースからデータを取得する方法について説明します。
過去記事は以下を参照ください。
環境
Xcode:9.4.1
Swift:4.1.2
Realm:3.7.4
前準備
以下のように、選手を表すPlayerに3件のデータがある前提で説明をします。
Player
name | height | weight |
---|---|---|
HIRO | 163 | 60.5 |
CHIHIRO | 175 | 58 |
SHUHRI | 170 | 61.2 |
参考までに、上記のモデル定義とデータ登録用のコードを以下に示します。
/// 選手モデル class Player: Object { // 選手名 @objc dynamic var name : String = "" // 身長(単位:m) @objc dynamic var height : Double = 0.0 // 体重(単位:kg) @objc dynamic var weight : Double = 0.0 } // データ登録用コード例 // 1人目の選手 let player1 = Player() player1.name = "HIRO" player1.height = 163.0 player1.weight = 60.5 // 2人目の選手 let player2 = Player() player2.name = "CHIHIRO" player2.height = 175.0 player2.weight = 58.0 // 3人目の選手 let player3 = Player() player3.name = "SHURI" player3.height = 170.0 player3.weight = 61.2 // 上記で作成した3人の選手を格納するリストの作成 let players = List<Player>() players.append(player1) players.append(player2) players.append(player3) // Realmのインスタンスを生成する let realm = try! Realm() // 書き込みトランザクション内でデータを追加する try! realm.write { realm.add(players) }
全データの取得
もっとも基本的なクエリは全データの取得です。
全データを取得する場合の書式は以下の通りです。
書式:全データの取得
let 結果格納用変数 = Realmインスタンス名.objects(モデルクラス名.self)
クエリを実行すると、Resultsクラスのインスタンスが返されます。クエリの結果はデータのコピーではないため、書き込みトランザクション内で変更すると、ディスク上のデータに反映されます。
Playerから全てのデータを取得する例を以下に示します。
全データを取得する例
// (1)Realmのインスタンスを生成する let realm = try! Realm() // (2)全データの取得 let results = realm.objects(Player.self) // (3)取得データの確認 print(results)
上記の実行結果は以下のようになります。
実行結果:全データの取得
Results<Player> <0x7ffdaa611380> ( [0] Player { name = HIRO; height = 163; weight = 60.5; }, [1] Player { name = CHIHIRO; height = 175; weight = 58; }, [2] Player { name = SHURI; height = 170; weight = 61.2; } )
件数の取得
何件取得できたのかを知りたい場合は、countプロパティを使用します。
データ件数の取得例
// 取得件数の表示 print(results.count)
各レコードデータの参照
取得結果は配列になっていますので、「取得結果[インデックス].プロパティ」で参照することができます。
取得結果の1件目のデータを参照する例を以下に示します。
取得結果の1件目を参照する例
// 取得結果の1件目を表示 print("氏名 = \(results[0].name)") print("身長 = \(results[0].height)") print("体重 = \(results[0].weight)")
filterによるクエリ
条件を指定したクエリを実行したい場合は filter メソッドを使用します。
filterメソッドはいくつかのオーバーライドがあります。代表的な書式を以下に示します。1つは文字列によるクエリ式を使用する方法、もう1つはNSPredicateクラスでクエリ式を作成して filterメソッドに渡す方法です。
filterメソッドの書式
// (1)文字列によるクエリ var クエリ結果格納用変数 = realm.objects(モデルクラス名.self).filter("クエリ文字列") // (2)NSPredicateクラスによるクエリ let predicate = NSPredicate(format: "クエリ文字列", パラメータ1, ... パラメータn) var クエリ結果格納用変数 = realm.objects(モデルクラス名.self).filter(predicate)
Playerモデルの nameプロパティが「HIRO」であるレコードを取得するクエリ例を以下に示します。
// 文字列によるクエリ let results = realm.objects(Player.self).filter("name == 'HIRO'") // NSPredicateクラスを使用したクエリ let predicate = NSPredicate(format: "name == %@", "HIRO") let results = realm.objects(Player.self).filter(predicate)
文字列によるクエリでは、「name = ‘HIRO’」という文字列を渡すことで、データを取得しています。
もう1つのNSPredicateによるクエリは、はじめにNSPredicateクラスを使用してfilterに渡す条件式を作成します。NSPredicateの第1引数にはクエリの文字列を指定するのですが、この文字列の中に 「%@」を埋め込むと、その部分は第2引数以降に指定した値で置換されます。「%@」はいくつでも使用することができます。また第2引数以降には、クエリ文字列の中に埋め込んだ「%@」の数だけ、カンマで区切って値を指定します。
以降のコード例は、文字列によるクエリの方法を使用して説明をしていきます。
比較演算子
クエリ文字列で使用可能な比較演算子には以下のものがあります。
演算子 | 説明 | 例 |
---|---|---|
== | 等しい | プロパティ名 = 値 |
< | 未満 | プロパティ名 < 値 |
<= | 以下 | プロパティ名 <= 値 |
> | より大きい | プロパティ名 > 値 |
>= | 以上 | プロパティ名 >= 値 |
!= | 等しくない | プロパティ名 != 値 |
BETWEEN | 最小値と最大値の間 | プロパティ名 BETWEEN {最小値, 最大値} |
上記で示した演算子の使用例を以下に示します。
比較演算子の使用例
// 「=」演算子 nameプロパティが'HIRO'と等しい let results = realm.objects(Player.self).filter("name == 'HIRO'") // 「<」演算子 heightプロパティが170未満 let results = realm.objects(Player.self).filter("height < 170") // 「<=」演算子 heightプロパティが170以下 let results = realm.objects(Player.self).filter("height <= 170") // 「>」演算子 heightプロパティが170より大きい let results = realm.objects(Player.self).filter("height > 170") // 「>=」演算子 heightプロパティが170以上 let results = realm.objects(Player.self).filter("height >= 170") // 「!=」演算子 nameプロパティが'HIRO'以外 let results = realm.objects(Player.self).filter("name != 'HIRO'") // 「BETWEEN」演算子 heightプロパティが165から175の間 let results = realm.objects(Player.self).filter("height BETWEEN {165, 175}")
パターン検索演算子
Realmでは、パターン検索を行うことも可能です。以下にパターン検索で使用できる演算子を示します。String型とData型のプロパティに対して使用可能です。
演算子 | 説明 | 例 |
---|---|---|
BEGINSWITH |
先頭が指定した文字で始まるデータを検索 | プロパティ名 BEGINSWITH = 値 |
ENDSWITH | 末尾が指定した文字で終わるデータを検索 | プロパティ名 ENDSWITH 値 |
CONTAINS | 指定した文字が含まれるデータを検索 | プロパティ名 CONTAINS 値 |
LIKE | LIKE検索 | プロパティ名 like 値
※「?」と「*」をワイルドカード文字として使用可能。 「?」は1つの文字と一致、「*」は0以上の文字との一致を表す |
以下に、パターン検索演算子の使用例を示します。
パターン検索演算子の使用例
// 「BEGINSWITH」演算子 先頭が「CH」で始まるデータを検索 let results = realm.objects(Player.self).filter("name BEGINSWITH 'CH'") // 「ENDSWITH」演算子 末尾が「RO」で終わるデータを検索 let results = realm.objects(Player.self).filter("name ENDSWITH 'RO'") // 「CONTAINS」演算子 「HI」を含むデータを検索 let results = realm.objects(Player.self).filter("name CONTAINS 'HI'") // 「like」演算子 「任意の1文字 + 'IR' + 0個以上の文字」のデータを検索 let results = realm.objects(Player.self).filter("name LIKE '?IR*'")
IN句
RealmでもIN句を使用することが可能です。
演算子 | 説明 | 例 |
---|---|---|
IN |
指定して複数の値のいずれかと一致するデータを検索 | プロパティ名 IN {値1, 値2, … 値n} |
以下に、IN句の使用例を示します。
IN句の使用例
// 「IN句」 name が'HIRO', 'SHURI'のいずれかと一致するデータを取得 let results = realm.objects(Player.self).filter("name IN {'HIRO','SHURI'}")
論理演算子
Realmでも論理演算子を使用して、複合条件を指定したクエリを実行することができます。
以下に使用可能な論理演算子を示します。
演算子 | 説明 | 例 |
---|---|---|
AND |
指定した両方の式に合致するデータを検索します | 条件式1 AND 条件式2 |
OR | 指定した式のいずれかに合致するデータを検索します | 条件式1 OR 条件式2 |
NOT | 指定した式を否定するデータを検索します | NOT 条件式 |
以下に、論理演算子の使用例を示します。
論理演算子の使用例
// 「AND」演算子 nameの末尾が'RO' かつ heightが165より大きい データを取得 let results = realm.objects(Player.self).filter("name ENDSWITH 'RO' AND height > 165") // 「OR」演算子 nameの末尾が'RO' または heightが165より大きい データを取得 let results = realm.objects(Player.self).filter("name ENDSWITH 'RO' AND height > 160") // 「NOT」演算子 nameの末尾が'RO' または heightが165より大きい データを取得 let results = realm.objects(Player.self).filter("NOT name ENDSWITH 'RO'")
クエリの連鎖
クエリは連鎖させることができます。
例えば、「身長が170以上」というクエリの後に、「名前に’HIRO’を含む」というクエリを連鎖して実行することができます。
filetrをメソッドチェーンで実行することも、複数行に分けて実行することもできます。
以下に使用例を示します。
クエリの連鎖例
// (1)メソッドチェーンによるクエリの実行 let results = realm.objects(Player.self).filter("height >= 170").filter("name CONTAINS 'HIRO'") // (2)複数行でのクエリの実行 let results = realm.objects(Player.self).filter("height >= 170") let results2 = results.filter("name CONTAINS 'HIRO'")
結果から最初の1件を取得する
複数件の結果から、最初の1件だけを取得したい場合は firstプロパティを使用します。
以下に使用例を示します。
最初の1件を取得する例
let results = realm.objects(Player.self).first
並べ替え
sortedメソッドを使用することで、取得結果を並べ替えることができます。
いくつかのオーバーロードメソッドがありますが、一番使用しやすいのは以下の書式です。
sortedメソッドの第1引数には並べ替えのキーとなるプロパティ名を、第2引数は昇順の場合はtrue、降順の場合はfalseを指定します。
let 結果格納用変数 = realm.objects(モデルクラス名.self).sorted(byKeyPath: "プロパティ", ascending: Bool値)
以下にsortedメソッドの使用例を示します。この例ではnameプロパティを降順で並べ替えて結果を取得します。
sortedメソッドの使用例
// nameプロパティを降順で並べ替えた結果を取得 let results = realm.objects(Player.self).sorted(byKeyPath: "name", ascending: false)
コメント
[…] 4.クエリによるデータの取得 […]
すごく勉強になりました。
ありがとうございました!
[…] 下記URLの「5.filterによるクエリ」が参考になりました。 [Realm][Swift4対応 完全保存版] 4.クエリによるデータの取得 […]
[…] 「SWIFT LIFE」 […]
[…] Realmのデータを検索する方法はググればすぐ出てくると思いますが、こちらの記事は参考になるかと思います。今回修正する箇所はMainViewController.swiftのlongPressed()です!コードは下記です〜! […]