python から riak を操作する


# 概要

python 3.4 から riak 2.0.x を操作します。python クライアントのドキュメントは、http://basho.github.io/riak-python-client/index.html にあります。 

# 準備
## riak をインストール

brew コマンドで riak 2.0.4 のインストールが可能です。brew コマンドだと、最新から遅れているようなので、最新にしたい場合は、各自でどうぞ。

```
% brew install riak
```
ファイルディスクリプタを変更するために設定をします。これらを実施しないと起動してくれないようです。

```/Library/LaunchDaemons/limit.maxfiles.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
    <dict>
      <key>Label</key>
        <string>limit.maxfiles</string>
      <key>ProgramArguments</key>
        <array>
          <string>launchctl</string>
          <string>limit</string>
          <string>maxfiles</string>
          <string>65536</string>
          <string>65536</string>
        </array>
      <key>RunAtLoad</key>
        <true/>
      <key>ServiceIPC</key>
        <false/>
    </dict>
  </plist>
``` 
  

```/Library/LaunchDaemons/limit.maxproc.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple/DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
    <dict>
      <key>Label</key>
        <string>limit.maxproc</string>
      <key>ProgramArguments</key>
        <array>
          <string>launchctl</string>
          <string>limit</string>
          <string>maxproc</string>
          <string>2048</string>
          <string>2048</string>
        </array>
      <key>RunAtLoad</key>
        <true />
      <key>ServiceIPC</key>
        <false />
    </dict>
  </plist>
```


ここでOSを再起動。

起動後に設定を確認。

```
% launchctl limit
cpu         unlimited      unlimited      
filesize    unlimited      unlimited      
data        unlimited      unlimited      
stack       8388608        67104768       
core        0              unlimited      
rss         unlimited      unlimited      
memlock     unlimited      unlimited      
maxproc     2048           2048           
maxfiles    65536          65536   
```	
      
## riak 起動  

pong と表示すればOK。

```   
% riak start
% riak ping
pong
```

## 設定を変更

allow_multをtrueにします。
まず起動時に自動生成された設定ファイルの場所を確認します。

```
$ riak config effective | grep allow_mult
buckets.default.allow_mult = false
```

上記の出力情報ですと false でしたので true に変更します。

```
$ sudo echo "buckets.default.allow_mult = true" >> /usr/local/Cellar/riak/2.0.4/libexec/etc/riak.conf
```

riakを再起動してtrueになっていることを確認します。

```
$ riak restart
$ riak config effective | grep allow_mult
buckets.default.allow_mult = true
```

## riak クライアントをインストール

公式でもある riak-python-client をインストールします。

```
% pip install riak
```

# KVS としての riak

## 簡単な例

```
% ipython
import riak
client = riak.RiakClient()
bucket = client.bucket('manifestations')
obj = bucket.new('4873113938', data={
  'title': '初めてのPython 第3版',
  'creator': 'Mark Lutz',
  'isbn': '4873113938'
}).store() # -> <riak.riak_object.RiakObject at 0x10a663898>

obj = bucket.new('4797372273', data={
  'title': 'たのしいRuby 第4版',
  'creator': '高橋 征義',
  'isbn': '4797372273'
}).store() # -> <riak.riak_object.RiakObject at 0x10a6b4470>

print(bucket.get('4873113938').data) # -> {'creator': 'Mark Lutz', 'title': '初めてのPython 第3版', 'isbn': '4873113938'}

bucket.get_keys() # -> ['4873113938', '4797372273']

print(bucket.multiget(['4873113938','4873113938'])) # -> [<riak.riak_object.RiakObject object at 0x10a6b45f8>, <riak.riak_object.RiakObject object at 0x10a787a90>]

bucket.delete('4873113938')
print(bucket.get('4873113938').data) # -> None

bucket.get_keys() # -> ['4797372273']

```

## カウンターを利用する

数値の増減を操作できる機能。標準であるのは助かります。
increment したあとに、store() しないといけないのは謎ですが...

```
$ riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}'
$ riak-admin bucket-type activate counters
$ ipython
import riak
from riak.datatypes import Counter
client = riak.RiakClient()
bucket = client.bucket_type('counters').bucket('manifestations')
counter = Counter(bucket, 'manifestations')
counter.value # -> 0
counter.increment()
counter.store()
counter.value # -> 1
```

# 全文検索機能(Yokozuna)を使う

## 準備

初期状態では、検索機能(search)は off になっているはずですが、一応確認しておきます。offであれば、設定を変更して、riakを再起動します。

```
$ riak config effective | grep "search ="
search = off
$ sudo sed -e 's/search = off/search = on/' -i.back /usr/local/Cellar/riak/2.0.4/libexec/etc/riak.conf
$ riak restart
ok
$ riak ping
pong
$ riak config effective | grep "search ="
search = on
```

