EvernoteのAPIを使ったノート検索でfindNotesの代わりにfindNotesMetadataを使ってみた



## 前置き

以前、[EvernoteAPIの話](http://qiita.com/yshr04hrk/items/ac05dee1c45c1543ed00)を書いていて、「検索のAPIは2つあるようなのですがそのうち調べます」とか言っていたものを書きました。EvernoteAPIのうち、ノートの検索に使うfindNotesとfindNotesMetadataの話です。ノートの検索に使うAPIであるfindNotesはdeperecatedとのことなので、推奨されているfindNotesMetadataを使ってみました。

## 概要

findNotesとfindNotesMetadataはどちらもノートのメタデータを返します。メタデータなので実際のノートの中身や、中身に埋め込まれた画像などのバイナリデータは含まれません。
findNotesMetadataで変わった点は、新しい引数を使ってこのメタデータに含めるデータを指定できるところです。findNotesで返されるメタデータに含まれるデータは固定なのですが、findNotesMetadataを使うことでより柔軟に結果を得られるようになったようです。（たぶん、速くなったりメモリを食わなくなったりしてるんだと思います。）また、容量の大きそうなノートの中身やバイナリデータのサイズなども取得できるので、その辺りを見つつうまいことやる、なんてこともできそうです。
この記事では、findNotesとfindNotesMetadataの使い方を示して、それぞれの違いについて説明します。

## 公式ドキュメント

説明します、といいつつ、この文章は解説というより、自分の使いみちにあった感じで書いたらこうなった、というところです。もっと色々調べたい方は公式のドキュメントをあたってください。というか、検索に限らず、API使って何かしたいと思ったら、まずドキュメントを片手に唸る感じです…。
この記事に関連するドキュメントはこちら。他は必要に応じてリンクを貼っています。またGUIDなど用語に関しては[前に書いた記事](http://qiita.com/yshr04hrk/items/ac05dee1c45c1543ed00)や[用語集 - Evernote Developers](http://dev.evernote.com/intl/jp/support/glossary.php)をご覧ください。

* [ノートの検索 - Evernote Developers](http://dev.evernote.com/intl/jp/doc/articles/searching_notes.php)
* [findNotesのドキュメント](http://dev.evernote.com/doc/reference/NoteStore.html#Fn_NoteStore_findNotes)
* [findNotesMetadataのドキュメント](http://dev.evernote.com/doc/reference/NoteStore.html#Fn_NoteStore_findNotesMetadata)


## findNotesのコード例

さて、まずは今までの、findNotesを使ったコード例を乗せます。このfind_notesメソッドは、Evernoteのノートの属性のうちいくつかをまとめたNoteオブジェクトの配列を返します。

```ruby
def find_notes(options={})
  guid  = options[:guid]			# 検索するノートブックのGUID
  words = options[:words]			# 検索語句
  count = options[:count] || 1		# 検索するノートの数

  # 検索フィルタの作成
  filter = Evernote::EDAM::NoteStore::NoteFilter.new
  filter.notebookGuid ||= guid
  filter.words ||= words

  noteslist = @note_store.findNotes(@token, filter, 0, count)
  noteslist.notes
end
```

## findNotesMetadataのコード例

これをfindNotesMetadataを使って、同じように動作させようと修正をしたらこうなりました。（動作は同じですが、実は返り値が変わっています。これについては後述します。）

```ruby
def find_notes(options={})
  guid  = options[:guid]			# 検索するノートブックのGUID
  words = options[:words]			# 検索語句
  count = options[:count] || 1		# 検索するノートの数

  # 検索フィルタの作成
  filter = Evernote::EDAM::NoteStore::NoteFilter.new
  filter.notebookGuid ||= guid
  filter.words ||= words

  # ここから変更点
  spec = Evernote::EDAM::NoteStore::NotesMetadataResultSpec.new
  metadatas = @note_store.findNotesMetadata(@token, filter, 0, count, spec)
  metadatas.notes
end
```

次にこの2つの違いについて説明します。

## 違いその1：spec

まず両者で変わったところ（返り値は後述します）というと、メソッドに渡す引数にspecが追加されました。
specには[Evernote::EDAM::NoteStore::NotesMetadataResultSpec](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NotesMetadataResultSpec)というクラスのオブジェクトを渡します。このオブジェクトには、返り値のメタデータに何を含めるかを指定します。ただnewしただけのspecを使うと、GUIDしか含まれないメタデータを返してきます。そこで、タイトルも返して！　というときには、つぎのように指定したspecをfindNotesMetadataメソッドに渡してやるといいようです。

```ruby
  spec.includeTitle = true
```

タイトルの他に、作成・更新・削除日時、ノートの中身のサイズなども検索結果のメタデータに含められるようです。（実際に何が含められるかは上記のspecのドキュメントをご覧ください。）

## 違いその2：返り値

次に違う点は返り値です。それぞれ次のようなオブジェクトを返します。

* findNotes
  * [Evernote::EDAM::NoteStore::NoteList](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NoteList)オブジェクト
* findNotesMetadata
  * [Evernote::EDAM::NoteStore::NotesMetadataList](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NotesMetadataList)オブジェクト

また上記のコード例では、どちらでもそれぞれのAPIの返り値に対して#notesメソッドを叩いています。この#notesメソッドの返り値が、検索に引っかかったノートのデータです。これの返り値も、findNotesとfindNotesMetadataで違います。

* findNotes#notes
  * [Evernote::EDAM::NoteStore::Note](http://dev.evernote.com/doc/reference/Types.html#Struct_Note)の配列（ただしノートの中身と画像などのバイナリデータを省いたもの）
* findNotesMetadata#notes
  * [Evernote::EDAM::NoteStore::NoteMetadata](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NoteMetadata)オブジェクトの配列

このNoteMetadataに、specで指定したデータが入っています。
なお、空のspecを指定しても、ノートのGUIDは必ず含まれます。（というより、それがないとノートを指定できない）findNotes側で出てくるNoteオブジェクトもノートのGUIDを持っています。

まとめると次のようになります。

API               | 返り値のクラス           | #notesの返り値      | #notes で取得できるもの               
------------------|-------------------|-----------------|-------------------------------
findNotes         | [ÏNoteList](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NoteList)          | [Note](http://dev.evernote.com/doc/reference/Types.html#Struct_Note)の配列         | GUIDやタイトルなど（ノートのデータのうち、中身やバイナリデータを除いたもの
findNotesMetadata | [NotesMetadataList](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NotesMetadataList) | [NoteMetadata](http://dev.evernote.com/doc/reference/NoteStore.html#Struct_NoteMetadata)の配列 | GUIDとspecで指定したデータ

じゃあ実際のノートの中身はどうすんだ、という話になりますが、そのときはGUIDを使ってノートの実体なり中身なりを取得します。この話はまた別に書きたいなあと思ってます。

## まとめ

ノートを検索するときにはfindNotesMetadataの方が欲しいデータだけ拾ってこれるので便利そうです。findNotesではタイトルなど返り値に含まれるデータは固定ですが、findNotesMetadataでGUIDしかいらない、というときにも対応できます。私が作っているものについては、GUIDだけ必要なのでまさにこのパターンでした。
findNotesMetadataの方が、（場合によりますが）余計なデータを取ってこないのでその分早かったりメモリを食わなかったりしそうです。多分。おそらく検索結果が膨大になる場合には結構影響が出そうだなあと思っています。