Kami menggunakan Node.js untuk bekerja dengan file besar dan set data mentah.



Posting ini adalah terjemahan dari artikel asli oleh Paid Nidrinhouse, insinyur perangkat lunak full-stack. Keistimewaan utamanya adalah JavaScript, tetapi Paige juga mempelajari bahasa dan kerangka kerja lain. Dan dia berbagi pengalamannya dengan para pembacanya. Ngomong-ngomong, artikel ini akan menarik bagi pemula.

Baru-baru ini, saya dihadapkan dengan tugas yang menarik minat saya - perlu untuk mengekstrak data tertentu dari sejumlah besar file yang tidak terstruktur dari Komisi Pemilihan Federal AS. Saya tidak bekerja terlalu banyak dengan data mentah, jadi saya memutuskan untuk mengambil tantangan dan mengambil tugas ini. Sebagai alat untuk menyelesaikannya, saya memilih Node.js.

Skillbox merekomendasikan: Kursus online Profesi Pengembang Frontend .

Kami mengingatkan Anda: untuk semua pembaca "Habr" - diskon 10.000 rubel saat mendaftar untuk kursus Skillbox apa pun menggunakan kode promosi "Habr".

Tugas itu dijelaskan dalam empat poin:
  • Program harus menghitung jumlah total baris dalam file.
  • Setiap kolom kedelapan berisi nama seseorang. Anda perlu memuat data ini dan membuat array dengan semua nama yang terkandung dalam file. Perlu untuk menampilkan nama ke-432 dan 43.243.
  • Setiap kolom kelima berisi tanggal donasi oleh relawan. Hitung berapa total sumbangan yang dilakukan setiap bulan, dan cetak total hasilnya.
  • Setiap kolom kedelapan berisi nama seseorang. Buat array dengan hanya memilih nama depan, tanpa nama belakang. Cari tahu nama mana yang paling sering ditemukan dan berapa kali?

(Tugas asli dapat dilihat di sini di tautan ini .)

File yang harus Anda kerjakan adalah .txt biasa sebesar 2,55 GB. Ada juga folder yang berisi bagian-bagian dari file utama (Anda dapat men-debug program pada mereka tanpa harus menganalisis seluruh array besar).

Dua kemungkinan solusi di Node.js


Pada prinsipnya, bekerja dengan file besar tidak membuat takut spesialis JavaScript. Selain itu, ini adalah salah satu fungsi utama Node.js. Ada beberapa solusi yang memungkinkan untuk membaca dari dan menulis ke file.

Yang familier adalah fs.readFile (). Ini memungkinkan Anda untuk membaca seluruh file, memasukkannya ke dalam memori, dan kemudian menggunakan Node.

Alternatifnya adalah fs.createReadStream (), fungsi yang meneruskan data yang mirip dengan bagaimana ia diatur dalam bahasa lain - misalnya, dalam Python atau Java.

Solusi yang saya pilih


Karena saya perlu menghitung jumlah total baris dan mem-parsing data ke mem-parsing nama dan tanggal, saya memutuskan untuk berhenti pada opsi kedua. Di sini saya bisa menggunakan fungsi rl.on ('line', ...) untuk mendapatkan data yang diperlukan dari baris.

Kode Node.js CreateReadStream () & ReadFile ()

Di bawah ini adalah kode yang saya tulis menggunakan Node.js dan fungsi fs.createReadStream ().



Awalnya, saya perlu mengatur semuanya, menyadari bahwa mengimpor data memerlukan fungsi Node.js seperti fs (sistem file), readline dan stream. Selanjutnya, saya bisa membuat instream dan outstream bersama dengan readLine.createInterface (). Kode yang dihasilkan memungkinkan untuk mengurai file baris demi baris, mengambil data yang diperlukan.

Selain itu, saya menambahkan beberapa variabel dan komentar untuk bekerja dengan data spesifik. Ini adalah lineCount, dupeNames dan array nama, donasi, dan firstNames.

Dalam fungsi rl.on ('line', ...), saya dapat mengatur parsing file baris demi baris. Jadi, saya memasukkan variabel lineCount untuk setiap baris. Saya menggunakan metode JavaScript split () untuk mem-parsing nama dengan menambahkannya ke array nama saya. Selanjutnya, saya hanya memisahkan nama tanpa nama keluarga, sambil menyoroti pengecualian, seperti keberadaan nama ganda, inisial di tengah nama, dll. Selanjutnya, saya memisahkan tahun dan tanggal dari kolom data, mengubah semua ini ke dalam format YYYY-MM dan menambahkan dateDonationCount ke array.

Dalam fungsi rl.on ('close', ...), saya melakukan semua transformasi data yang ditambahkan ke array, dengan informasi yang diterima di console.log.

lineCount dan nama diperlukan untuk menentukan nama 432 dan 43.243; tidak diperlukan konversi di sini. Tetapi identifikasi nama yang paling umum dalam array dan penentuan jumlah sumbangan adalah tugas yang lebih rumit.

Untuk mengidentifikasi nama yang paling umum, saya harus membuat objek pasangan nilai untuk setiap nama (kunci) dan jumlah referensi ke Object.entries (). (nilai) dan kemudian mengonversi semuanya menjadi array array menggunakan fungsi ES6. Setelah itu, tugas menyortir nama dan mengidentifikasi duplikat terbanyak tidak lagi sulit.

Dengan donasi, saya melakukan trik yang sama: Saya membuat objek pasangan nilai dan fungsi logDateElements (), yang memungkinkan saya, menggunakan interpolasi ES6, untuk menampilkan kunci dan nilai untuk setiap bulan. Lalu saya membuat Map baru (), mengonversi objek dateDonations ke metamarray, dan looped melalui setiap array menggunakan logDateElements (). (Ternyata tidak sesederhana seperti yang terlihat di awal.)

Tapi itu berhasil, saya bisa membaca file yang relatif kecil 400 MB, menyoroti informasi yang diperlukan.

Setelah itu saya mencoba fs.createReadStream () - Saya mengimplementasikan tugas di fs.readFile () untuk melihat perbedaannya. Ini kodenya:



Anda dapat melihat seluruh solusinya di sini .

Hasil kerja dengan Node.js


Solusinya ternyata berhasil. Saya menambahkan path ke file readFileStream.js dan ... menyaksikan server Node crash dengan JavaScript yang menumpuk karena kesalahan memori.



Ternyata, meskipun semuanya bekerja, tetapi solusi ini mencoba mentransfer seluruh isi file ke memori, yang tidak mungkin dengan kapasitas 2,55 GB. Node dapat bekerja secara bersamaan dengan memori 1,5 GB, tidak lebih.

Karena itu, tidak ada keputusan saya yang muncul. Butuh yang baru yang dapat bekerja bahkan dengan file yang sangat banyak.

Solusi baru


Ternyata, perlu untuk menggunakan EventStream modul NPM populer.

Setelah mempelajari dokumentasinya, saya dapat memahami apa yang perlu dilakukan. Ini adalah versi ketiga dari kode program.



Dokumentasi untuk modul menunjukkan bahwa aliran data harus dibagi menjadi elemen-elemen terpisah menggunakan karakter \ n pada akhir setiap baris file txt.

Pada dasarnya, satu-satunya hal yang harus saya ubah adalah respons nama. Saya tidak bisa memasukkan 130 juta nama ke dalam array - kesalahan kekurangan memori muncul lagi. Saya memecahkan masalah dengan menghitung nama 432 dan 43.243 dan menambahkannya ke array saya sendiri. Sedikit tidak apa yang ditanyakan dalam kondisi, tetapi siapa bilang Anda tidak bisa kreatif?

Babak 2. Kami mencoba program dalam pekerjaan


Ya, semua file yang sama dengan volume 2,55 GB, kami menyilangkan jari kami dan mengikuti hasilnya.



Sukses!

Ternyata, hanya Node.js tidak cocok untuk memecahkan masalah seperti itu, kemampuannya agak terbatas. Tetapi memperluas mereka menggunakan modul, Anda dapat bekerja dengan file besar seperti itu.

Skillbox merekomendasikan:

Source: https://habr.com/ru/post/id427415/


All Articles