Yura YuLife

ITエンジニアの覚え書き

Gorm で PostgreSQL の JSONB 型の key, value で絞り込む

この記事では、Golang の ORM である Gorm を使って、 PostgreSQL の JSONB 型の中身の key や value による絞り込みをかける方法を紹介しています。

動作環境

Gorm で PostgreSQL の JSONB を絞り込む

モデル

例えば、以下のような、JSON 型の任意のタグを持てるデータ型について考えます。

type TaggedData struct {
    ID        uint32         `gorm:"NOT NULL;primary_key" sql:"TYPE:serial"`
    Name      string         `json:"name" gorm:"NOT NULL"`
    Tags      postgres.Jsonb `json:"tags"`
}

ここに、3件のデータを登録します。

host := "localhost"
user := "postgres"
password := ""
db := "tagged"
client, err := gorm.Open("postgres", fmt.Sprintf(
    "host=%s user=%s password=%s dbname=%s sslmode=disable", host, user, password, db,
))
if err != nil {
    fmt.Printf("%v\n", err)
    return
}

// テーブルを作成
client.AutoMigrate(&TaggedData{})

// データを登録
client.Create(&TaggedData{Name: "data1",
    Tags: postgres.Jsonb{[]byte(`{"key1": "val1"}`)}})
client.Create(&TaggedData{Name: "data2",
    Tags: postgres.Jsonb{[]byte(`{"key1": "val1", "key2": "val2"}`)}})
client.Create(&TaggedData{Name: "data3",
    Tags: postgres.Jsonb{[]byte(`{"key1": "val3", "key2": "val2", "key3": {"key4": "val4"}}`)}})

JSONB の値で検索

例えば Tags"key1" == "val1" のデータを検索してみます。

data := []TaggedData{}
// JSONB の値で検索
res := client.Where("tags ->> 'key1' = ?", "val1").Find(&data)
if res.Error != nil {
    fmt.Printf("%v\n", res.Error)
    return
}
// 結果を表示
for _, d := range data {
    fmt.Println(d.Name)
}

結果は以下のようになります。

data1
data2

続いて、 "key2" == "val2" のデータを検索してみます。

res := client.Where("tags ->> 'key2' = ?", "val2").Find(&data)

結果は以下の通りで、data1 の Tags"key2" というフィールドを持っていませんが、エラーにはなりません。

data2
data3

入れ子になっているフィールドを検索することもできます。

// 入れ子のフィールドを検索
res := client.Where("tags #>> '{key3, key4}' = ?", "val4").Find(&data)

こちらも想定通り動きました。結果は以下。

data3

プログラム全体

package main

import (
    "fmt"

    "github.com/jinzhu/gorm"
    "github.com/jinzhu/gorm/dialects/postgres"
)

type TaggedData struct {
    ID   uint32         `gorm:"NOT NULL;primary_key" sql:"TYPE:serial"`
    Name string         `json:"name" gorm:"NOT NULL"`
    Tags postgres.Jsonb `json:"tags"`
}

func main() {
    host := "localhost"
    user := "postgres"
    password := ""
    db := "tagged"
    client, err := gorm.Open("postgres", fmt.Sprintf(
        "host=%s user=%s password=%s dbname=%s sslmode=disable", host, user, password, db,
    ))
    if err != nil {
        fmt.Printf("%v\n", err)
        return
    }

    client.AutoMigrate(&TaggedData{})
    client.Create(&TaggedData{Name: "data1",
        Tags: postgres.Jsonb{[]byte(`{"key1": "val1"}`)}})
    client.Create(&TaggedData{Name: "data2",
        Tags: postgres.Jsonb{[]byte(`{"key1": "val1", "key2": "val2"}`)}})
    client.Create(&TaggedData{Name: "data3",
        Tags: postgres.Jsonb{[]byte(`{"key1": "val3", "key2": "val2", "key3": {"key4": "val4"}}`)}})

    data := []TaggedData{}
    res := client.Where("tags ->> 'key1' = ?", "val1").Find(&data)
    if res.Error != nil {
        fmt.Printf("%v\n", res.Error)
        return
    }
    for _, d := range data {
        fmt.Println(d.Name)
    }

    data = []TaggedData{}
    res = client.Where("tags ->> 'key2' = ?", "val2").Find(&data)
    if res.Error != nil {
        fmt.Printf("%v\n", res.Error)
        return
    }
    for _, d := range data {
        fmt.Println(d.Name)
    }

    res = client.Where("tags #>> '{key3, key4}' = ?", "val4").Find(&data)
    if res.Error != nil {
        fmt.Printf("%v\n", res.Error)
        return
    }
    for _, d := range data {
        fmt.Println(d.Name)
    }

    if err := client.DropTable(&TaggedData{}); err != nil {
        fmt.Printf("%v\n", err)
        return
    }
}

参考URL

Debian Stretch に protobuf 3.6 をインストール

本記事では Debian 9 Stretch に protobuf 3.6 をインストールする方法を紹介しています。

C++ で gRPC/protobuf を利用するためには、ソースからコンパイルする必要があります。 deb パッケージ提供してほしい。。。

環境

  • Debian 9 Stretch
  • protobuf v3.6.0

インストール手順

protobuf/README.md at master · google/protobuf · GitHub を参考にインストールします。

依存パッケージのインストール

$ sudo apt-get install autoconf automake libtool curl make g++ unzip

protobuf のリポジトリをクローン

$ git clone https://github.com/google/protobuf.git
$ cd protobuf
$ git checkout v3.6.0

サブモジュールの更新

$ git submodule update --init --recursive
$ ./autogen.sh

コンパイル&インストール

$ ./configure
$ make  # 10分〜20分くらいかかります
$ make check  # 5分〜10分くらいかかります
$ sudo make install
$ sudo ldconfig

これで protoc コマンド等が利用できるようになります!

参考URL

Debian Stretch に OpenCV 3.4 をインストール

この記事では Debian Stretch に OpenCV 3系をインストールする手順を紹介しています。

環境

インストール手順

Releases - OpenCV library からインストールしたいバージョンの zip ファイルをダウンロードします。

もしくはコマンドラインからダウンロード。

$ wget https://github.com/opencv/opencv/archive/3.4.0.zip

zip ファイルを展開します。

$ unzip opencv-3.4.0.zip

make && make install します。

$ cd opencv-3.4.0
$ mkdir build && cd build
$ cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
$ make -j4  # -j4 の部分はCPU数に合わせて変更してください
$ sudo make install

これだけ。6コア12スレッドの Core i7 だと make にかかったのは 1〜2分でした、はやい。

参考URL

Debian Stretch で Planex GW-450S を使う

この記事では、Debian Stretch の PC で Planex GW-450S という USB 無線LAN 子機を使えるようにする方法を紹介します。

動作環境

  • Debian Stretch (kernel: 4.9.0-6-amd64)
  • Planex GW-450S

ドライバのインストール手順

以下の手順は対象の PC が有線 LAN 等でインターネットに繋がった状態で作業することを想定しています。

依存パッケージのインストール

$ sudo apt install linux-headers-$(uname -r)
# もしくは sudo apt install linux-headers-4.9.0-6-all のように明示的に指定

ドライバーのコンパイルとインストール

$ sudo apt install git
$ git clone https://github.com/abperiasamy/rtl8812AU_8821AU_linux.git
$ cd rtl8812AU_8821AU_linux/
$ make
$ sudo make install

ドライバーの有効化

$ sudo modprobe rtl8812au

上記を実行し、 iwconfig コマンドやネットワークの設定画面等で無線LANのモジュールが確認できるか、近くのアクセスポイントを検索できるか等を確認します。

上記の設定だけだと、起動時に毎回コマンドを打たないといけないので、/etc/modulesrtl8812au を追記することで、起動時にドライバが読み込まれるようにします。

# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.

rtl8812au

Wi-Fi のインターフェース名の固定

上記を実行して Wi-Fi に接続できた場合は以下の手順は不要です。

SSID のスキャンやパスワード入力は出来るのに、何故か接続できない場合があります。

iwconfig を実行した際に認識されるインターフェース名が wlan0 ではなく wl....... のような長い名前になっている場合は、以下の手順を実行します。

/etc/default/grubGRUB_CMDLINE_LINUX_DEFAULTnet.ifnames=0 を追記します。

# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
# 以下の行をコメントアウト
# GRUB_CMDLINE_LINUX_DEFAULT="quiet"
# 以下の行を追加
GRUB_CMDLINE_LINUX_DEFAULT="quiet net.ifnames=0"
GRUB_CMDLINE_LINUX=""

そして、以下のコマンドを実行後、PC を再起動します。

$ sudo update-grub

これで問題なく Wi-Fi に繋がるようになりました。

参考サイト

matplotlib でプロット上の点をドラッグする例

Python の matplotlib 上で、マーカーをドラッグ可能にするプログラムの参考例です。

f:id:yurayur:20170912000951g:plain

動作環境

実装例

github.com

使い方

  • プロット上で左クリックで点を追加
  • プロット上の点をドラッグで移動
  • プロット上の点を右クリックで削除

解説

そんなに難しいことはしておらず、matplotlib の canvas に対してマウスイベントを拾って、適切な処理を行うだけです。

  • マウスのクリックイベント時のコールバック _on_click で新しい点の追加、もしくは既存の点の選択・削除処理
  • マウス移動イベント時のコールバック _on_motion で選択中の点がある場合は移動処理
  • マウスリリースイベント時のコールバック _on_release で選択中の点がある場合は確定処理

ちょっとずるいのは、点のX, Y座標を整数に絞ることで、点の管理を簡略化しているところかも。。。

もっと良い実装があれば PR くださーい!

参考URL

rosbag から連番の画像ファイルを生成する

カメラ画像を録画した rosbag データから、連番の画像ファイルを生成する方法です。

画像ファイルを生成する

基本的には ja/rosbag/Tutorials/Exporting image and video data - ROS Wiki に載っている手順の通りに行うだけです。

launch ファイルの作成

以下のような launch ファイル(例: bag-to-image.launch)を作成します。

<launch>
  <!-- rosbag を再生するノード -->
  <node pkg="rosbag" type="play" name="rosbag" args="-d 2 rosbagまでのフルパス(例: /opt/ros/bag/your.bag)"/>
  <!-- カメラ映像から画像ファイルを生成するノード -->
  <node name="extract" pkg="image_view" type="extract_images" respawn="false" output="screen">
    <remap from="image" to="カメラ画像のトピック名(例: /camera/image_raw)"/>
    <param name="filename_format" value="画像ファイルを保存するフォルダとファイル名のフォーマット (例: /home/ros/Pictures/frame%04d.jpg)" />
  </node>
</launch>

launch ファイルの実行

上記の launch ファイルを実行します。

$ roslaunch ./bag-to-image.launch

実行が完了すると、指定したフォルダ(例: /home/ros/Pictures/)に frame0001.jpg のような連番の画像ファイルが生成されます。簡単ですね!

参考URL

BitBucket で Git LFS の転送に失敗する場合の対処法

動作環境

Git LFS でサイズの大きいファイルの転送に失敗する場合

Git LFS を用いて BitBucket にサイズの大きいファイルを push する場合、 timeout エラーが発生して失敗することがあります。

特に HTTPS ではなく SSH で接続している場合に、起きやすいようです。

何度 push しても失敗する場合は Bitbucket LFS Media Adapter を使うのが有効です。

Bitbucket LFS Media Adapter の導入方法

Bitbucket LFS Media Adapter のページから Linux 64-bit 用のファイルをダウンロードします。

ファイルを展開し、git-lfs-bitbucket-media-api の実行ファイルをどこか適当な場所に配置します。

# ファイルの解凍
$ unzip GitLfsBitbucketAdapter-linux-amd64-1.0.6.zip
# 例えば /usr/local/bin/ にコピーする
$ sudo cp GitLfsBitbucketAdapter-linux-amd64-1.0.6/git-lfs-bitbucket-media-api /usr/local/bin/

そして、 git 側の設定で BitBucket に Git LFS でファイルを転送する場合に Bitbucket LFS Media Adapter を利用するように設定します。

# 例えば /usr/local/bin/git-lfs-bitbucket-media-api に置いた場合
$ git config --global lfs.customtransfer.bitbucket-media-api.path /usr/local/bin/git-lfs-bitbucket-media-api

あとは通常通り、 git lfs track large-file.zip とか git push すれば、 Bitbucket LFS Media Adapter を使ってファイルを転送してくれます。

Bitbucket LFS Media Adapter の利点

公式ページによると Bitbucket LFS Media Adapter は以下のような特徴があるとのこと。

  • アップロード・ダウンロードを中断&再開できる
  • 大きなファイルを分割して転送
  • 並列で転送することで転送時間を短縮できる
  • ファイルに変更があった場合のみ再転送が行われる
  • Git コマンドに対して特に変更が必要ない

便利ですね!

参考URL