CoreData で SQL 的に NOT LIKE を書くとエラーになる話

 分かっていると当たり前の話なのですが、CoreData で SQL ライクに NOT LIKE を書こうとした時に、「ん?」となったので、ちょっと覚え書き。

 もし、どなたかのお役に立ちましたら。

 ちなみに、使用言語は Swift ですが、Objective-C でもほぼ変わらない筈です。

スポンサーリンク

読み込み中です。少々お待ち下さい

まえおき

 CoreData の NSPredicate でも、当然ながら「LIKE」は使えるのですが、書き方が SQL の LIKE とは微妙に異なります。

 まず、ワイルドカードが「%」「_」ではなく「*(0 文字以上一致)」「?(1 文字以上一致)」という違いがあります。

 また、LIKE の他に「BEGINSWITH」「CONTAINS」「ENDSWITH」が用意されており、それぞれ SQL の LIKE で良く使われる「pattern%」「%pattern%」「%pattern」に相当すると考えて良いでしょう。

 ですので、SQL 的な感覚を引き摺っていると少々気持ち悪く感じられるのですが、CoreData では「LIKE」は素直に文字列の完全一致に使用し、単純な部分一致に関しては、パターンに含まれる「*」や「?」のエスケープをいちいち考慮しなくてはならない「LIKE」よりも、「BEGINSWITH」「CONTAINS」「ENDSWITH」を使った方が楽ができそうです。

 余談ですが、さらに複雑なパターンマッチには、正規表現を扱える「MATCHES」を使うことになるでしょう。

 さて、もう少し具体的に、コードで見てみましょう。

 例えば「title」という Attribute(SQL でいう COLUMN)を持つ「Event」という Entity(SQL でいう TABLE)が存在するとします。

 SQL ならば「SELECT * FROM EVENT WHERE TITLE LIKE '%hoge%'」と書くような場合、CoreData では以下のようなコードになります。

// これは単なるサンプルです。context は NSManagedObjectContext であれば何でも構いません let context = self.fetchedResultsController.managedObjectContext let pattern = "hoge" let request = NSFetchRequest(entityName: "Event") request.predicate = NSPredicate(format: "title CONTAINS %@", pattern) do { if let results = try context.executeFetchRequest(request) as? [NSManagedObject] { for result in results { // 何らかの処理を行います。ここではサンプルとして Attribute 名と値を列挙しています print("{") for attributeName in result.entity.attributesByName { print(" \(attributeName.0) = \(result.valueForKey(attributeName.0))") } print("}") } } } catch { // Error handling }

NOT LIKE を書く

 以上の前提を踏まえて、ようやく本題の NOT LIKE です。

 SQL と同じ感覚で、次のように書くとエラーが発生します。

request.predicate = NSPredicate(format: "title NOT LIKE %@", pattern) // 以下のようなエラーが発生します // 'NSInvalidArgumentException', reason: 'Unable to parse the format string "title NOT LIKE %@"'

 CoreData でも NOT は使えるのですが、置く位置が異なります。以下のように書きます。

request.predicate = NSPredicate(format: "NOT title LIKE %@", pattern)

 上述したように、SQL でいうところの「LIKE '%pattern%'」は「CONTAINS」なので、実際は次のように書くことになるでしょう。

request.predicate = NSPredicate(format: "NOT title CONTAINS %@", pattern)

おわりに

 単語としては SQL と同じなのに、書き方や意味が異なるケースがちょいちょいあるのが紛らわしいです。

 どうせ中身は SQLite なんだから(いや、SQLite とは限らないけど)、下手に似たような書き方をするくらいなら、いっそのことそのままでいいじゃないかと思うのですが、きっとなんか色々あるんでしょう。

 まぁ、「NOT (attribute1 LIKE %@ OR attribute2 LIKE %@)」みたいに書けるので、より理に適っていると言われれば、そんな気もしますけれども。

 NSPredicate のフォーマットについてより詳しくは、本家のこちらのドキュメント等を参照してください。

この記事をシェア
  • このエントリーをはてなブックマークに追加
  • Share on Google+
  • この記事についてツイート
  • この記事を Facebook でシェア