Flask Rest API with MongoDB

I found the great article, How to Build an API with Python and Flask. This post describes how to build REST API using Flask and MySQL. As I’d like to use MongoDB as a database. I’d like to explain how to create REST API using Flask and MongoDB in this post based on How to Build an API with Python and Flask.

1. Set up MongoDB
Please refer http://docs.mongodb.org/manual/installation/.

2. Import data into MongoDB
2.a. Download dataset from Download the raw text file of UFO sightings. This is TSV formated data.
2.b. This TSV file does not have header. So I manually added heder. See below.

$ head sightings.tsv
index	sighted_at	reported_at	location	shape	duration	description	latitude	longitude
0	19951009	19951009	Iowa City, IA			Man repts. witnessing "flash, followed by a classic UFO, w/ a tailfin at back." Red color on top half of tailfin. Became triangular.	41.6611277	-91.5301683

2.c. Also, as mongoimport command does not work well with double-quoted (“) string, I striped double-quotes from file with following command.

cat sightings.tsv | sed 's/"//g' > data.tsv

2.d. Following is command to import TSV formated data into MongoDB.

mongoimport --db ufo --collection ufo --type tsv --file data.tsv

2.e Make sure if data is loaded correctly.

$ mongo ufo
MongoDB shell version: 2.4.3
connecting to: ufo
> db.ufo.count()
60504
>

3. Install PyMongo, which is Python based driver to access MongoDB

$ pip install pymongo

