Skip to content

Ceci n'est pas une pipe c'est une sandbox pour tester des trucs en NodeJS

Notifications You must be signed in to change notification settings

blured75/sandbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sandbox

Prérequis : node 8 minimum et maximum :)

Un mongo installé en local, sans auth et qui écoute sur 27017

Ce bac à sable à pour vocation de partager des expériences de coding NodeJS

La programmation avec des méthodes asynchrone étant assez particulière avec node, j'ai eu envie de partager mon expérience sur le principe d'intérroger une base mongo, d'écrire le résultat produit dans un fichier csv et aussi dans une base mysql. C'est complétement con me direz vous mais c'est dans les conneries qu'on fait les meilleures confitures.

En tout cas ça reste de l'expérimentation et si ça peut servir ou questionner ça sera tant mieux. Pour l'instant ça me sert de pense bête

Warning, les explications sont pour l'instant aussi bordélique que le niveau de ma compréhension du concept.

Fichiers disponibles :

. mongoExportAsync.js (version 1 du concept, qui fonctionne mais qui n'est pas géniale)

. mongoWithWriteOnFile.js (version 2 du concept, qui pourrait être bien si j'arrivais à la faire fonctionner)

But : Export sous forme csv des résultats d'une requête Mongo

Utilisation de promise à l'intérieur de la fonction callback passée à MongoClient.connect afin d'éviter d'avoir à imbriquer les sous-fonction à l'intérieur de cette fonction callback

Un find sur une base mongo est asynchrone comme beaucoup de méthodes disponibles sous nodeJS

On ne peut pas les gérer de la même manière qu'une programmation séquentielle habituelle

Les fonctions callback sont une belle avancée car elles permettent de déclencher des traitements à la suite d'une méthode asynchrone. Le problème c'est que ça devient difficilement gérable lorsque l'on empile et l'on tente d'ordonnencer le tout

Les promises ont été créé depuis ES5 afin de moins se prendre la tête avec le chainage des callback.

Une promesse (une sorte de function) prend en constructeur 2 fonctions de callback (resolve et error). resolve sera à appeler dans la promesse si l'exécution s'est déroulé sans encombre, error dans le cas contraire. Une promesse permet d'englober un code asynchrone et d'appeler la fonction success si tout se passe bien. On passe alors un argument à reject ou success. A la suite de la promesse on indique .then(...) pour exécuter le code que l'on souhaite et on récupère le paramètre passée à la fonction success.

-- Day 1

Dossier firstSample/

Exemple :

MongoClient.connect(connectionStr, mongoOptions, function(err, db) {
        assert.equal(null, err);
        
        //Step 1: declare promise
        let fetchBC = () => {
        return new Promise((resolve, reject) => {
            db
            .collection('content_businesscard')
            .find({ $or: [ {"content.articles.keywords.cptId":78922} , { "content.articles.keywords.cptId":78923 }] , 
                            "content.portfolio":"EUR"}, 
                  {"content.articles":1, "content.uid":1, "content.":1, "content.sid":1, "content.offerCode":1, "content.companyName":1})
            .toArray((err, docs) => {
                err 
                    ? reject(err) 
                    : resolve(docs);
            });
        }); 
    };

    //Step 2: make the real call with a then méthode
    // the argument passed to then is the one passed to the resolve(..) function
    // like some kind of magic
    fetchBC().then(docs => {
        console.log("**** after the query call and after the obtention of results ****")
        countNumberOfKeywordsPerDoc(docs)
        db.close();
     })
  }); //end mongo client

Le truc qui me déplait c'est que la requête passée à mongo n'est pas dans une function propre. Je souhaiterais pouvoir faire une vrai méthode qui me retourne une liste de docs que je puisse traiter et de ne pas intégrer tout mon traitement dans une grosse méthode difficilement factorisable

Dans l'absolue quelque chose comme ça serait bien

  function getGaliceWine() {
      ....
      return docs
  }

  function exportDocs(docs) {
      docs.forEach(...) {
          ...
      }
  }

  let docs = getGaliceWine()
  exportDocs(docs)

J'ai essayé ce genre de code qui tourne plus à de l'expérimentation wtf avec des instructions non maitrisées de bout en bout

function fetchGalliceBC() {
  return () => {
      MongoClient.connect(connectionStr, mongoOptions, function(err, db) {
          assert.equal(null, err)
          
          //Step 1: declare promise

          return () => {
              return new Promise((resolve, reject) => {
                  db
                  .collection('content_businesscard')
                  .find({ $or: [ {"content.articles.keywords.cptId":78922} , { "content.articles.keywords.cptId":78923 }] , 
                                  "content.portfolio":"EUR"}, 
                      {"content.articles":1, "content.uid":1, "content.":1, "content.sid":1, "content.offerCode":1, "content.companyName":1})
                  .toArray((err, docs) => {
                      err 
                          ? reject(err) 
                          : resolve(docs)
                  })
              })
          }
          
      })
  }    
}


fetchGalliceBC().then(docs => {
  console.log("**** after the query call and after the obtention of results ****")
  countNumberOfKeywordsPerDoc(docs)
  db.close()
  writeStream.end()
}) 
.catch(err => {
  throw err
})

Ce code pue méchament surtout les return ()=> placés là au petit bohneur la chance

Mais pourquoi cette méthodes fetchGalliceBC() n'est pas une promesse ? Je suis bloqué sur ce point pour l'instant

/Users/dboutin/dev/sandbox/mongoWithWriteOnFile.js:50
fetchGalliceBC().then(docs => {
                 ^

TypeError: fetchGalliceBC(...).then is not a function

Je viens de trouver la solution je suis passé de l'écoute de Meddle à Moustaki. Y aurait il un lien de causalité ?

Il suffit de retourner la promesse un niveau plus haut (avant MongoClient.connect)

function fetchGalliceBC() {
    //Step 1: declare promise
    return new Promise((resolve, reject) => {
        MongoClient.connect(connectionStr, mongoOptions, function(err, db) {
            assert.equal(null, err)
            
            db
            .collection('content_businesscard')
            .find({ $or: [ {"content.articles.keywords.cptId":78922} , { "content.articles.keywords.cptId":78923 }] , 
                            "content.portfolio":"EUR"}, 
                {"content.articles":1, "content.uid":1, "content.":1, "content.sid":1, "content.offerCode":1, "content.companyName":1})
            .toArray((err, docs) => {
                err 
                    ? reject(err) 
                    : resolve(docs)
            })
            db.close()
        })
    })
}

J'ai décidé d'ajouter un comptage de concepts dans mongodb par lettre de début de mots (compter le nombre de concepts commençant par A, par B, par C etc...)

Et bien un effet de bord marrant, le db.close() ferme la connexion trop tôt..

function fetchAllHeadings() {
    //Step 1: declare promise
    return new Promise((resolve, reject) => {
        
        MongoClient.connect(connectionStr, mongoOptions, function(err, db) {
            assert.equal(null, err)

            db
            .collection('content_concept')
            .find({ }, 
                {"content.labels":1})
            .toArray((err, docs) => {
                err 
                    ? reject(err) 
                    : resolve(docs)

                    
            })
           
               
        })
        
    })

L'erreur obtenue est la suivante

node:36513) UnhandledPromiseRejectionWarning: MongoError: connection destroyed, not possible to instantiate cursor
    at nextFunction (/Users/dboutin/dev/sandbox/node_modules/mongodb-core/lib/cursor.js:616:55)
    at Cursor.next [as _next] (/Users/dboutin/dev/sandbox/node_modules/mongodb-core/lib/cursor.js:701:3)
    at fetchDocs (/Users/dboutin/dev/sandbox/node_modules/mongodb/lib/cursor.js:857:10)
    at /Users/dboutin/dev/sandbox/node_modules/mongodb/lib/cursor.js:880:7

Faudrait il lancer une promise dans la promise ??? NON !

1 ère solution :

passer les objets db et docs en tableau à la méthode resolve, le then de la promesse y aura accès et il pourra fermer la connexion

function fetchAllHeadings() {
    //Step 1: declare promise
    return new Promise((resolve, reject) => {
        
        MongoClient.connect(connectionStr, mongoOptions, function(err, db) {
            assert.equal(null, err)

            db
            .collection('content_concept')
            .find({ }, 
                {"content.labels":1})
            .toArray((err, docs) => {
                err 
                    ? reject(err) 
                    : resolve([docs, db])
            })
        })
        
    })
}

Pour le fun ajout d'une méthode qui itère directement sur le curseur plutôt que sur un tableau en mémoire

2ème solution :

Dossier mongo/ Refactoring et utilisation de modules

db.js : module retournant une connexion vers un mongo local testDAO.js : mise à disposition des méthodes insert(object) et findAll() qui travaillent sur la base test

les functions insert et findAll sont appelées directment dans ce même fichier à des fins de tests

Et bim ! Encore un truc bizarre : nodeJS ne rend jamais la main après l'appel de $ node testDAO.js

Next step : Mettre en place un modèle plus objet pour gérer les accès mongo à DAO style

--- Day 2

Problème de nodejs qui ne rend pas la main résolu. La connection n'était jamais fermée réellement L'idée du db.close() était un peu merdique

Du coup dans l'objet db je ne laisse l'accès qu'à open() La promise renvoie la connection qu'il faut penser à fermer dans la classe appelante

Bon c'était bien marrant mais en fait c'est portnawak parce que mongoclient fournit déjà des promesses nativement si on n'utilise pas de callback

Dossier day2

mongoDay2.js 1 function simple d'insert et 1 function simple de find

  • Implementing an ArticleAPI with MongoDB model/Article.js articleTest.js

  • Sample of Redis usage redis/channelPubSub.js redis/testRedis.js

  • Test of serialization / deserialization perf serialization.js

  • Test on webStorage implementation for node usage of node webStorage

Pourquoi ne pas passer en anglais de temps en temps, ça passe bien

day 3

  • Test postgres access day3/postgres/testPostgres.js

    On a une série d'ordre SQL lancés sous forme de callback et de promises et de divers autres traitements. Comment ordonnancer tout ça ?

    Pour l'instant ça s'exécute séquentiellement, j'ai l'impression que c'est un coup de chance

A priori le client.query est asynchrone donc le console.log("..... between 2 queries ....") va s'éxécuter avant les 2 requêtes.

    client.query(query, (err, res) => {
        if (err) {
            console.log(err.stack)
        } else {
            console.log("1. bla")
        }
    })

    console.log(".... between 2 queries ...")

    client.query(query, (err, res) => {
        if (err) {
            console.log(err.stack)
        } else {
            console.log("2. bli")
        }
    })

day4 Si on veut chainer ces requêtes et être sur que tout se déroule dans l'ordre définit

Ce type d'écriture est un peu bizarre mais ça fonctionne

getConn().
then(async() => {
  console.log("avant 1st selectNow2")
  await selectNow2()
  console.log("after 1st selectNow2")
})
.then(async() => {
  console.log("avant 2nd selectNow2")
  await selectNow2()
  console.log("after 2nd selectNow2")
})

async function selectNow2() {
  await client.query('SELECT NOW()')
  .then(res => {
    console.log(res.rows[0])
  })
  .catch(err => {
    console.log(err.stack)
  })
  
  console.log("1. connect & select now()")
}

Une manière plus simple de faire la même chose

async function performQuery(){
  await getConn();
  console.log("before 1st selectNow2");
  await selectNow2();
  console.log("after 1st selectNow2");
  console.log("before 2nd selectNow2");
  await selectNow2();
  console.log("after 2nd selectNow2");
}


async function selectNow2() {
  console.log("1. connect & select now()")
  await client.query('SELECT NOW()')
  .then(res => {
    console.log(res.rows[0])
  })
  .catch(err => {
    console.log(err.stack)
  })
  
}

performQuery().then(() => {
  console.log("DONE")
  client.end()
})

day5 : Express / Connect + postgres

day5/testConnect.js

day6/

  • handlingErrors.js error handling defined in errorHandler.js

  • expressTest.js

  • completeExpressSite/shoutBox Implementation of a kind of twitter in node Using express, ejs

Now, it is time to provide an external api to shoutBox


Day7 : Simple express with docker

Utilisation des variables
PGPASSWORD: postgres PGUSER: postgres PGDATABASE: articles

Le driver postgres reconnait ces variables et les inclus dans la demande de connexion si rien n'est fournit

Pour builder l'image de l'application express

$ docker build -t blured75/node-web-app .

Pour monter l'ensemble des VM (postgres & express)

$ docker-compose up

Accès à l'application via http://localhost:3000 qui affiche l'heure en faisant un appel à postgres :D

Trèèèès utile

About

Ceci n'est pas une pipe c'est une sandbox pour tester des trucs en NodeJS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages