Comment itérer un gros fichier json

Chers Stackoverflow de la communauté,

j'ai un fichier JSON de 34 GO qui contient beaucoup de données. J'ai essayé d'importer dans mon mongodb en utilisant mongoimport --file file.json-mais il a échoué bien sûr le fichier est trop grand et a jeté une erreur de jet de système de mémoire vous le savez. Est-il possible d'utiliser du code php pour parcourir le fichier avec un curseur? Je n'ai aucune expérience sur ce, quelqu'un m'a dit que ce serait possible. Je veux savoir comment le fichier est en construction, mais je ne sais pas comment pour voir un exemple du tableau. De la source je pourrais obtenir un tableau d'exemple:

{
     "_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)
         }
     }
 }

le champ stats contient plus de tableaux, 110 est juste un exemple. Comment puis-je itérer à travers ce fichier de grande taille ou comment puis-je l'importer dans mon mongodb? Par exemple; je veux echo summonerid,championid (qui est 110 dans ce cas),totalssionsplayed. Il doit se relouer autant qu'il le faut jusqu'à ce qu'il n'y ait plus de champion pour ce summonerid en particulier.

Encore... Un summonerID a une liste de champions qu'il a joué dans sa carrière. Les Champions font référence à (dans cet exemple) 110. Chaque summonerid peut contenir plusieurs champions et je veux avoir tous les champions, combien de fois le champion a été joué (totalsessionplayed) par summonerid.

7
demandé sur floppy 2014-10-16 02:31:36

1 réponses

vous voudrez utiliser un analyseur de flux. Ces tirer seulement de petites portions de votre fichier dans la mémoire.

ils viennent dans un couple de saveurs différentes: Sax-like parsers, et pull parsers. XML reader models: SAX versus XML pull parser donne un aperçu de la différence.


Push Parser

ceci est un exemple rapide en utilisant salsifis/json-streaming-analyseur .

comme il roule dans le dossier nous garderons la trace du summonerId , championId , et l'état. Tout est basé sur les événements - vous n'avez pas accès au hasard avec un analyseur séquentiel donc vous devez garder une trace des choses vous-même. Chaque fois qu'un totalSessionsPlayed apparaît, il fait écho aux summonerId , championId , et totalssionsplayed .


données.json

il s'agit d'un fichier JSON couplé pour une démonstration.

[
    {
        "_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
            }
        }
    }
]

exemple:

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;
}

sortie:

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

Pull Parser

ceci utilise un analyseur que j'ai récemment écrit, pcrov/jsonreader (nécessite PHP 7.)

mêmes données.json comme ci-dessus.

exemple:

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();

sortie:

24570940, 110, 3
24570940, 112, 45
555555, 42, 65
555555, 88, 99
10
répondu Paul Crovella 2016-09-17 12:49:11