简体   繁体   中英

Node.JS, Express & MongoDB :: Multiple Collections

I'm using Node.JS, Express, MongoDB and EJS. I have a question regarding multiple MongoDB collections under one database, and calling them to use on the front-end. I have 3 collections, and after much research haven't found how to call more than one collection without errors.

I understand that it wouldn't be a problem to store everything under one collection, but I feel like the best approach would be to separate them out by category. Here's my db/collection call:

app.use(express.bodyParser());
var MongoClient = require('mongodb').MongoClient;

app.get('/', function(req, res){
  MongoClient.connect("mongodb://localhost:27017/michael", function(err, db) {
    if(!err) {
      console.log("We are connected");
    }
    db.collection("portfolio", function(err, collection) {
      collection.find().sort({order_num: 1}).toArray(function(err, result) {
        var portfolio = [];
        if (err) {
          throw err;
        } else {
          for (i=0; i<result.length; i++) {
            portfolio[i] = result[i];
          }
          res.render('index.html', {portfolio: portfolio});
        }
      });
    });
  });
});

How could I make multiple calls to additional collections, such as an "about" collection, etc? I tried adding another...

db.collection("about", function(err, collection) {

...within...

MongoClient.connect("mongodb://localhost:27017/michael", function(err, db) {

...but it gives me errors in the Terminal console log.

Thanks in advance.

EDIT:

Here's what I'm trying to do, which gives me an error:

app.get('/', function(req, res){
  MongoClient.connect("mongodb://localhost:27017/michael", function(err, db) {
    if(!err) {
      console.log("We are connected");
    }
    db.collection("portfolio", function(err, collection) {
      collection.find().sort({order_num: 1}).toArray(function(err, result) {
        var portfolio = [];
        if (err) {
          throw err;
        } else {
          for (i=0; i<result.length; i++) {
            portfolio[i] = result[i];
          }
          res.render('index.html', {portfolio: portfolio});
        }
      });
    });
     // Here's the additional collection call:
     db.collection("about", function(err, collection) {
      collection.find().sort({order_num: 1}).toArray(function(err, result) {
        var about = [];
        if (err) {
          throw err;
        } else {
          for (i=0; i<result.length; i++) {
            about[i] = result[i];
          }
          res.render('index.html', {about: about});
        }
      });
    });
  });
});

Here's the error from the console:

ReferenceError: /Volumes/Files/WebDev/RHC/michael/michael/views/index.html:62
60|               <div class="row-fluid">
61|                 <ul id="work" class="thumbnails">
>> 62|                     <% for(i=0; i<portfolio.length; i++) { %>
63|                         <% if (portfolio[i].proj_type == "work") { %>
64|                             <% var newLine = ""; %>
65|                             <% var fancyBox = ""; %>

portfolio is not defined

So I have two collections in MongoDB under the db called "michael", and I'm trying to call both of them under one MongoClient.connect(). I'm then sending the results to the front-end (via EJS) in two arrays: "portfolio" and "about". It seems that when I do this, it renders "portfolio" undefined. Hope that makes sense.

Ok, using aesede's solution plus my own tinkering, I got it to render two collections. It could probably use some finessing but here's what I got:

// GLOBAL ARRAYS FOR STORING COLLECTION DATA
var collectionOne = [];
var collectionTwo = [];
app.get('/', function(req, res){
  MongoClient.connect("mongodb://localhost:27017/michael", function(err, db) {
    if(!err) {
      console.log("We are connected");
    }
    db.collection("collectionOne", function(err, collection) {
      collection.find().sort({order_num: 1}).toArray(function(err, result) {
        if (err) {
          throw err;
        } else {
          for (i=0; i<result.length; i++) {
            collectionOne[i] = result[i];
          }
        }
      });
      db.collection("collectionTwo", function(err, collection) {
        collection.find().sort({order_num: 1}).toArray(function(err, result) {
          if (err) {
            throw err;
          } else {
            for (i=0; i<result.length; i++) {
              collectionTwo[i] = result[i];
            }
          }
        });
      });
      // Thank you aesede!
      res.render('index.html', {
        collectionOne: collectionOne,
        collectionTwo: collectionTwo
      });
    });
  });
});

The only bug, per se, that I found, was that when Node restarted and I hit "refresh" in the browser, I didn't see any content being rendered in the HTML. However, any subsequent refresh showed the content consistently.

There is actually a really simple, intuitive answer to this problem. It's definitely not elegant and I do think an update to Express, Mongoose and NodeJS should include a preset way of doing this rather than having to logically think the process through.

All you have to do is nest your find() calls into each other for each collection you want to pass through to the page. Example below:

app.get("/", function(req, res) {
     Collection.find({}, function(err, collection) {
          if(err) {
               console.log(err);
          } else {
               Collection2.find({}, function(err, collection2) {
                    if(err) {
                         console.log(err)
                    } else {
                         res.render("page", {collection: collection, collection2: collection2});
                    }  
               }); 
          }
     });
});

You can keep doing this infinitely but obviously this concept does not scale. If you had thirty different collections to pass through, this would be a really inconvenient way to do so.

From the research I've done, this is not as simple as it should be because the Collection.find() method is actually a Promise function. If it wasn't a promise function, we could simply just run the find() method for each collection, pass the results into a global object, and pass the entire object through the render. You can't do this though because the global variable will be initialized and passed through the render before the promise function finishes. You end up passing an empty object through the render. I know this because I tried it.

I have no experience with Promise functions. I'm going to do some research on them and see if there is a way to structure your code to take advantage of the promise function and execute this task more "elegantly". But in the mean time, use my example above, as I believe it is the most intuitive way to accomplish this.

I'm with same problem, and now I know it has to do with the fact that you cannot res.render() twice. There's a solution some recommend: using socket.io . I'd like to know if there's another solution, in my case I want to get recipes and authors from 2 different mongoDB collections but I can't do it with only 1 res.render(), can't believe node.js + express + mongoose won't accomplish something that simple to do in let's say... LAMP.

app.get("/customer",function(req,res) {
    var orders={}; //Create Empty order Object
    var products={}; //Create Empty product Object
    Product.find({},function (err,allproducts) {
        if (err) {
            console.log(err);
        } else {
            //Find Collection And Assign It To Object
            products=allproducts;             
        }
    });
    Order.find({}).sort({"date":'desc'}).exec(function(err, allOrders) {
        if (err) {
            console.log(err);
        } else {
            //find order collection and sort it in desending order and assign it to order object  
            orders=allOrders;
            res.render("./customer/dashboard",{products:products , orders:orders});
        }
    });

});

I faced the same issue i solved it with above mechanism i recommend using empty objects instead of arrays as MongoDb returns object and also you may face some type errors when you use arrays instead of objects

function mongoConnection() {
    return new Promise((res, rej) => {
        try {
            MongoClient.connect(conn_url, {},
                function (err, client) {
                    if (err) { res(false); }
                    else {
                        promise_db_connection = new Promise((resolve, reject) => {
                            var db = client.db(Database);
                            resolve(
                                {
                                    'collection_1': db.collection("collection_1"),
                                    'collection_2': db.collection("collection_2"),
                                    'collection_3': db.collection("collection_3")
                                }
                            );
                            res(true);
                        });
                    }
                }
            );
        } catch (error) {
            res(false);
        }
    })
}

mongoConnection().then((result) => {
    console.log(result ? 'Connection Successful' : 'Connection Failed');
})

app.get('/customer', (req, res) => {
    var product = [], order = []
    promise_db_connection.then((result) => {
        result.collection_1.find({}).toArray((err, data) => {
            if (!err) product = data;
            result.collection_2.find({}).toArray((err, data) => {
                if (!err) order = data;
            })
            res.render("page", { product: product, order: order });
        })

    })
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM