2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【kintone】kintone REST API でレコードをたくさん(100件以上)登録する方法

Last updated at Posted at 2020-12-23

こんにちは!
今回は、他のアプリにたくさん準備したレコードを、1つのアプリに全部登録するということをやってみたいと思います。
kintone REST API では一度に一括登録できるレコード数の上限が100件です。
なので、何回も100件登録を繰り返すことになるかなぁ・・・。

今回作ってみるのは

アプリA:レコードたくさん
アプリB:レコードたくさん

アプリCに登録(たくさんのレコードを100件ずつ切り取りながら・・・)

という感じ。

ちょっと説明足らずになるかも知れませんが、やっていきたいと思います!

アプリの準備

(1)エクセルなどで2000件くらいレコードを作ります。
image.png
こんなデータを作りまして、
アップロードしてアプリA,アプリBを作成します。

(2)アプリAを再利用してレコードが空っぽのアプリCを作成します。
↓こんな感じ

アプリA アプリB アプリC
image.png image.png image.png

ちなみにどのアプリもフィールドはこんなかんじ

フィールド名 フィールドコード フィールド種類
数字 数値 数値
名前 文字列__1行_ 文字列(1行)

JavaScriptを書く

kintone UI Componentを使う

こちらから、
https://github.com/kintone/kintone-ui-component/tree/master/dist

kintone-ui-component.min.css
kintone-ui-component.min.js

をダウンロードしてアプリCにアップロードしておきます。
(spinnerという機能を使いたくて)

カーソルAPIを使って取ってくる

アプリA,アプリBからは公式のコードを使って取ってくるようにしましょう。
レコード一括取得の JavaScript コーディング例 - カーソル API を利用する方法

※イマドキなlet、constで書くES6なコードは、
kintone Advent Calendar 2020 8日目
@tarimo34 さんの記事カーソルAPIを使ってレコードを一括取得したい(シンプル風味のソースを添えて)
を是非ご覧くださいっ!

第1段階の準備としてはこんな感じ。
//ココに一覧表示後イベントのコードを書いていく
//ココにレコード登録用コードを書いていくのところにコードを書いていきます。

(()=>{

    'use strict';

    kintone.events.on(['app.record.index.show'],  event => {
    //ココに一覧表示後イベントのコードを書いていく
    });

    const putRecords = async (_params) =>{
    //ココにレコード登録用コードを書いていく
    }

//以下、公式サイト通りのソース
    // カーソルを作成する
    var postCursor = function(_params) {
        var MAX_READ_LIMIT = 500;
    
        var params = _params || {};
        var app = params.app || kintone.app.getId();
        var filterCond = params.filterCond;
        var sortConds = params.sortConds;
        var fields = params.fields;
    
        var conditions = [];
        if (filterCond) {
        conditions.push(filterCond);
        }
    
        var sortCondsAndLimit =
        (sortConds && sortConds.length > 0 ? ' order by ' + sortConds.join(', ') : '');
        var query = conditions.join(' and ') + sortCondsAndLimit;
        var body = {
        app: app,
        query: query,
        size: MAX_READ_LIMIT
        };
        if (fields && fields.length > 0) {
        body.fields = fields;
        }
    
        return kintone.api(kintone.api.url('/k/v1/records/cursor', true), 'POST', body).then(function(r) {
        return r.id;
        });
    };
    
    // 作成したカーソルからレコードを取得する
    var getRecordsByCursorId = function(_params) {
        var params = _params || {};
        var id = params.id;
    
        var data = params.data;
    
        if (!data) {
        data = {
            records: []
        };
        }
    
        var body = {
        id: id
        };

        return kintone.api(kintone.api.url('/k/v1/records/cursor', true), 'GET', body).then(function(r) {
        data.records = data.records.concat(r.records);
        if (r.next) {
            return getRecordsByCursorId({ id: id, data: data });
        }
        return data;
        });
    };
    
    /*
    * @param {Object} params
    *   - app {String}: アプリID(省略時は表示中アプリ)
    *   - filterCond {String}: 絞り込み条件
    *   - sortConds {Array}: ソート条件の配列
    *   - fields {Array}: 取得対象フィールドの配列
    * @return {Object} response
    *   - records {Array}: 取得レコードの配列
    */
    var getRecords =  function(_params) {
        return postCursor(_params).then(function(id) {
        return getRecordsByCursorId({ id: id });
        });
    };

})();

一覧表示イベント

一覧表示はこんな感じにします。
↓spinner(待ってる間にグルグル表示するやつ)についてはこちらをご参考に
「kintone UI Component」を使って簡単にkintoneライクなUIを設置する

//一覧表示後イベント
kintone.events.on(['app.record.index.show'],  event => {
    //メニュー横の
    const sp = kintone.app.getHeaderMenuSpaceElement()
    //ボタン追加
    const btn = document.createElement('button');
    btn.textContent='ボタン';
    sp.appendChild(btn);

    //sppiner追加
    const spinner = new kintoneUIComponent.Spinner();
    const spn = spinner.render();
    sp.appendChild(spn);

    //ボタンクリック
    btn.onclick = async () => {
        //sppiner(待ってる間グルグル表示するやつ)表示する
        spinner.show();

        //アプリA、アプリBからレコード全件取ってくる(公式のコード内の関数を使用)
        const appA = await getRecords({app:アプリAのID, fields:['文字列__1行_','数値']});
        const appB = await getRecords({app:アプリBのID, fields:['文字列__1行_','数値']});

        //アプリAとアプリBのレコードをつなげる
        const appAB = appA.records.concat(appB.records);
        //アプリCにレコードを登録する(次の節でつくる関数)
        await postRecords({app:アプリCのID, records:appAB, next:appAB.length>0}); 
        
        //sppiner隠す(表示更新するから要らないと思うけど)
        spinner.hide();

        //表示更新
        location.reload();
    }

});

アプリAとアプリBからレコードを全件取ってきて、くっつけて、アプリCに登録する・・・という流れです。
fields:['文字列__1行_','数値']とすることで、余計な「レコード番号」等を取ってこなくて済みます。


//アプリA、アプリBからレコード全件取ってくる(公式のコード内の関数を使用)
const appA = await getRecords({app:アプリAのID, fields:['文字列__1行_','数値']});
const appB = await getRecords({app:アプリBのID, fields:['文字列__1行_','数値']});

//アプリAとアプリBのレコードをつなげる
const appAB = appA.records.concat(appB.records);
//アプリCにレコードを登録する(次の節でつくる関数)
await postRecords({app:アプリCのID, records:appAB, next:appAB.length>0});

レコード登録(今回のメイン)

たくさんのレコードをどうやって登録しましょうかね?
というわけで、

postRecords関数として↓を考えてみました。


const postRecords = async (_params) =>{
        
    //_params.nextがfalseだったら終了
    if (!_params.next ){ return; }
    
    //今回POSTする100件のbody
    const body = {
        app: _params.app,
        records: _params.records.splice(0,100), //POSTする最大100件分だけ取り出す。
    };

    //次回以降分のパラメータ
    const next_params = {
        app: _params.app,
        records: _params.records, //残りのレコード
        next:(_params.records.length == 0) ? false: true //残りのレコードが0件ならfalse
    }

    return kintone.api(kintone.api.url('/k/v1/records', true), 'POST', body).then(function(r) {
        //next_params.nextがfalseだったらreturn;でいいやんと、投稿後に気づいた。
        return postRecords(next_params);
    });
}

引数の_paramsですが、
ドコから何を渡してやってきたかと言うと、この部分です。


//アプリCにレコードを登録する(次の節でつくる関数)
await postRecords({app:アプリCのID, records:appAB, next:appAB.length>0}); 

引数の中身はこんな感じ。
_params.app:アプリCのID
_params.records:登録するレコード(AとBあわせた4000件くらいのレコード)
_params.next:true or false(続きのレコードがあればtrue, 全部登録し終わったらfalse)

↓つづいてこちらbodyはレコードPOSTのためのパラメータ。

//今回POSTする100件のbody
const body = {
    app: _params.app,
    records: _params.records.splice(0,100), //POSTする最大100件分だけ取り出す。
};

kintone REST APIのPOSTではレコードは100件ずつまでしか一括登録できないので、
100件ずつ登録して、続きがあれば繰り返すようにしています。

_params.records.splice(0,100)

ここアプリABのレコードである_params.recordsの4000件の100件を取り出してbodyrecordsに渡しています。
_params.records自体は3900件に減ります。

続きまして、次回用のパラメータ(postRecords関数を再帰的に呼び出す際のパラメータ)です。


//次回以降分のパラメータ
const next_params = {
    app: _params.app,
    records: _params.records, //残りのレコード
    next:(_params.records.length == 0) ? false: true //残りのレコードが0件ならfalse、0件じゃなければtrue
}

そして最後の「登録!」の部分

return kintone.api(kintone.api.url('/k/v1/records', true), 'POST', body).then(function(r) {
    return postRecords(next_params);
});

1行目で100件に切り取ったレコード分のPOSTをして、メソッドチェーン。
もう一度自分(postRecords)を呼びます。
何だか不思議ですが、自分(postRecords)を次々に呼ぶことにより、
最初4000件ほどあったレコードが100件ずつ切り取られていって、最後0件になったときにはもうPOSTせずに終わる・・・。
というような処理を実現できます。(再帰的な処理といいます)

※処理を終わりたいときは自分を呼ぶ時のパラメータで、next_params.nextをfalseにしておけば、次のpostRecords関数内の最初でreturn;して、処理を終了!とすることができるのです。

//_params.nextがfalseだったら終了
if (!_params.next ){ return; }

動作確認

アプリAに2201件、アプリBにも2201件レコードがある状態で、アプリCで動かしてみます。
ノーカット版なのでちょっとspinner表示(グルグル)が長いよ!

231.gif

まとめ

たくさんのレコードから、どうやって100件ずつ登録したら良いかなぁと思いましたが、なんとなくうまくいきました!splice(0,100)でいい感じに取ってこれそうです。

※kintone JavaScript ClientのaddAllRecordsというメソッドを使うという方法もありますが、別の機会に書けたら良いなぁと思います。

また、いつものことながら、行数節約のため、エラー処理などは入れていません(><)
もっと良い方法があったり、この書き方だとこんなデメリットがあるよ~というのがあったら教えて下さいm(_ _)m

というわけで、今日はこのへんで~(´・ω・`)ノシノシ

追記

冷静にコードを読んでいたら・・・、nextなフラグなんていらなかったので、修正したものを貼り付けて終わりますm(_ _)m

kintone.events.on(['app.record.index.show'],  event => {
    const sp = kintone.app.getHeaderMenuSpaceElement()
    //ボタン追加
    const btn = document.createElement('button');
    btn.textContent='ボタン';
    sp.appendChild(btn);

    //sppiner追加
    const spinner = new kintoneUIComponent.Spinner();
    const spn = spinner.render();
    sp.appendChild(spn);

    btn.onclick = async () => {
        //sppiner表示
        spinner.show();
        const appA = await getRecords({app:アプリAのID, fields:['文字列__1行_','数値']});
        const appB = await getRecords({app:アプリBのID, fields:['文字列__1行_','数値']});
        const appAB = appA.records.concat(appB.records);

        //登録!
        await postRecords({app:アプリCのID, records:appAB});
        
        //sppiner隠す(表示更新するから要らないと思う)
        spinner.hide();

        //表示更新
        location.reload();
    }

});

const postRecords = async (_params) =>{
        
    //今回POSTする100件のbody
    const body = {
        app: _params.app,
        records: _params.records.splice(0,100), //POSTする最大100件分だけ取り出す。
    };

    //次回以降分
    const next_params = {
        app: _params.app,
        records: _params.records, //残りのレコード
    }

    return kintone.api(kintone.api.url('/k/v1/records', true), 'POST', body).then(function(r) {
        return _params.records.length > 0 && postRecords(next_params);
    });
}
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?