コンパイラかく語りき

import { Fun } from 'programming'

The git source `hoge` uses the `git` protocol, which transmits data without encryption...の対処法

問題

Gemfile内でgemに対して、:github プロパティを使っていた所、以下のようなエラーが。

The git source `hoge` uses the `git` protocol, 
which transmits data without encryption. Disable
this warning with `bundle config git.allow_insecure 
true`, or switch to the `https` protocol to keep 
your data secure.

解決

以下のコマンドを発行する。

$ bundle config --global github.https true

他のプロジェクトでも適用されたいので、--global フラグをつけました。

あるいは、~/.bundle/config に以下を追記。

BUNDLE_GITHUB__HTTPS: "true"

参考

https://github.com/bundler/bundler/issues/4978#issuecomment-260141406:embed:cite

ReqlDriverError: Could not connect to localhost:28015. → RethinkDB自体が起動していませんでした

RethinkDBRethinkDBのnodeドライバに関して、ちょっとハマったのでメモ。

問題

ローカルのコンソールからRethinkDBのドライバを起動しようとする。

// server.js
r = require('rethinkdb')

r.connect({host: 'localhost', port: 28015}...
$ node server.js

すると、

ReqlDriverError: Could not connect to localhost:28015.

というエラーが出てしまって、コネクトできない。

ちなみに、ReQL error typesというページによると、ReqlDriverErrorはドライバのバグかも。とのこと。

原因

単純にRethinkDB自体が起動していなかった。

$ rethinkdb 

コンソールにこんな1文が出る。

Listening for client driver connections on port 28015

そりゃあ、DBが起動してないと、ドライバも動かないよなあ…というサーバサイドに弱いエンジニアの気付きでした。

RethinkDBで「DBが存在して無ければ作る」「テーブルが存在して無ければ作る」

※ Nodeで書いてますが、ReQL自体は言語フリーなので他言語の方にも参考にはなると思います。

初期化時にありがちな、DBとテーブルを有無をチェックしながら作成するという処理。

DB作成

// 無ければDBを作成
r.dbList().contains('messages').do((containsDb) => {
  return r.branch(
    containsDb,
    {created: 0},
    r.dbCreate('messages')
  )
}).run(conn)

テーブル作成

// 無ければTableを作成
r.tableList().contains('messages').do((containsTable) => {
  return r.branch(
    containsTable,
    {created: 0},
    r.tableCreate('messages')
  )
}).run(conn)

リファレンス

dbList, tableList

文字通り、リストを取得します。返り値はArrayです。

dblist - ReQL command tablelist - ReQL command

contains

Sequence内に引数で渡した値がある場合はTrueを返します。あるいは、Boolean判定関数を引数に渡すことも可能です。 ※Sequence = Array, Stream, Tablesのようなリスト型

contains - ReQL command

do

引数に関数を設定すると、その関数を実行します。その際、直前のReQLの結果を引き継ぎ、関数の引数として渡すことができます。

do - ReQL command

branch

第一引数にBooleanを取り、trueなら第二引数の処理を、falseなら第三引数の処理を行います。if-then-else的な処理ができます。 引数はいくらでも設定でき、if-then-elseif-then-elseif-then…とつなぐこともできるようです。

branch - ReQL command

おまけ

ちなみに、上記の処理を、接続から起動までチェーンにした1例がこちら。

const async = require('async')

// 接続
const connectRethinkDB = (cb) => {
  r.connect({
    host: 'localhost',
    port: 28015,
    authKey: '',
    db: 'trialDB'
  }, cb)
}

// 無ければDBを作成
const createDB = (conn, cb) => {
  r.dbList().contains('messages').do((containsDb) => {
    return r.branch(
      containsDb,
      {created: 0},
      r.dbCreate('messages')
    )
  }).run(conn, (err) => {
    cb(err, conn)
  })
}

// 無ければTableを作成
const createTable = (conn, cb) => {
  r.tableList().contains('messages').do((containsTable) => {
    return r.branch(
      containsTable,
      {created: 0},
      r.tableCreate('messages')
    )
  }).run(conn, (err) => {
    cb(err, conn)
  })
}

// 複数処理を連続的に実行
async.waterfall([
  connectRethinkDB,
  createDB,
  createTable
], (err, conn) => {
  if(err) {
    console.log(err)
    process.exit(1)
    return
  }
  startExpress()
})

こちらのコードをガッツリお手本にしました。 https://github.com/rethinkdb/rethinkdb-example-nodejs/blob/master/todo-angular-express/app.js

node.jsでRethinkDBを扱うためのORM "Thinky" を試してみた

Thinkyというnode.jsでRethinkDBを扱うためのORMを試してみました。

RethiknDB: https://www.rethinkdb.com/ Thinky: https://thinky.io/

参考ページ

公式のドキュメントのクイックスタートというページを参考にしてみました。

https://thinky.io/documentation/

以下で示すコードは、その写経と若干の補足コメントです。

コード

パッケージインポート

// Thinkyを実行すると、RethinkDBの接続プールが作成されます。
const thinky = require('thinky')() 
// コンソール出力:"Creating a pool connected to localhost:28015"

// typeはスキーマ定義の型を指定するために使います。
const type = thinky.type

RethinkDBへのドライバも利用可

// thinkyはRethinkDBへのドライバの参照を持っています。
const r = v.r
r.now().run().then((time) => {
  return time // 2016-09-19T05:22:53.098Z (例)
})

モデルの作成

// モデルの作成:thinky.createModel(tableName, schema, options)
const Post = thinky.createModel("Post", {
  id: type.string(),
  title: type.string(),
  content: type.string(),
  idAuthor: type.string()
})

const Author = thinky.createModel("Author", {
  id: type.string(),
  name: type.string()
})
// 有効なスキーマについては -> https://thinky.io/documentation/schemas/

モデルのジョイン

// モデルのジョイン: belongsTo(OtherModel, fieldName, leftKey, rightKey[, options]))
Post.belongsTo(Author, "author", "idAuthor", "id")

ドキュメントの作成

// モデルを初期化して、ドキュメントを作成します。
const post = new Post({
  title: "Hello world",
  content: "This is an example"
})
const author = new Author({
  name: "Michel"
})

// ドキュメントをジョインさせます。
post.author = author

ドキュメントの保存

// ドキュメントの保存(ジョイン含む): SaveAll([modelToSave], [callback])
post.saveAll({author: true}).then((result) => {
  //
})
// 外部キーが自動的にセットされます。

データの取得

// PostをそのAuthorとともに取得します。
Post.get('d2b2ee18-c8d2-45a0-9e89-35a7e026aea5') // Idは適当
  .getJoin({author: true}).then((post) => {
      // 取得結果↓↓
      // model {
      //  author: model { id: '97f5faed-4c7a-449c-8570-4aca9a273bf9', name: 'Michel' },
      //  content: 'This is an example',
      //  id: 'd2b2ee18-c8d2-45a0-9e89-35a7e026aea5',
      //  idAuthor: '97f5faed-4c7a-449c-8570-4aca9a273bf9',
      //  title: 'Hello world'
      // }
  
    // Authorの名前を変更します。
    post.author.name = 'John'
    post.saveAll({author: true})
})

ファーストインプレッション

いいですね。 スキーマの型定義や、シンプルなデータのリトリーブが嬉しいです。

次回は、リレーション周りをもう少し詳しく見てみます。

おまけ

Thinkyのキャラクターがゆるくて可愛いです。

Screen Shot 2016-09-19 at 3.02.47 PM.png

RethinkDBのキャラもゆるいですよね。和みます。

Thinkyを使ったRethinkDB REST API

メモ。 参考にした動画:A Simple REST API with Express, RethinkDB, and Thinky

下準備

セットアップ

thinkyをrequireする時に、セットアップ情報をオブジェクトで渡して実行する。

const thinky = require('thinky')({
  host: 'localhost',
  port: 28015,
  db: 'People'
})

RethinkDBドライバへの参照

thinkyにはrethinkDBへの参照が紐付いているので、変数格納しておく。

const r = thinky.r

モデルの作成

thinky.createModel(‘TableName’, {schema})

const People = thinky.createModel('People', {
  firstName: String,
  lastName: String,
  coolnessFactor: Number,
  date: { _type: Date, default: r.now() }
})

thinky/#createmodel

インデックス作成

dateカラムがインデックスに適しているかどうかはさておき…

People.ensureIndex('date')

model/#ensureindex

REST API

get

const get = function (req, res) {
  People.get(req.params.id).run().then((person) => {
    res.json(person)
  }).error((err) => {
    res.json({ message: err })
  })
}

getAll

const getAll = function (req, res) {
  People.orderBy({ index: r.desc('date') }).run().then((people) => {
    res.json(people)
  }).error((err) => {
    res.json({ message: err })
  })
}

add

const add = function (req, res) {
  var person = new People(req.body)
  person.save().then((result) => {
    res.json(result)
  }).error((err) => {
    res.json({ message: err })
  })
}

delete

const delete = function (req, res) {
  People.get(req.params.id).run().then((person) => {
    person.delete().then((result) => {
      res.json(result)
    }).error((err) => {
      res.json({ message: err })
    })
  }).error((err) => {
    res.json({ message: err })
  })
}

update

const update = function (req, res) {
  People.get(req.params.id).run().then((person) => {
    if(req.body.firstName) {
      person.firstName = req.body.firstName
    }
  
    if(req.body.lastName) {
      person.lastName = req.body.lastName
    }
    
    if(req.body.coolnessFactor) {
      person.coolnessFactor = parseInt(req.body.coolnessFactor)
    }
    person.data = r.now()
    
    person.save().then((result) => {
      res.json(result)
    }).error((err) => {
      res.json({ message: err})
    })
  })
}

あとは、expressなりなんなりでrouteしてあげれば動きます。

RethinkDBでGeoJSON操作

RethinkDBでGeoJSONをゴニョゴニョする方法について。 サンプルコードはJavaScriptで書いてありますが、Ruby, Python, JavaでもOKです。

GeoJSONとは

JSONをベースとした、位置情報データフォーマットです。

ReQL - geojson

RethinkDBのクエリであるReQLには、geojsonというクエリがあります。

ReQL - geojson

geojsonを使うと、GeoJSONをRethinkDBのGeometry型データに変換することができます。 Geometry型については、以下に公式サイトのリンクを貼ります。

ReQL data types - Geometry data types

サポート対象

GeoJSONはいくつかの型を持ちますが、RethinkDBがサポートするのは Point, LineString, and Polygon のみです。 MultiPoint, MultiLineString, and MultiPolygonはサポートされていませんが、array型を用いて複数のPoint, LineString, Polygonを格納することはできるようです。

また、座標に関して、緯度経度の座標はサポートされていますが、デカルト座標や高度座標を持つGeoJSONをRethinkDBは受け付けません。

コードサンプル

var geoJson = {
    'type': 'Point',
    'coordinates': [ -122.423246, 37.779388 ]
};
r.table('geo').insert({
    id: 'sfo',
    name: 'San Francisco',
    location: r.geojson(geoJson)
}).run(conn, callback);

ReQL - toGeojson

一方、Geometry型をGeoJSONに変換するには、toGeojsonクエリを使います。

ReQL - toGeojson

コードサンプル

r.table('geo').get('sfo')('location').toGeojson.run(conn, callback);
// result passed to callback
{
    'type': 'Point',
    'coordinates': [ -122.423246, 37.779388 ]
}