Bulk Importeer een CSV-bestand in MongoDB met behulp van Mongoose met Node.js

Wat je gaat creëren

Dit onderwerp is echt een plezierige voor mij. In veel webtoepassingen is het gebruikelijk om gebruikersinvoer te accepteren en één record in uw database op te slaan. Maar hoe zit het wanneer uw gebruikers (of u) meerdere invoegingen in één opdracht willen uitvoeren? 

Geef dit artikel op, waarin wordt uitgelegd hoe u een CSV-sjabloon en een formulier kunt maken om het CSV-bestand te uploaden en hoe u de CSV kunt ontleden in een Mongoose-model dat wordt opgeslagen in een MongoDB-database.

In dit artikel wordt ervan uitgegaan dat je een basiskennis hebt van Mongoose en hoe het interageert met MongoDB. Als je dat niet doet, raad ik aan om eerst mijn artikel Introduction to Mongoose voor MongoDB en Node.js te lezen. In dit artikel wordt beschreven hoe Mongoose interageert met MongoDB door sterk getypeerde Schema's te maken waaruit een model is gemaakt. Als je al een goed begrip hebt van Mongoose, laten we doorgaan.

Ermee beginnen

Laten we beginnen met het maken van een nieuwe Node.js-toepassing. Navigeer in een opdrachtprompt naar waar u uw Node.js-toepassingen wilt hosten en voer de volgende opdrachten uit:

mkdir csvimport cd csvimport npm init

Ik heb alle standaardinstellingen op mijn plaats gelaten, dus mijn toepassing zal beginnen index.js. Voordat u CSV-bestanden kunt maken en parseren, moet eerst een eerste installatie worden uitgevoerd. Ik wil dit een webapplicatie maken; om dat te doen, ga ik het Express-pakket gebruiken om alle strikt noodzakelijke server-instellingen af ​​te handelen. Installeer Express in de opdrachtprompt met de volgende opdracht:

npm install express --save

Omdat deze webapplicatie bestanden accepteert via een webformulier, ga ik ook het Express-subpakket Express-bestand uploaden gebruiken. Laten we dat nu ook installeren:

npm installeer express-fileupload - opslaan

Ik heb nu genoeg initiële configuratie gedaan om mijn webtoepassing in te stellen en een basiswebpagina te maken waarmee ik mijn bestanduploadformulier kan maken.

Hier is mijn index.js bestand dat mijn webserver opzet:

var app = require ('express') (); var fileUpload = require ('express-fileupload'); var server = vereisen ('http') Server (app); app.use (FileUpload ()); server.listen (80); app.get ('/', functie (req, res) res.sendFile (__ dirname + '/index.html'););

Dit voorbeeld importeert Express en de Express File Upload-bibliotheken, configureert mijn webtoepassing om het uploaden van bestanden te gebruiken en luistert naar poort 80. Dit voorbeeld heeft ook een route gecreëerd met Express op "/", wat de standaard bestemmingspagina voor mijn web is toepassing. Deze route retourneert een index.html bestand dat het webformulier bevat waarmee een gebruiker een CSV-bestand kan uploaden. In mijn geval werk ik op mijn lokale computer, dus wanneer ik http: // localhost bezoek, zal ik de vorm zien die ik in het volgende voorbeeld maak.

Hier is mijn index.html pagina die mijn formulier maakt voor het uploaden van een CSV-bestand:

   Upload auteurs   

Gebruik het onderstaande formulier om een ​​lijst met auteurs te uploaden. Klik hier voor een voorbeeldsjabloon.



Dit HTML-bestand bevat twee belangrijke zaken:

  1. Een link naar "/ sjabloon" die, wanneer erop wordt geklikt, een CSV-sjabloon downloadt die kan worden gevuld met de informatie die moet worden geïmporteerd.
  2. Een formulier met de eNCTYPE instellen als multipart / form-data en een invoerveld met een type het dossier dat accepteert bestanden met een "csv" extensie.

Zoals je misschien hebt gemerkt, verwijst de HTML naar een sjabloon van een auteur. Als je mijn artikel Introduction to Mongoose leest, heb ik een Author Schema gemaakt. In dit artikel ga ik dit Schema opnieuw maken en de gebruiker toestaan ​​om een ​​verzameling auteurs massaal in mijn MongoDB-database te importeren. Laten we het Schema van de auteur bekijken. Voordat we dat echter doen, heb je het waarschijnlijk al geraden: we moeten het Mongoose-pakket installeren:

npm mongoose installeren - opslaan

Het schema en het model maken

Met Mongoose geïnstalleerd, laten we een nieuw maken author.js bestand dat het Auteursschema en -model zal definiëren:

var mongoose = vereisen ('mongoose'); var authorSchema = mongoose.Schema (_id: mongo.Schema.Types.ObjectId, name: firstName: type: String, required: true, lastName: String, biography: String, twitter: type: String, validate : validator: function (text) if (text! == null && text.length> 0) return text.indexOf ('https://twitter.com/') === 0; return true;, bericht : 'Twitter-handvat moet beginnen met https://twitter.com/', facebook: type: String, valideren: validator: functie (tekst) if (text! == null && text.length> 0) return text.indexOf ('https://www.facebook.com/') === 0; return true;, bericht: 'Facebook Page moet beginnen met https://www.facebook.com/', linkedin: type: String, valideren: validator: functie (tekst) if (text! == null && text.length> 0) return text.indexOf ('https://www.linkedin.com/') = == 0; return true;, bericht: 'LinkedIn moet beginnen met https://www.linkedin.com/', profilePicture: Buffer, aangemaakt: type: Date, default: Date.now); var Auteur = mongoose.model ('Auteur', auteur Schema); module.exports = Auteur;

Met het auteursschema en het gemaakte model kunnen we schakelen en ons richten op het maken van de CSV-sjabloon die kan worden gedownload door op de sjabloonkoppeling te klikken. Als hulp bij het genereren van de CSV-sjabloon ga ik het pakket JSON naar CSV gebruiken. Laten we dat nu installeren:

npm installeer json2csv --save

Ik ga nu mijn eerder gemaakte update updaten index.js bestand om een ​​nieuwe route voor "/ sjabloon" op te nemen:

var template = require ('./ template.js'); app.get ('/ template', template.get);

Ik heb alleen de nieuwe code toegevoegd voor de sjabloonroute die aan de vorige is toegevoegd index.js het dossier.

Het eerste dat deze code doet, is een nieuw template.js bestand (wordt hierna aangemaakt) en maak een route voor "/ sjabloon". Deze route roept een krijgen functie in de template.js het dossier.

Nadat de Express-server is bijgewerkt met de nieuwe route, maken we de nieuwe template.js het dossier:

var json2csv = require ('json2csv'); exports.get = function (req, res) var fields = ['name.firstName', 'name.lastName', 'biography', 'twitter', 'facebook', 'linkedin']; var csv = json2csv (data: ", fields: fields); res.set (" Content-Disposition "," attachment; filename = authors.csv "); res.set (" Content-Type "," application / octet-stream "); res.send (csv);;

Dit bestand bevat eerst de eerder geïnstalleerde json2csv pakket. Ik maak en exporteer vervolgens een krijgen functie. Deze functie accepteert de aanvraag- en reactieobjecten van de Express-server.

Binnen de functie heb ik een array met de velden gemaakt die ik in mijn CSV-sjabloon wil opnemen. Dit kan op twee manieren worden gedaan. De eerste manier (die in dit voorbeeld wordt gedaan) is om een ​​statische lijst te maken van de velden die in de sjabloon moeten worden opgenomen. De tweede manier is om de lijst met velden dynamisch te maken door de eigenschappen uit het auteursschema te extraheren.

De tweede manier zou kunnen worden gedaan met de volgende code:

var fields = Object.keys (Author.schema.obj);

Ik had graag deze dynamische methode willen gebruiken, maar het wordt een beetje ingewikkeld als ik niet meerdere eigenschappen van het Schema wil opnemen in mijn CSV-sjabloon. In dit geval bevat mijn sjabloon de _ID kaart en aangemaakt eigenschappen omdat deze worden ingevuld via code. Als u echter geen velden hebt die u wilt uitsluiten, werkt de dynamische methode ook.

De CSV-sjabloon maken

Met de reeks gedefinieerde velden gebruik ik de json2csv pakket om mijn CSV-sjabloon van mijn JavaScript-object te maken. Deze csv object zullen de resultaten van deze route zijn.

En tot slot, met behulp van de res eigenschap van de Express-server, heb ik twee header-eigenschappen ingesteld die het downloaden van een authors.csv het dossier.

Als u op dit moment uw Node-toepassing zou uitvoeren en naar http: // localhost in uw webbrowser zou gaan, zou het webformulier worden weergegeven met een koppeling om de sjabloon te downloaden. Als u op de koppeling klikt om de sjabloon te downloaden, kunt u de authors.csv bestand dat moet worden ingevuld voordat het wordt geüpload.

Hier is een voorbeeld van een bevolkt CSV-bestand:

name.firstName, name.lastName, biografie, twitter, facebook, linkedin Jamie, Munro, Jamie is een webontwikkelaar en auteur ,,, Mike, Wilson, Mike is een webontwikkelaar en auteur van Node.js,,,

Als dit voorbeeld wordt geüpload, worden twee auteurs gemaakt: ikzelf en een vriend die een paar jaar geleden een boek schreef over Node.js. Het kan je opvallen dat aan het einde van elke regel drie komma's ",,," staan. Dit wordt gedaan om het voorbeeld af te korten. Ik heb de sociale netwerkeigenschappen niet ingevuld (tjilpen, facebook, en linkedin).

De puzzelstukjes beginnen samen te komen en vormen een foto. Laten we naar het vlees en de aardappelen van dit voorbeeld gaan en dat CSV-bestand analyseren. De index.js bestand vereist wat updates om verbinding te maken met MongoDB en een nieuwe POST-route te maken die het uploaden van het bestand accepteert:

var app = require ('express') (); var fileUpload = require ('express-fileupload'); var mongoose = vereisen ('mongoose'); var server = vereisen ('http') Server (app); app.use (FileUpload ()); server.listen (80); mongoose.connect ( 'MongoDB: // localhost / csvimport'); app.get ('/', functie (req, res) res.sendFile (__ dirname + '/index.html');); var template = require ('./ template.js'); app.get ('/ template', template.get); var upload = require ('./ upload.js'); app.post ('/', upload.post);

Met een databaseverbinding en een nieuwe POST-route geconfigureerd, is het tijd om het CSV-bestand te ontleden. Gelukkig zijn er verschillende geweldige bibliotheken die helpen met deze taak. Ik heb ervoor gekozen om de fast-csv pakket dat kan worden geïnstalleerd met de volgende opdracht:

npm installeer fast-csv --save

De POST-route is op dezelfde manier gemaakt als de sjabloonroute die a aanroept post functie van de upload.js het dossier. Het is niet nodig om deze functies in afzonderlijke bestanden te plaatsen; Ik maak echter graag afzonderlijke bestanden voor deze routes omdat dit helpt de code mooi en georganiseerd te houden.

Gegevens verzenden

En tot slot, laten we de upload.js bestand met de post functie die wordt aangeroepen wanneer het eerder gemaakte formulier wordt verzonden:

var csv = require ('fast-csv'); var mongoose = vereisen ('mongoose'); var Auteur = require ('./ author'); exports.post = functie (req, res) if (! req.files) return res.status (400) .send ('Geen bestanden geüpload.'); var authorFile = req.bestanden.bestand; var authors = []; csv .fromString (authorFile.data.toString (), headers: true, ignoreEmpty: true) .on ("data", functie (data) data ['_ id'] = new mongoose.Types.ObjectId (); authors.push (data);) .on ("end", function () Author.create (authors, function (err, documents) if (err) gooi err;); res.send (authors.length + 'auteurs zijn succesvol geüpload.');); ;

Er gebeurt nogal wat in dit bestand. De eerste drie regels bevatten de benodigde pakketten die nodig zijn om de CSV-gegevens te ontleden en op te slaan.

Vervolgens de post functie is gedefinieerd en geëxporteerd voor gebruik door de index.js het dossier. Binnen deze functie vindt de betovering plaats.

De functie controleert eerst of er een bestand is in de behuizing van het verzoek. Als dit niet het geval is, wordt een fout geretourneerd die aangeeft dat een bestand moet worden geüpload.

Wanneer een bestand is geüpload, wordt een verwijzing naar het bestand opgeslagen in een variabele met de naam authorFile. Dit wordt gedaan door toegang te krijgen tot de bestanden array en de het dossier eigenschap in de array. De het dossier eigenschap komt overeen met de naam van mijn bestandsinvoer naam die ik voor het eerst heb gedefinieerd in de index.html voorbeeld.

Ik heb ook een gemaakt auteurs array die wordt gevuld als het CSV-bestand wordt geparseerd. Deze array wordt gebruikt om de gegevens in de database op te slaan.

De fast-csv bibliotheek wordt nu gebeld door gebruik te maken van de fromString functie. Met deze functie accepteert u het CSV-bestand als een tekenreeks. Ik heb de string uit de authorFile.data eigendom. De gegevens property bevat de inhoud van mijn geüploade CSV-bestand.

Ik heb twee opties toegevoegd aan de fast-csv functie: headers en ignoreEmpty. Deze zijn beide ingesteld op waar. Dit vertelt de bibliotheek dat de eerste regel van het CSV-bestand de kopteksten bevat en dat lege rijen moeten worden genegeerd.

Met de geconfigureerde opties, heb ik twee listener-functies ingesteld die worden aangeroepen wanneer de gegevens evenement en de einde gebeurtenis wordt geactiveerd. De gegevens event wordt eenmaal voor elke rij van het CSV-bestand aangeroepen. Deze gebeurtenis bevat een JavaScript-object van de geparseerde gegevens.

Ik werk dit object bij met de _ID kaart eigendom van de auteur Schema met een nieuwe ObjectId. Dit object wordt vervolgens toegevoegd aan de auteurs rangschikking.

Wanneer het CSV-bestand volledig is geparseerd, wordt de einde gebeurtenis is geactiveerd. Binnen de callback-functie van het evenement, bel ik de creëren functioneert op het Auteur-model en geeft de reeks door auteurs ernaar toe.

Als er een fout optreedt bij het proberen de array op te slaan, wordt een uitzondering gegenereerd; anders wordt een succesbericht weergegeven aan de gebruiker die aangeeft hoeveel auteurs zijn geüpload en opgeslagen in de database.

Als je de volledige broncode wilt zien, heb ik een GitHub-repository met de code gemaakt.

Conclusie

In mijn voorbeeld heb ik slechts een aantal records geüpload. Als u in uw use-case duizenden records kunt uploaden, is het wellicht een goed idee om de records in kleinere delen op te slaan.

Dit kan op verschillende manieren worden gedaan. Als ik het zou implementeren, zou ik willen voorstellen het gegevens callback-functie om de lengte van de auteurs-array te controleren. Wanneer de array uw gedefinieerde lengte overschrijdt, b.v. 100, bel de Author.create op de array en stel vervolgens de array opnieuw in op leeg. Dit zal dan de records in stukken van 100 opslaan. Zorg ervoor dat u de finale verlaat creëren bel in de einde callback-functie om de definitieve records op te slaan.

Genieten!