4. Python codes with Flask and PyMongo
4.a Create connection to MongoDB using PyMongo
4.b toJson(data): To generate JSON from MongoDB objects,  use json_util (http://api.mongodb.org/python/1.7/api/pymongo/json_util.html).
4.c sightings(): PyMongo’s find() method returns Cursor (data is not yet loaded). So it is unable to directly serialize to JSON. So, it requires to read data into another object(s)
4.d sighting(sighting_id): To query by _id, use ObjectId. find_one() method returns object (not cursor).

from flask import Flask, request
import json
from bson import json_util
from bson.objectid import ObjectId
from pymongo import Connection

# Flask
app = Flask(__name__)

# MongoDB connection
connection = Connection('localhost', 27017)
db = connection.ufo
def toJson(data):
"""Convert Mongo object(s) to JSON"""
return json.dumps(data, default=json_util.default)

@app.route('/sightings/', methods=['GET'])
def sightings():
  """Return a list of all UFO sightings
  ex) GET /sightings/?limit=10&offset=20
  """
  if request.method == 'GET':
    lim = int(request.args.get('limit', 10))
    off = int(request.args.get('offset', 0))
    results = db['ufo'].find().skip(off).limit(lim)
    json_results = []
    for result in results:
      json_results.append(result)
    return toJson(json_results)

@app.route('/sightings/<sighting_id>', methods=['GET'])
def sighting(sighting_id):
  """Return specific UFO sighting
  ex) GET /sightings/123456
  """
  if request.method == 'GET':
    result = db['ufo'].find_one({'_id': ObjectId(sighting_id)})
    return toJson(result)

if __name__ == '__main__':
  app.run(debug=True)

6. How to test
Start flask dev server

python sightings.py

Test URLs

http://127.0.0.1:5000/sightings/?offset=10&limit=3
http://127.0.0.1:5000/sightings/517d625aee2f8817d3609574

Note: For Geo query with MongoDB, I will post how to do it with MongoDB as soon as I could figure out.

Advertisements

Measure temperature using Raspberry Pi with Temper USB Thermometer

Followings are notes how I set up USB Thermometer with Raspberry Pi

1. Hardware: USB Thermometer
I bought TEMPer USB Thermometer w/ Alerts.

2. temper – A command line sensor logger for Temper1 devices (C language)

Requirements (build-essential libusb-1.0.0 and libusb-dev)
    sudo apt-get install build-essential libusb-1.0.0 libusb-dev
    Note: Should install libusb-dev instead of libusb-x.x.x-dev

Source codes
    git clone https://github.com/bitplane/temper.git

Compile
    cd temper
    make

Usage
    sudo ./termper

Output
    28-Jan-2013 07:09,23.581625

temper-python – libusb/PyUSB-based driver to read TEMPer USB HID devices (USB ID 0c45:7401)

Requirements (libusb-1.0.0 and python-usb)
    sudo apt-get install libusb-1.0.0 python-usb

Source Codes
    git clone https://github.com/padelt/temper-python.git

Usage
    cd temper-python
    sudo python src/temper.py

Output
    Found 1 devices
    Device #0: 23.6°C 74.5°F

To allow non-root users access

sudo cp udev/99-tempsensor.rules /etc/udev/rules.d/
    Note: As default, root user has access to usb.

photo

AppEngineの書き込みオペレーションに付いて(前回からの宿題)

AppEngineのDataStoreでインデックスを無効にした場合、書き込みコストが下がるか調べてみました。

まず、全てのModelの全てのフィールドにてindexを無効にしました。

下記のコード参照
AppEngine DataStore Model which is disabled index

class Post(db.Model):
    id             = db.StringProperty(required=True, indexed=False)
    from_name      = db.StringProperty(required=True, indexed=False)
    from_id        = db.StringProperty(required=True, indexed=False)
    message        = db.TextProperty(required=False, indexed=False)
    type           = db.StringProperty(required=False, indexed=False)
    created_time   = db.StringProperty(required=False, indexed=False)

上記の設定にてAppEngine上のサービスを起動。

結果:

  1. 各フィールド毎のインデックスは作成されなくなった。よって書き込みオペレーションの回数が減り、コスト削減。
  2. しかし、DataStoreのエントリーの数と同数のインデックスが作成されている。よって、書き込みオペレーションの回数は書き込んだ(Put)エントリーの2倍発生。下記のイメージ参照。

考察:

キー(Key)でしかデータを参照(Lookup)しないので、なんとかインデックスを完全に無効にしたい。推測であるが、もしかしたら、このインデックスはキーでの参照用に作成されているのかも?自分のアプリのコストは、書き込みオペレーションからくるので、どうにかしたい。。。。。。

検索エンジン on AppEngine 開発の問題点

最近、Google AppEngine上で動作する、単純なFacebook個人コンテンツ検索エンジンを開発している。現在幾つかの問題点にぶつかっているので、その問題点を列挙しておこうと思います。

1. Facebookコンテンツの取得(クロール)に時間がかかる

Facebookのデータ取得にはFacebookのGraph APIを利用しています。自分のユーザー権限を利用して取得できるアップデート(ポスト)の数は約50,000件になります。1度のAPI呼び出しに25件取得出来るので、データを全て取得する為には、2,000回APIを呼び出す必要があります。さらに1度のAPI呼び出しに約数秒かかり、遅いケースでは5秒以上かかるケースもあります。(Slow facebook API via Python on Google App Engine (GAE))ユーザーがサインナップしてから、検索を始める事が出来るようになるまでに、1時間とか2時間とか待たなければならない事は避けなければなりません。コンテンツにプライオリティを付けて重要なものから取得するなどの工夫が必要です。

2. AppEngineのDB(DataStore)への書き込みが遅い

検索エンジンには、転置インデックスというデータ構造を用いています。転置インデックスはKey-Valueのデータ構造を用い、Keyにはキーワード(単語)、Valueにはキーワードを含むドキュメントのリストが保持されます。キーワードの種類はインデックスするドキュメントにもよりますが、ロングテイルとなります。さらに実装にも依存しますが、同じキーワードに対して何回かの更新がかかります。現在共有出来る数値はありませんが、大量の書き込みがある場合はAppEngineのDataStoreは遅いように感じます。

AppEngineのHigh Replication DataStore (HRD)は、信頼性を上げる為に書き込み速度を犠牲にしています。「What are the speed comparisons of NDB vs DB (on High Replication Datastore)? 」によると、HRDの書き込み速度は45ms、Master-Slaveの書き込みは20msかかるようです。

3. AppEngineのDB(DataStore)への書き込みコストが高い

自分のケースでは、アップデート(ポスト)の件数が、約50,000件、転置インデックスインデックスのデータ数が約25,000件あります。合計約75,000。これだけのデータを書き込む(更新も含む)のに、AppEngineのダッシュボードによると0.77M (77万)回の書き込みオペレーションが発生、1M の書き込みオペレーションのコストは$1なので、実際に77セント課金されています。プラス読み込みoperationにも課金され、読み込みには57セント課金されています。合計$1.24。1人分のインデックス作成(約180人の友人がいるケース)に$1は個人サービスとしては高すぎると感じる。

補足

あまりに書き込みオペレーションの回数がデータ数に対して大きいので、もう少し調べた結果、DataStoreがサポートするインデックの為に作られるデータの書き込みもカウントされているようです。自分のデータでは、74,608のデータに対し、977,995のインデックの為のデータが作成されています。上記のデータと一致しないのは、情報取得時のタイミング?インデックスを自動的に作成されないように、インデックスを無効にすることにより、書き込み速度の向上、および書き込みコストが下がるか調べようと考えています。

プロトタイプ

現在のバージョンは自分のアップデートしか検索できませんが、下記から試す事が出来ます。

https://locateweb.appspot.com

どこで働きたい?自分の会社(21%)

Techcrunchに、「スタートアップで働くなら、どこで働きたい?(The Startup You Want To Work At The Most Is Your Own)」という記事がアップされていました。このウエブベースの調査が記事になった時点で約5千人程、現在は6千人強が調査に参加しています。その内、約21%(現在は約23%)が自分のスタートアップで働きたいと答えています。この質問の仕方だったらメジャーなスタートアップが人気なんだろうなと思って結果を見たので、この結果に嬉しい驚きと、みんな起業家精神旺盛だなーという感想を持ち、ポジティブな気持ちにさせてもらいました。Go Enterpluners!! 二番目に人気なMilkと言うスタートアップ、調べてみたらKevin Ross(Diggのファウンダー)のステルスモードのスタートアップだそうです。これは、Kevin Ross効果というのがあって、彼が”Vote us up! http://t.co/3TxuVnj”とつぶやいた効果だそうです。ちなみにKevinには126万のフォロアーがいる。すごい。サンプル(調査に参加した人)に、偏りはあると思うし、フェースブックなどがスタートアップであるか疑問であるが、なかなか面白い調査結果でした。

実際のPollが行われているサイト =>
GoPollGo (If you could work for a startup, any startup, which one would it be?)

Phil Libin (Evernote CEO)のお話

一昨日、7月15日(金)のお話。今の上司が、スタンフォード大学で夏の間、週に一度教えていて、今週はEvernote(日本でかなり人気?)のCEOとNetflixのArchitectが、ゲストでトークしに来るから聞きに来ればと誘ってくれたので、金曜日の午後スタンフォードに行って来ました。EvernoteのCEOである、Philの話が興味深かったので、忘れないように、ここに書いておこうと思います。

いきなりPhilが話しだした事は、「起業家は大変だからやめとけ、殆ど成功しないし。自分はたまたま3つ目の会社(Evernote)が当たっただけ。」と言い始めたのです。技術系のクラスなのに、起業の話だし、「やめとけ」ってなによ?この後どう話が展開するのか興味が湧く。

次のスライで、Philは「起業したいだったら、世界を変えたいやつだけ起業しろ」と、「世界を変える」は言い換えると「Save Humanity(人間性を守る)」と言う事だと言っていました。

現在の環境は下記の5つの点で昔とは違うから、起業するには好条件がそろっている。

  • App Store (アップルのAppストアーなど、ディストリビューションの環境?)
  • Cloud Services (AmazonのEC2などの環境)
  • Open Source Infrastructure (MySQLなどのオープンソース)
  • Social Media (Twitterなどのメディア)
  • Freemium Economics (フリー経済)

これは、「Geek Meritocracy!」だと、日本語に直訳すると「ギーク能力主義社会」。勝手に意訳すると、「ギークが活躍できる社会」だと。

ビジネスの観点でも世の中は進化している:

  • 産業時代は、価値は時間と共に減って行く。
  • 情報時代は、価値は時間に関係なく変わらない。
  • クラウド時代は、価値は時間と共に上昇する。

上記の事をEvernoteのデータを使用し、説明してくれました。

  • Evernoteのベータ版を立ち上げた最初の1ヶ月に加入したユーザーは、最初の2ヶ月間でかなり減り、50%のユーザーが残ったそうです。しかし、そこで残った50%のユーザーは過去3年間使い続けているそうです。(フラット)価値が時間と共に変わらない話にリンクしているのだと思う。
  • 3年前に、Premium(有料)サービスを始めたが最初は、上記の最初の1ヶ月に加入したユーザーの内、1%がフリー版から有料版に以降してくれた。最初の1ヶ月に加入してくれた方からの売上は月$700(月6万円位)。しかし、過去3年間で、有料版に切り替えてくれた人が増え、最初の一ヶ月に加入したユーザーの23%が今では有料版を利用してくれている。その方々からの売上が月$10,000(月80万円)になったそうです。価値が時間と共に上昇する話にリンク。

毎月新規のユーザーが過去3年間加入してくれていて、最初の2ヶ月を経つと常連ユーザーとなり、その内の多くのユーザーが無料版から、有料版に切り替えてくれつづけていると。これが「Freemium Economics」だと。

その他には、Evernoteは自前でホスティングしているそうです。その方がEvernoteにとってはコストパフォーマンスが良いので。ウエブバージョンより、ネイティブアプリに力を入れているそうです。

上手く行かなかったら辞めるという選択肢

Off | 書評:「グローバルキャリア:ユニークな自分の見つけ方」を読みました。読んでいて昔を思い出したので、久しぶりのブログを書いてみようと思います。リンクしたブログの中に下記の一節を見つけました。

で、家庭教師などしながら7年フリーター的通訳・翻訳業を続け、バージニアにMBA留学、さらにハーバードの博士課程へ、と。博士課程進学時は、就職とハーバード行きを迷っていた石倉さんに、バージニアの教授が

「ボストンはいい所だから、気に入らなかったらやめればよい」

と。

ああ、これ、殺し文句なんですよねぇ。

「やってみて、いやだったらやめる」

これ、大事。

13年前にアメリカに行ける可能性が出来た時、友人の何人かに相談してみました。後で考えると、相談相手がなんと言をうと、アメリカに行ったと思うが。殆どの友人が、会社からの長期出張(3年間位)や、留学という形ではなかったので、「信頼できるか分からない環境(情報があまりなかったので)に行くのはやめた方が良いよ」とアドバイスをくれた。一人だけ、「上手く行かなかったら、3ヶ月間位英語学校でも行って、帰ってくれば」と言ってくれた。一人でも賛成してくれて嬉しかったのを思い出す。上手く行かなかったら辞めるという選択肢を選択しても、それ程カッコ悪く無いようにも思えて助かったようにも思う。

今日は7月16日なのですが、11日程前にアメリカに来てから13年経っていました。