## 設定

面倒ですが、設定します。

```
% wget https://raw.githubusercontent.com/basho/yokozuna/develop/priv/default_schema.xml
% ipython
import riak
client = riak.RiakClient()
f = open('default_schema.xml')
content = f.read()
f.close()
schema_name = '_yz_default'
client.create_search_schema(schema_name, content)
client.create_search_index('manifestation_index','_yz_default')
```

bucket-typeを作成します。

```
$ riak-admin bucket-type create manifestation_index '{"props":{"search_index":"manifestation_index"}}'
manifestation_index created
$ riak-admin bucket-type activate manifestation_index
manifestations has been activated
```

データ登録と検索をやってみます。

```
$ ipython
import riak
client = riak.RiakClient()
bucket = client.bucket_type('manifestation_index').bucket('books')

book = bucket.new('4621088629', {'title_s': '東洋天文学史', 'isbn_s': '4621088629', 'price_i': 1080, 'creator_s': '中村 士', 'date_of_publication_dts': '2014-10-25T00:00:00Z'})
book.store()

book = bucket.new('4864102538', {'title_s': '宇宙探査機', 'isbn_s': '4864102538', 'price_i': 4320, 'creator_s': 'フィリップ・セゲラ', 'date_of_publication_dts': '2013-08-06T00:00:00Z'})
book.store()

book = bucket.new('4422420046', {'title_s': '世界で一番美しい元素図鑑', 'isbn_s': '4422420046', 'price_i': 4104, 'creator_s': 'セオドア・グレイ', 'date_of_publication_dts': '2010-10-22T00:00:00Z'})
book.store()

book = bucket.new('4087472221', {'title_s': '神々の山嶺〈上〉', 'isbn_s': '4087472221', 'price_i': 788, 'creator_s': '夢枕 獏', 'date_of_publication_dts': '2000-08-01T00:00:00Z'})
book.store()

book = bucket.new('408747223X', {'title_s': '神々の山嶺〈下〉', 'isbn_s': '408747223X', 'price_i': 864, 'creator_s': '夢枕 獏', 'date_of_publication_dts': '2000-08-01T00:00:00Z'})
book.store()

book = bucket.new('4163537406', {'title_s': '敵対水域', 'isbn_s': '4163537406', 'price_i': 119, 'creator_s': 'ピーター ハクソーゼン', 'date_of_publication_dts': '1998-01-01T00:00:00Z'})
book.store()

bucket.get_keys() # -> ['4422420046',
 '4163537406',
 '4087472221',
 '408747223X',
 '4621088629',
 '4864102538']

results = client.fulltext_search('manifestation_index', 'title_s:神*')
for doc in results['docs']:
  bucket = client.bucket_type(doc['_yz_rt']).bucket(doc['_yz_rb']) 
  b = bucket.get(doc['_yz_rk']) 
  print(b.data)
  
# -> {'creator_s': '夢枕 獏', 'title_s': '神々の山嶺〈上〉', 'price_i': 788, 'date_of_publication_dts': '2000-08-01T00:00:00Z', 'isbn_s': '4087472221'}
{'creator_s': '夢枕 獏', 'title_s': '神々の山嶺〈下〉', 'price_i': 864, 'date_of_publication_dts': '2000-08-01T00:00:00Z', 'isbn_s': '408747223X'}

results = client.fulltext_search('manifestation_index', 'price_i:[* TO 1000]')
for doc in results['docs']:
  bucket = client.bucket_type(doc['_yz_rt']).bucket(doc['_yz_rb']) 
  b = bucket.get(doc['_yz_rk']) 
  print(b.data)
  
# -> {'creator_s': '夢枕 獏', 'title_s': '神々の山嶺〈上〉', 'price_i': 788, 'date_of_publication_dts': '2000-08-01T00:00:00Z', 'isbn_s': '4087472221'}
{'creator_s': '夢枕 獏', 'title_s': '神々の山嶺〈下〉', 'price_i': 864, 'date_of_publication_dts': '2000-08-01T00:00:00Z', 'isbn_s': '408747223X'}
{'creator_s': 'ピーター ハクソーゼン', 'title_s': '敵対水域', 'price_i': 119, 'date_of_publication_dts': '1998-01-01T00:00:00Z', 'isbn_s': '4163537406'}  
```

# 最後に

## ConflictError エラー

riak.riak_object.RiakObject であるインスタンス変数の siblings プロパティを使うとリストで複数件でてきます。この場合は、delete メソッドで削除してから再登録してみると良いです。
一括削除の方法があれば良いのですが...