8

I am using Mongoose in a MEAN environment. How can I make sure to not have any duplicate results in my result set? Example: my database contains 10 (partly duplicate) names:

  • Allan
  • Allan Fourier
  • Allan
  • Allan Maxwell
  • Allan
  • Allan Foo
  • Allan Whatever
  • Allan Whoever
  • Allan Smith
  • Allan Rogers

When querying this database for 'Allan' or maybe even just 'all' (using .find(regex...) and limiting the number of returned results to 5, I get this:

  • Allan
  • Allan Fourier
  • Allan
  • Allan Maxwell
  • Allan

Having three duplicate entries of 'Allan', we waste a lot of result-diversity (talking about an autocomplete function for a search input field). I need the returned result set free of duplicates, such as:

  • Allan
  • Allan Fourier
  • Allan Maxwell
  • Allan Foo
  • Allan Whatever

How can that be achieved using mongoose, if at all?

Igor P.
  • 1,407
  • 2
  • 20
  • 34

3 Answers3

13

You can use find to establish the query and then chain a call to distinct on the resulting query object to get the unique names in the result:

var search = 'Allan';
Name.find({name: new RegExp(search)}).distinct('name').exec(function(err, names) {...});

Or you can combine it all into a call to distinct on the model, providing the query object as the second parameter:

var search = 'Allan';
Name.distinct('name', {name: new RegExp(search)}, function(err, names) {...});

In both cases, names is an array of just the distinct names, not full document objects.

You can also do this with aggregate which would then let you directly limit the number of results:

Name.aggregate([
    {$match: {name: new RegExp(search)}},
    {$group: {_id: '$name'}},
    {$limit: 5}
])
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • Thanks for that but is there also a way to limit the amount of results as I can't use distinct() and .limit together :( – Igor P. May 05 '15 at 13:41
  • @IgorP. Not directly. But you can easily take just the first N elements from the array of names. e.g. `names.slice(0, 5)`. – JohnnyHK May 05 '15 at 13:52
  • I see. Still, I'd like to avoid querying the entire database for every request for performance reasons. Too bad there is no native way to combine .distinct() and limit. Any other suggestions how to get a limited amount of dinstinct results for a regex search? – Igor P. May 08 '15 at 12:03
  • 1
    @IgorP. You can also use `aggregate` to do this which does let you limit the results. See the updated answer. – JohnnyHK May 08 '15 at 12:57
  • How would you get the object, not just the value of the field? I want to do a `.find()` and only return the ones if the value for a field is different then the other ones in the others. like if I have a `name` field and some docs have the same name, I want to only return the doc 1 for the name. and the same for all other docs with different `name` values – jack blank Feb 04 '17 at 23:24
0

You can use MongoDB's distinct() query to find only distinct values (i.e., unique) in your set. Per the API docs, distinct can be used with Mongoose.

Their example:

{ "_id": 1, "dept": "A", "item": { "sku": "111", "color": "red" }, "sizes": [ "S", "M" ] }
{ "_id": 2, "dept": "A", "item": { "sku": "111", "color": "blue" }, "sizes": [ "M", "L" ] }
{ "_id": 3, "dept": "B", "item": { "sku": "222", "color": "blue" }, "sizes": "S" }
{ "_id": 4, "dept": "A", "item": { "sku": "333", "color": "black" }, "sizes": [ "S" ] }

With db.inventory.distinct( "dept" ) will return [ "A", "B" ]

Aweary
  • 2,302
  • 17
  • 26
  • But distinct() is "native MongoDB", thus skipping Mongoose (which I don't want to) or can I combine that somehow? – Igor P. May 04 '15 at 00:42
  • Look at the Mongoose docs that I linked, they provided access to the base `distinct` method. You can find it here: http://mongoosejs.com/docs/api.html#query_Query-distinct – Aweary May 04 '15 at 00:42
  • I see, but that still does not work with regular expressions inside (which I use), right? 'Name.find().distinct({name: new RegExp()});' gives me a TypeError – Igor P. May 04 '15 at 00:48
  • You can feel free to post your code, but it doesn't seem you are using Mongoose right. If you want to match a `RegExp`, you should be using the `$regex` method. – Aweary May 04 '15 at 00:53
  • Oh ok. This is my query: var searchQuery = Name.find().distinct({name: new RegExp("...")}); Wrong? – Igor P. May 04 '15 at 01:01
0

You can filter the search result which is array using method suggested here:

Delete duplicate from Array

Community
  • 1
  • 1
vishal patel
  • 832
  • 1
  • 7
  • 21
  • Sorry, but that's not what I am looking for. If possible I'd like to always return 5 (unique) results. If I query the database for 5 results and then filter the result set manually - using your approach - then I end up with only 3 results (sticking to my example). The filtering must therefore be initiated natively on the database layer. – Igor P. May 04 '15 at 00:54