Как правильно перебрать большой файл JSON

Уважаемое сообщество Stackru,

У меня есть файл JSON объемом 34 ГБ, в котором много данных. Я попытался импортировать в мой mongodb, используя mongoimport --file file.json - но он не смог найти файл слишком большого размера и выдал ошибку сброса системы памяти, которую вы знаете. Можно ли использовать php-код для перебора файла с курсором? У меня нет опыта в этом, кто-то сказал мне, что это будет возможно. Я хочу знать, как создается файл, но я не знаю, как просмотреть пример его массива. Из источника я мог получить пример массива:

{
     "_id": ObjectId("53b29644aafd413977b23b7e"),
     "summonerId": NumberLong(24570940),
     "region": "euw",
     "updatedAt": NumberLong(1404212804),
     "season": NumberLong(4),
     "stats": {
         "110": {
             "totalSessionsPlayed": NumberLong(3),
             "totalSessionsLost": NumberLong(2),
             "totalSessionsWon": NumberLong(1),
             "totalChampionKills": NumberLong(34),
             "totalDamageDealt": NumberLong(415051),
             "totalDamageTaken": NumberLong(63237),
             "mostChampionKillsPerSession": NumberLong(12),
             "totalMinionKills": NumberLong(538),
             "totalDoubleKills": NumberLong(5),
             "totalTripleKills": NumberLong(1),
             "totalDeathsPerSession": NumberLong(18),
             "totalGoldEarned": NumberLong(40977),
             "totalTurretsKilled": NumberLong(6),
             "totalPhysicalDamageDealt": NumberLong(381668),
             "totalMagicDamageDealt": NumberLong(31340),
             "totalAssists": NumberLong(25),
             "maxChampionsKilled": NumberLong(12),
             "maxNumDeaths": NumberLong(10)
         }
     }
 }

Поле stats содержит больше массивов, 110 - это просто пример. Как я могу перебрать этот файл большого размера или как я могу импортировать его в мой mongodb? Например; Я хочу повторить summonerid,championid (в данном случае это 110),totalSessionsPlayed. Он должен перебрасываться столько, сколько ему нужно, до тех пор, пока для этого конкретного суммонерида не останется чемпионов.

Опять же... У summonerID есть список чемпионов, в которых он играл за свою игровую карьеру. Чемпионы имеют в виду (в данном примере) 110. Каждый отдельный суммонерид может содержать несколько чемпионов, и я хочу, чтобы все чемпионы были равны тому, сколько раз чемпион играл (totalsessionplayed) с помощью summonerid.

3 ответа

Вы захотите использовать потоковый парсер. Они вытягивают только небольшие части вашего файла в память одновременно.

Они бывают разных видов: push-парсеры типа SAX и парсеры pull. Модели чтения XML: SAX по сравнению с синтаксическим анализатором XML Pull дает обзор различий.


Push Parser

Это быстрый пример использования https://github.com/salsify/jsonstreamingparser.

Когда он катится по файлу, мы будем следить за summonerId, championIdи гос. Все это основано на событиях - вы не получаете произвольный доступ с последовательным парсером, поэтому вы должны сами следить за вещами. Каждый раз totalSessionsPlayed появится, он выведет summonerId, championId и totalSessionsPlayed.


data.json

Это спаренный файл json для демонстрационных целей.

[
    {
        "_id": "53b29644aafd413977b23b7e",
        "summonerId": 24570940,
        "region": "euw",
        "stats": {
            "110": {
                "totalSessionsPlayed": 3,
                "totalSessionsLost": 2,
                "totalSessionsWon": 1
            },
            "112": {
                "totalSessionsPlayed": 45,
                "totalSessionsLost": 2,
                "totalSessionsWon": 1
            }
        }
    },
    {
        "_id": "asdfasdfasdf",
        "summonerId": 555555,
        "region": "euw",
        "stats": {
            "42": {
                "totalSessionsPlayed": 65,
                "totalSessionsLost": 2,
                "totalSessionsWon": 1
            },
            "88": {
                "totalSessionsPlayed": 99,
                "totalSessionsLost": 2,
                "totalSessionsWon": 1
            }
        }
    }
]

Пример:

class ListMatchUps extends JsonStreamingParser\Listener\IdleListener
{

    private $key;
    private $summonerId;
    private $championId;
    private $inStats;

    public function start_document()
    {
        $this->key        = null;
        $this->summonerId = null;
        $this->championId = null;
        $this->inStats    = false;
    }

    public function start_object()
    {
        if ($this->key === 'stats') {
            $this->inStats = true;
        } else if ($this->inStats) {
            $this->championId = $this->key;
        }
    }

    public function end_object()
    {
        if ($this->championId !== null) {
            $this->championId = null;
        } else if ($this->inStats) {
            $this->inStats = false;
        } else {
            $this->summonerId = null;
        }
    }

    public function key($key)
    {
        $this->key = $key;
    }

    public function value($value)
    {
        switch ($this->key) {
            case 'summonerId':
                $this->summonerId = $value;
                break;
            case 'totalSessionsPlayed':
                echo "{$this->summonerId},{$this->championId},$value\n";
                break;
        }
    }
}

$stream = fopen('data.json', 'r');
$listener = new ListMatchUps();
try {
    $parser = new JsonStreamingParser_Parser($stream, $listener);
    $parser->parse();
} catch (Exception $e) {
    fclose($stream);
    throw $e;
}

Выход:

24570940,110,3
24570940,112,45
555555,42,65
555555,88,99

Pull Parser

Это использует парсер, который я недавно написал, pcrov / jsonreader (требуется PHP 7.)

Тот же data.json, что и выше.

Пример:

use pcrov\JsonReader\JsonReader;

$reader = new JsonReader();
$reader->open("data.json");

while($reader->read("summonerId")) {
    $summonerId = $reader->value();
    $reader->next("stats");
    foreach($reader->value() as $championId => $stats) {
        echo "$summonerId, $championId, {$stats['totalSessionsPlayed']}\n";
    }
}
$reader->close();

Выход:

24570940, 110, 3
24570940, 112, 45
555555, 42, 65
555555, 88, 99

Вам нужно использовать анализ потока. Есть несколько библиотек, которые могут вам в этом помочь. Например, я попробовал JSON Machine (он упоминается в репозитории проекта Awesome PHP ), и он у меня отлично работает:

      use \JsonMachine\Items;

$fruits = Items::fromFile('1.json', ['pointer' => '/stats']);
foreach ($fruits as $name => $data) {
        echo $name . " totalSessionsPlayed: " . $data->totalSessionsPlayed . "\n";
}

Еще один пример аналогичной библиотеки JSON Stream Parser, но я не пробовал анализировать с ее помощью ваш пример JSON.

Обработка большого файла JSON размером 34 ГБ в памяти может быть сложной задачей, поэтому рекомендуется обрабатывать его небольшими фрагментами, чтобы избежать проблем, связанных с памятью. Вот подход, использующий PHP для перебора большого файла JSON и импорта данных в MongoDB небольшими пакетами.

  1. Разделите большой файл JSON . Чтобы избежать загрузки всего файла JSON в память, вы можете разделить его на более мелкие фрагменты с помощью такой команды (в системах на базе Unix) или специального сценария.

    Пример использования:

            split -l 10000 large_file.json split_
    

    Эта команда разбивает файл JSON на файлы меньшего размера по 10000 строк каждый.

  2. PHP-скрипт для импорта в MongoDB . Напишите PHP-скрипт для перебора небольших файлов JSON и пакетной вставки данных в MongoDB. Вы можете использоватьРасширение PHP для взаимодействия с MongoDB.

    Вот упрощенный пример:

            <?php
    // Load the MongoDB extension
    $mongo = new MongoDB\Driver\Manager("mongodb://localhost:27017");
    
    // Iterate through the split JSON files
    $fileList = glob("split_*");
    
    foreach ($fileList as $file) {
        $lines = file($file);
        foreach ($lines as $line) {
            // Parse each line as JSON
            $document = json_decode($line, true);
    
            // Insert the document into MongoDB
            $bulk = new MongoDB\Driver\BulkWrite();
            $bulk->insert($document);
    
            // Execute the bulk write
            $mongo->executeBulkWrite('your_database.your_collection', $bulk);
        }
    }
    ?>
    

    Обязательно замените,, ис данными вашего подключения к MongoDB, а также именами целевой базы данных и коллекции.

  3. Обработка данных . После импорта данных в MongoDB вы можете использовать запросы MongoDB для обработки и получения конкретных данных, которые вам нужны. Например, вы можете использовать запросы агрегирования для группировки данных по,, и вычислить.

Обратите внимание, что это упрощенный пример, и вам, возможно, придется адаптировать его к вашим конкретным требованиям и обеспечить обработку потенциальных ошибок и исключений в процессе импорта. Кроме того, MongoDB может иметь определенные оптимизации и особенности обработки больших наборов данных, поэтому вам следует обратиться к документации MongoDB для получения дополнительных указаний по оптимизации и выполнению запросов к большим коллекциям.

Другие вопросы по тегам