6

My first attempt at building something with Angular + express + mongodb, so I'm probably going about this completely the wrong way. Express is being used to serve up json. Angular then takes care of all the views etc.

I'm using Mongoose to interact with Mongo.

I have the following database schema:

var categorySchema = new mongoose.Schema({
  title: String, // this is the Category title
  retailers : [ 
    {
      title: String,  // this is the retailer title
      data: {       // this is the retailers Data
        strapLine: String,
        img: String ,  // this is the retailer's image
        intro: String,
        website: String,
        address: String,
        tel: String,
        email: String
      } 
    }
  ]
});

var Category = mongoose.model('Category', categorySchema);

and in Express I have a couple of routes to get the data:

 app.get('/data/categories', function(req, res) {
   // Find all Categories.
   Category.find(function(err, data) {
     if (err) return console.error(err);
     res.json(data)
   });
 });


 // return a list of retailers belonging to the category
 app.get('/data/retailer_list/:category', function(req, res) {
   //pass in the category param (the unique ID), and use that to do our retailer lookup
   Category.findOne({ _id: req.params.category }, function(err, data) {
     if (err) return console.error(err);
     res.json(data)
   }); 
 });

The above works - I'm just having big problems trying to get at a single retailer. I'm passing the category, and retailer id through... I've tried all sorts of things - from doing a find on the category, then a findOne on the contents within... but I just cant get it to work. I'm probably going about this all wrong...

I found this thread here: findOne Subdocument in Mongoose and implemented the solution - however, it returns all my retailers - and not just the one I want.

// Returns a single retailer
app.get('/data/retailer_detail/:category/:id', function(req, res) {
  //pass in the category param (the unique ID), and use that to do our retailer lookup
 Category.findOne({_id: req.params.category , 'retailers.$': 1}, function(err, data) {
    console.log(data);
    if (err) return console.error(err);
    res.json(data)
  }); 
});    

Thanks, Rob

Community
  • 1
  • 1
Rob
  • 1,576
  • 3
  • 22
  • 52
  • To use the projection operator `$`, you need to query something in the array as well. http://docs.mongodb.org/manual/reference/projection/positional/#proj._S_ – WiredPrairie Sep 19 '13 at 01:21
  • How were you narrowing it to one retailer? – WiredPrairie Sep 19 '13 at 01:22
  • thanks for the replies :) I'm passing in the :category through the app.get - so: app.get('/data/retailer_detail/:category/:id', function(req, res) {.... I'm trying to do a find - locate the category document, and then search for a single retailer within it – Rob Sep 19 '13 at 01:26
  • Why haven't you added the search for the retailer as part of the query to `findOne`? – WiredPrairie Sep 19 '13 at 01:36
  • hehe - good point :) 'cos I' don't really know what I'm doing. I've updated the last code block above to do that - but I still cant get it to work - it just logs out "null" – Rob Sep 19 '13 at 01:51
  • How are you matching a specific retailer? That's what you'd need to add there, and then as the projection use the `retailer.$` syntax. – WiredPrairie Sep 19 '13 at 02:13

2 Answers2

9

Now that I see your full filter/query, you should be able to use the array positional operator in this case as part of the projection rather than doing client side filtering:

app.get('/data/retailer_detail/:category/:id', function(req, res) {
  //pass in the category param (the unique ID), and use that to do our retailer lookup
 Category.findOne({
    /* query */
    _id: req.params.category , 
    'retailers._id' : req.params.id
  },
  {  /* projection */
     "retailers.$" : 1 
  }, 
  function(err, data) {
  var retailer = _.where(data.retailers , { id : req.params.id });
    if (err) return console.error(err);
    res.json(retailer)
  }); 
}); 

For the { "retailers.$" : 1 } to work properly, the query must include a field from an element in the array. The $ operator returns the first match only.

WiredPrairie
  • 58,954
  • 17
  • 116
  • 143
  • Thanks - this has made it a little easier to do further manipulations - add the catgegory ID back into the retailer object :) thanks – Rob Sep 19 '13 at 21:52
1

The guys next door use Mongo + Express and gave me some pointers: they explained to me how mongo worked, and advised I should use underscore.js to assist with my filter.

They said I needed to pull the entire category out - and then run the filter. I don't strictly need , 'retailers._id' : req.params.id} but they said to leave it in as it guaranteed that the category would only be returned if an item within it contained that information. I still don't really know why or how... So can't really mark this as solved.. it it solved, but I don't really get why as yet - so will do more reading :)

app.get('/data/retailer_detail/:category/:id', function(req, res) {
  //pass in the category param (the unique ID), and use that to do our retailer lookup
 Category.findOne({_id: req.params.category , 'retailers._id' : req.params.id}, function(err, data) {
  var retailer = _.where(data.retailers , { id : req.params.id });
    if (err) return console.error(err);
    res.json(retailer)
  }); 
}); 
Rob
  • 1,576
  • 3
  • 22
  • 52