質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

【laravel・RDS】DBに保存されている配列がnullになり取得できない(取得するjson配列が途中で切れる)

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 4,816

score 74

わからないこと

配列でIDを取得してそれをもとに一覧ページを表示しています。
しかし、該当箇所でnullとなり、dd()で確認したところ、配列が途中までしか表示されておりませんでした。
すべての表示結果でそうなるわけではなく、一定数以上の件数でのみそうなっており、データ量が影響しているのではと考えております。
どうしてそうなるのかがわかっておらず、したがってどう対処すればよいのかわかっておりません。
ご教授いただければ幸いです。

※変数に格納してあるデータが1Mちょうどで収まっているのを確認しました。つまり1Mを超えたデータについては切られている状況になります。
そのため、json配列の「 ] 」も切られているので、nullが取得されていると考えています。

コード

protected $casts = array(
    'result' => 'array',
    'query' => 'array',
);
$data = Search::select('result', 'query')->where('id', 5)->first();
$all = $data ? $data['result'] : [];


上記の「$data['result']」にddをかけるとnullが取得されます。
※Searchはsearchesというテーブルを指します。
そのうちの「result」カラムが非常に肥大している状況です。
通常ですと、「result:[1, 2, 3, ..., 555555]」のように表示されるのですが、「 result:[1, 2, 3, ..., 555554, 」と表示されています。

テーブル構造

Name: searches
Engine: InnoDB
Version: 10
Row_format: Dynamic
Rows: 83424
Avg_row_length: 37212
Data_length: 3104407552
Max_data_length: 0
Index_length: 0
Data_free: 7340032
Auto_increment: 89328
Create_time: 2017-07-11 23:28:01
Update_time: 2017-10-04 06:19:23
Check_time: NULL
Collation: utf8_unicode_ci
Checksum: NULL
Create_options:
Comment:

よろしくお願いいたします。

追加事項

モデル

protected $table = 'searches';
protected $casts = array(
    'id' => 'integer',
    'result' => 'array',
    'query' => 'array',
);

テーブル構造

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `result` longtext COLLATE utf8_unicode_ci NOT NULL,
  `query` longtext COLLATE utf8_unicode_ci NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=89390 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_c

queryの中身

array:3 [▼
  "pha" => array:18 [▶]
  "q" => ""
  "pri" => []
]

ddの結果

Search {#987 ▼
  #table: "searches"
  #casts: array:3 [▶]
  #connection: null
  #primaryKey: "id"
  #perPage: 15
  +incrementing: true
  +timestamps: true
  #attributes: array:2 [▼
    "result" => "[142264,142265,142266,142263,142262,142261,142260,...]
 "query" => "{"phase":[1,2,3,5,6,8,12,32,10,11,28,29,27,26,25,31,24,0],"q":"","privacy":[]}"
]

追記

$search = (int)request()->input('search');
        if ($search) {
            $data = Search::select('result', 'query')->where('id', $search)->first();
            dd(strlen($data->getAttributeFromArray('result')));
            $all = $data ? $data['result'] : [];
            $q   = isset($data['query']['q']) ? $data['query']['q'] : '';
            $phaseIds[0] = isset($data['query']['phase']) ? $data['query']['phase'] : [];
            $privacy = isset($data['query']['privacy']) ? $data['query']['privacy'] : [];
        } else {
            $phaseIds[0] = array_unique(array_map('intval', (array)request()->input('phase')));
            if ($phaseIds[0]) {
            } elseif (request()->ajax()) {
                unset($phaseIds[0]);
                unset($phaseIds[1]);
                $phaseIds = array_values($phaseIds);
            } else {
                $phaseIds[0] = array_values(array_diff(last($phaseIds),(array)Option::getArray(Option::JPHASE_FINISH)));
            }
            // 検索フォームでの検索値の取得
            $q = (string)request()->input('q');
            // 各種ポータルサイトへの公開/非公開設定の取得
            $privacy = array_unique((array)request()->input('privacy'));
            // 全案件のidを取得
            $all = self::commitSearch($q, $phaseIds[0], $privacy);
            // インスタンスの生成
            $search = new Search();
            $search['result'] = $all;
            $search['query']  = ['phase'=>$phaseIds[0],'q'=>$q,'privacy'=>$privacy];
            $search->save();
            $search = $search['id'];
        }
public static function commitSearch($q, $phaseIds, $privacy, $ids = NULL) {
        $fulltext = Fulltext::where('type', 'job')->orderBy('time','DESC');
        if ($ids) $fulltext->whereIn('main_id', $ids);
        if ($phaseIds) $fulltext->whereIn('phase_id', $phaseIds);
        if ($q !== '') $fulltext->whereIndex($q);
        if (!$privacy || count($privacy) === 3) {
            return array_map('intval', $fulltext->lists('main_id')->toArray());
        } elseif (!($ids = array_map('intval', Job::whereIn('privacy', $privacy)->lists('id')->toArray()))) {
            return [];
        } elseif (count($ids) <= 10000) {
            $fulltext->whereIn('main_id', $ids);
            return array_map('intval', $fulltext->lists('main_id')->toArray());
        } else {
            return array_values(array_intersect(array_map('intval', $fulltext->lists('main_id')->toArray()), $ids));
        }
    }

追記②

「strlen(getAttribute['result'])」:1041645

「substr(getAttribute['result'], -10)」:"7392,7372,"

「LENGTH(result)」:1053784

「RIGHT(result, 10)」:1576,3449]

現状報告

調べたところ、1MBを境目にエラーが発生しているようです。
そのため、phpとmysqlの設定をあらっている現状になります。

バージョン

php7
laravel5.1
データベース:RDS(mysql5.7)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • masaya_ohashi

    2017/10/05 15:09

    すみません、よく調べてみたところ、getAttributeFromArrayはprotectedなメソッドであったため、getAttributes()['result']で代用してください。

    キャンセル

  • youthK

    2017/10/05 15:14

    DBでは、Length:1051656、Cnt:157323.0000、PHPでは1048576と表示されました。ご推測の通り件数がずれていると思われます

    キャンセル

  • masaya_ohashi

    2017/10/05 15:19

    私の回答を修正したので、修正後のコードで再度試してみてください。

    キャンセル

回答 3

checkベストアンサー

+6

これまで切り分け頂いている結果から、おそらく下記が該当するのではないかと思います。

LaravelとMySQLで1048576の罠

PDOでMySQLに接続している場合、PDO::MYSQL_ATTR_MAX_BUFFER_SIZEのデフォルト値が1MiBとなっており、それ以上のサイズの列を取得することができないという事象です。

対処としては、上記サイトにもあるように、app/config/database.phpの options で十分大きなサイズのバッファを確保するように指定することで対応できるのではないかと思います。
上記サイトでは16MBに設定しているようです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/06 02:30

    夜分遅くにありがとうございます!!
    上記の設定にて無事表示させることができました。
    ありがとうございました

    キャンセル

  • 2017/10/06 09:04

    すごい!モヤモヤが取れました!

    キャンセル

+4

Laravelの場合、クエリビルダーやEloquentクラスで結果を取得した場合、通常は配列ではなくオブジェクトになるのではないかと思います。

参考:
https://readouble.com/laravel/5.1/ja/queries.html
の「テーブルから1カラム/1レコード取得」項

そのため、
$data['result']

$data->result

に変更してはいかがでしょう?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/05 14:42

    ご確認ありがとうございます。

    > 15万件以上ですとnullに、それ以下ですと配列が出力される状況です。

    ということは、取得方法には問題なさそうですね。

    データ量が多い時特有の問題のようですので、お役に立てない気がしてきました(申し訳ない限りです……)。

    どうか解決にいたりますように。

    キャンセル

  • 2017/10/06 02:31

    無事解決に至ることができました
    ありがとうございました!

    キャンセル

  • 2017/10/06 12:04

    よかったです。お二方とも、お疲れ様でした!

    キャンセル

+4

らちがあかないので、デコードでなにが起きたのかエラーを検知するコードを作りました。直接的な問題の解決ではないですが、手がかりになるかと思います。

$data = Search::select('result', 'query')->where('id', 5)->first();

$json_array = json_decode($data->getAttributes()['result']));

// この処理を追加
switch (json_last_error()) {
        case JSON_ERROR_NONE: dd('JSON_ERROR_NONE'); break;
        case JSON_ERROR_DEPTH: dd('JSON_ERROR_DEPTH'); break;
        case JSON_ERROR_STATE_MISMATCH: dd('JSON_ERROR_STATE_MISMATCH'); break;
        case JSON_ERROR_CTRL_CHAR: dd('JSON_ERROR_CTRL_CHAR'); break;
        case JSON_ERROR_SYNTAX: dd('JSON_ERROR_SYNTAX'); break;
        case JSON_ERROR_UTF8: dd('JSON_ERROR_UTF8'); break;
        case JSON_ERROR_RECURSION: dd('JSON_ERROR_RECURSION'); break;
        case JSON_ERROR_INF_OR_NAN: dd('JSON_ERROR_INF_OR_NAN'); break;
        case JSON_ERROR_UNSUPPORTED_TYPE: dd('JSON_ERROR_UNSUPPORTED_TYPE'); break;
        case JSON_ERROR_INVALID_PROPERTY_NAME: dd('JSON_ERROR_INVALID_PROPERTY_NAME'); break;
        case JSON_ERROR_UTF16: dd('JSON_ERROR_UTF16'); break;
    }

 追記

不整合の原因が、モデルへの変換過程で起きるのか、DBから値を取得する際に起こるのかを判断するため、このコードを試してください。

// Modelへの代入を行わず、レコードをstdClassの配列形式で得られる
$data = \DB::select(\DB::raw("SELECT * FROM searches WHERE id = :id"), ['id' => $id]);
dd(strlen($data[0]->result));
// もし文字列長が正しければ以下も実験
$a = json_decode($data[0]->result, true);
dd($a);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/05 18:03

    惜しい、LENGTH(result)が抜けています。

    キャンセル

  • 2017/10/05 18:10

    ご指摘ありがとうございます。
    修正しました

    キャンセル

  • 2017/10/06 02:31

    無事回答を頂きまして解決することができました。
    ご助力いただきましてありがとうございました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • PHPに関する質問
  • 【laravel・RDS】DBに保存されている配列がnullになり取得できない(取得するjson配列が途中で切れる)