2

I am trying to add a new static method to my Mongoose Model. The method should accept the names of several fields and return an object with the distinct values of each of those fields.

var mongoose = require('Mongoose');
var Schema = mongoose.Schema;
var mySchema = new Schema({Year: Number, Segment: String, 
             Sector: String, Name: String, Group: String, Value: Number});

mySchema.statics.manyDistinct = function(err, fields, callback) {
    var out = {};
    for (var i = 0; i < fields.length; i++) {
        this.distinct(fields[i], function(err, dat) {
            if (err) {
                return err;
            } else {
                out[fields[i]] = toJSON.stringify(dat); 
            }
        }); 
     }
     callback(err, out);
};

mongoose.model('myThing', mySchema);

Data example:

{Year: 2015, Segment: 'North', Sector: 'Alpha', Name: 'Bob', Group: 'Engineering', Value: 12}
{Year: 2012, Segment: 'North', Sector: 'Beta', Name: 'Joe', Group: 'Accounting', Value: 29}
{Year: 2013, Segment: 'South', Sector: 'Alpha', Name: 'Betty', Group: 'Accounting', Value: 6}

Calling the method and expected output:

myThing.manyDistinct(['Year', 'Segment', 'Sector'], function(err, result) {
    if (err) return err;
    console.log(result);
});

{Year: [2015, 2012, 2013], Segment: ['North', 'South'], Sector: ['Alpha', 'Beta']}

What I'm getting:

Error: No value for `distinct` has been declared

Please keep in mind that I'm still fairly new to JS, Node, MongoDB, and Mongoose, so I'm not 100% clear on things like the difference or situational use of lists vs arrays vs objects and some other things.


Update

Based on additional research I'm beginning to wonder if this a problem for async.map(). I definitely could use some advice here.

Here is what I think the async.map version of manyDistinct() should look like.

mySchema.statics.manyDistinct = function(fields, callback) {
    async.map(fields, function(field, callback) {
        this.distinct(field, function(err, result) {
            if (err) return callback(err);
            return {field: result};
        })
    },
    function(err, results) {
        if (err) {
            return console.log(err);
        } else {
            return results;
        }
     })
};
doicomehereoften1
  • 537
  • 1
  • 4
  • 12
  • Check [this related question](http://stackoverflow.com/questions/6043847/how-do-i-query-for-distinct-values-in-mongoose?rq=1). I'm even more unfamiliar with MongoDB and Mongoose than you are, but maybe if you change `this.distinct(` to `this.collection.disctinct(` or even `mySchema.distinct(` it would work? – Kenney Jul 28 '15 at 18:03
  • Thanks for the suggestion. I did check that one out before posting, but the Mongoose API for doing distinct has been fixed in the four years since that question was posted. Using the normal distinct method on a model using just one field (as the api requires) works just fine. – doicomehereoften1 Jul 28 '15 at 18:26
  • Is that question 4 yrs old! - I'm sorry, I should've checked. I apologize. It appears to be library specific, and I can't help you there (yet). (The error message seems to say that it just doesn't know how to do what you ask.. :-)) – Kenney Jul 28 '15 at 18:39
  • Yeah, I'm trying to translate my method into a function that I can run from the mongoshell to see if it presents the same error. That way I'll know if it's Mongo or Mongoose that's complaining. It isn't clear why there should be a problem with distinct in one context, but not in another. – doicomehereoften1 Jul 28 '15 at 18:45
  • It's puzzling. One last thing I forgot to ask: it can't be that the arguments are off, can it? For instance, I would expect `myThing.manyDistinct(myFields, fn)` to result (semantically) in `manyDistinct( err = fields, fields = callback, callback = undefined )`, – Kenney Jul 28 '15 at 19:32
  • What is the err in the function declaration: mySchema.statics.manyDistinct = function(err, fields, callback). Note that there is a function inside it too with the same parameter name. – dreamerkumar Jul 28 '15 at 20:01
  • I figured that since it was going to be a mongoose function it ought to take an error as the first argument, as the callbacks do. That very well may be the entirely wrong thing to do. – doicomehereoften1 Jul 28 '15 at 22:33

1 Answers1

0

See if this works:

mySchema.statics.manyDistinct = function(fields, callback) {
var out = {};
var error = null;
for (var i = 0; i < fields.length; i++) {
    this.distinct(fields[i], function(err, dat) {
        if (err) {
            error = error? error + ', ' + err : err;
        } else {
            out[fields[i]] = toJSON.stringify(dat); 
        }
    }); 
 }
 callback(error, out);
};

mongoose.model('myThing', mySchema);
dreamerkumar
  • 1,540
  • 1
  • 18
  • 28
  • This stops the error from occurring, however it returns an empty array, which is more baffling. – doicomehereoften1 Jul 28 '15 at 22:58
  • I was only trying to fix the syntactical errors in your code. Put debug statements in your code to figure out what is happening with your data. Is anything returned for the error field? What are you passing to the function mySchema.statics.manyDistinct. Can you paste an example of a call to this method? – dreamerkumar Jul 29 '15 at 14:54
  • Your version didn't return an error. An example of a call to the function is in the original post, second to last code block before the update I made today. When I add `console.log(dat);` to the line before it gets stringified and added to the array/object thing it only returns empty arrays. – doicomehereoften1 Jul 29 '15 at 15:35
  • Do you get all the results when you do myThing.find() Just making sure if it isn't a data assignment problem. – dreamerkumar Jul 29 '15 at 17:18
  • Yeah, running `myThing.find()`, `myThing.findOne()`, `myThing.distinct('field')` all return results as expected. Coming from SQL, I would have thought that a `distinct()` method would accept the names of many fields and return the distinct values for each field included. I was surprised to find out that isn't how MongoDB (and hence Mongoose) work. – doicomehereoften1 Jul 29 '15 at 17:46
  • But here you are trying to do distinct on one field at a time. So it should work. The toJSON.stringify call might not work because you are dealing with Mongoose objects. Mongoose documents are their own special class and not standard Javascript objects. In order to get a javascript object from it, you must use the toObject() method on it first. – dreamerkumar Jul 29 '15 at 22:26
  • Yeah, some of the other threads that I've seen have approached this using async, as I attempt in my update section. My temporary workaround actually didn't work using the for loop method and so I learned the bare minimum I needed to do the async method, which does work. Unfortunately my workaround is not a portable solution, as I'm envisioning by having a static method, so I'm not going to post it as an answer, but I do think async may be the key to solving this problem. – doicomehereoften1 Jul 30 '15 at 15:00