Help us understand the problem. What is going on with this article?

kintoneコマンドラインツールでUPSERTしてみた

More than 3 years have passed since last update.

前回の「kintoneコマンドラインツールでデータ登録してみた」では、kintoneの異なるアプリのデータを定期的に別のアプリにコピーする方法を紹介しました。今回はコピー(追加)ではなく、UPSERT(キーとなる値があったら更新、なかったら追加)する方法を紹介したいと思います。

イメージ

20160505-1-37_No-00.png

kintoneにはUPSERTするAPIがないのですが、2016年5月にアップデートされた kintone API(レコード番号以外のキー項目でレコードの更新)を利用して次のようにINSERTとUPDATEを使い分けることにしました。

INSERT
アプリB(コピー先)の最大KeyCode(アプリAのレコードID)より大きいレコードIDを持ったアプリAのデータを抽出してアプリBに登録する。
※アプリAからアプリBにコピーする想定で、他のツールや人の操作によるレコード追加は想定していません。

UPDATE
アプリB(コピー先)のキー日時(レコードごとの処理した日時)の最大が、アプリAの更新日時以下のレコードIDを持ったアプリAのデータを抽出してアプリBに更新する。
※INSERTの後にUPDATEするため、INSERTするデータがUPDATEするデータにも含まれますが、kintone APIでは変更のないデータは更新しない仕様です。

kintoneコマンドラインツールを用意する

kintoneコマンドラインツール」を使います。このツールを使った過去の記事を紹介します。

2つのアプリを用意する

異なるドメインでkintoneアプリをそれぞれ 1つずつ用意します。
※同じドメイン内の異なるアプリ間でも動作します。
kintoneの環境を持っていない!という方、cybozu.com developer networkで無償の開発者ライセンスを取得しておきましょう。

アプリの構成

アプリA

フィールドコード フィールドタイプ
TitleA 文字列(1行)
NumberA 数値
UpdateDatetime 更新日時(組み込み)

アプリB

フィールドコード フィールドタイプ 備考
KeyCode 数値 重複なし(コピー元のレコードIDを保存)
KeyUpdateDatetime 日時 処理した日時を保存
TitleB 文字列(1行)
NumberB 数値

レコード登録画面

(フィールド名とフィールドコードを同じにしています)
アプリA

img_20160203095822.png

アプリB

20160505-23-19_No-01.png

kintoneコマンドラインツールに必要な情報

アプリA

パラメータ ここで実験する値
-a アプリID 85 ※IDはURLに表示されています。
-d サブドメイン名 sample1.cybozu.com ※サブドメイン名は各自異なります。
-t APIトークン De5Xb3TM1TDccdqEM20eP1dpesjuBLckOx6AyM4n ※アプリの管理画面から発行できます。

アプリB

パラメータ ここで実験する値
-a アプリID 822 ※IDはURLに表示されています。
-d サブドメイン名 sample2.cybozu.com ※サブドメイン名は各自異なります。
-t APIトークン BoNFRmX0oDoKQMSrTiZC7hbEmb9QaZa1vYVfnyD4 ※アプリの管理画面から発行できます。

それぞれの値は環境によって異なります。

いよいよ本題!シェルスクリプトを実行してみよう

Linux環境の場合、次のシェルスクリプトをUTF-8で保存しましょう。
ここでのファイル名は「upsert.sh」にしておきます。

次の処理をシェルスクリプトで実現してみます。
1. アプリB(コピー先)から最大KeyCode(アプリAのレコードID)を取得する
2. 最大KeyCodeより大きいレコードIDを持ったアプリAのデータを抽出してアプリBに登録する
3. アプリB(コピー先)のキー日時(レコードごとの処理した日時)の最大を取得する
4. その最大キー日時がアプリAの更新日時以下のレコードIDを持ったアプリAのデータを抽出してアプリBに更新する

説明

--- self setting ---で囲まれた部分のみ変更しましょう。

  • ### APP_A内の変数にアプリAの情報を設定
  • ### APP_B内の変数にアプリBの情報を設定
  • ### Mapping custom 内の変数にコピー元のフィールド:コピー先のフィールドを設定
    • TitleAからTitleBにコピーする際には、TitleA:TitleB と書きます
    • $idなど「$」を使う際にはエスケープしましょう
    • コピーするフィールドに応じて増やしましょう

プログラム

upsert.sh
#!/bin/bash

# --------------- self setting ------------------
### character encoding
OUTPUT_TYPE="sjis"
#OUTPUT_TYPE="utf-8"

### APP_A
APP_ID_A="85"
SUB_DOMAIN_A="sample1"
API_TOKEN_A="De5Xb3TM1TDccdqEM20eP1dpesjuBLckOx6AyM4n"
KEY_DATETIME_A="UpdateDatetime"

### APP_B
APP_ID_B="822"
SUB_DOMAIN_B="sample2"
API_TOKEN_B="BoNFRmX0oDoKQMSrTiZC7hbEmb9QaZa1vYVfnyD4"
UNIQUE_B="KeyCode"
KEY_DATETIME_B="KeyUpdateDatetime"
QUERY_B_INSERT="$UNIQUE_B != \"\" order by $UNIQUE_B desc limit 1"
QUERY_B_UPDATE="$UNIQUE_B != \"\" order by $KEY_DATETIME_B desc limit 1"

# Mapping
declare -a ARR_MAPPING=()
# Mapping define
ARR_MAPPING+=("\$id:KeyCode")
ARR_MAPPING+=("UpdateDatetime:KeyUpdateDatetime")
### Mapping custom
ARR_MAPPING+=("TitleA:TitleB") #sample:\$id,Title,Number,...
ARR_MAPPING+=("NumberA:NumberB")
# --------------- self setting ------------------

# Set cli-kintone files
CLI_FILES="cli-kintone"
BASE_DIR=$(cd $(dirname $0); pwd)
if [ ! -f "$BASE_DIR/$CLI_FILES" ]; then
    echo "Not found $CLI_FILES files."
    exit 1
fi
# Create files dirctory
OUTPUT_DIR="files"
BASE_DIR=$(cd $(dirname $0); pwd)
if [ ! -e "$BASE_DIR/$OUTPUT_DIR" ]; then
    mkdir -p "$BASE_DIR/$OUTPUT_DIR"
fi

# Set header fields
declare -a ARR_FIELDS_A=()
declare -a ARR_FIELDS_B=()
for i in "${ARR_MAPPING[@]}"
do
   ARR_FIELDS_A+=(`echo $i | cut -d":" -f1 | cut -d":" -f1`)
   strB=\"`echo $i | cut -d":" -f2 | cut -d":" -f1`\"
   ARR_FIELDS_B+=($strB)
done
OUTPUT_FIELDS="$(IFS=,; echo "${ARR_FIELDS_A[*]}")"
INSERT_FIELDS="$(IFS=,; echo "${ARR_FIELDS_B[*]}")"
UPDATE_FIELDS="$(IFS=,; echo "${ARR_FIELDS_B[*]}")"
# Set update field flg
UPDATE_FIELDS=`echo $UPDATE_FIELDS | sed -e "s/\"$UNIQUE_B\"/\"*$UNIQUE_B\"/"`

# Set datetime file
DATETIME=`date +%Y%m%d%H%M%S.%2N`
OUTPUT_FILE="output$DATETIME.csv"
OUTPUT_PATH=$BASE_DIR/$OUTPUT_FILE

# ---Get Update datetime Script----------------------
# Get max data
MAX_LIST=(`./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -c "$KEY_DATETIME_B" -q "$QUERY_B_UPDATE"`)
MAX_DATA=$(echo ${MAX_LIST[1]} | sed s/\"//g)
if [ -n "$MAX_DATA" ] ; then
    QUERY_A_DATE="$KEY_DATETIME_A > ${MAX_LIST[1]}"
fi
echo $QUERY_A_DATE

# ---Insert Script------------------------------------
# Get max data
MAX_LIST=(`./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -c "$UNIQUE_B" -q "$QUERY_B_INSERT"`)
MAX_DATA=${MAX_LIST[1]}
if [ -z "$MAX_DATA" ] ; then
    MAX_DATA="0"
    echo "Changed ID."
fi
MAX_DATA=`echo $MAX_DATA | sed s/\"//g`
QUERY_A="\$id > $MAX_DATA"
echo $QUERY_A

# Excute output file
./$CLI_FILES -a $APP_ID_A -d $SUB_DOMAIN_A -t $API_TOKEN_A -e $OUTPUT_TYPE -c "$OUTPUT_FIELDS" -q "$QUERY_A" >> $OUTPUT_FILE

# Change csv header
INSERT_FILE="Insert$DATETIME.csv"
find . -name "$OUTPUT_FILE" | sed -i -e "1,1d" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | sed -i -e "1i $INSERT_FIELDS" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | mv "$OUTPUT_FILE" "$INSERT_FILE"
echo "Insert file: $INSERT_FILE"

# Excute import file
./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -f "$INSERT_FILE"
mv "$INSERT_FILE" "$OUTPUT_DIR"
echo "Finished insert data."

# ---Update Script------------------------------------
# Excute output file
./$CLI_FILES -a $APP_ID_A -d $SUB_DOMAIN_A -t $API_TOKEN_A -e $OUTPUT_TYPE -c "$OUTPUT_FIELDS" -q "$QUERY_A_DATE" >> $OUTPUT_FILE

# Change csv header
UPDATE_FILE="Update$DATETIME.csv"
find . -name "$OUTPUT_FILE" | sed -i -e "1,1d" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | sed -i -e "1i $UPDATE_FIELDS" $OUTPUT_FILE
find . -name "$OUTPUT_FILE" | mv "$OUTPUT_FILE" "$UPDATE_FILE"
echo "Update file: $UPDATE_FILE"

# Excute import file
./$CLI_FILES -a $APP_ID_B -d $SUB_DOMAIN_B -t $API_TOKEN_B -e $OUTPUT_TYPE -f "$UPDATE_FILE"
mv "$UPDATE_FILE" "$OUTPUT_DIR"
echo "Finished update data."

シェルスクリプトを実行してみる

$ ./upsert.sh

できた!!
処理したファイルは、filesフォルダにそれぞれ Insert~.csv、Update~.csv というファイルで、保存しています。
20160505-23-12_No-00.png

さいごに

いかがでしたでしょうか。kintoneコマンドラインツールとシェルスクリプトを使うと 定期的に、別会社(ドメイン)へデータを同期して活用 することができますね。お試しください。

ushiron
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした