Ilmar
Ilmar

Reputation: 97

Text search with Meteor and Mongo 2.6

I'd like to perform the equivalent of this Mongo shell command in meteor(server-side, of course):

db.articles.find(
  { $text: { $search: "apple pie" } },
  { score: { $meta: "textScore" } }
).sort( { score: { $meta: "textScore" } } ).limit(10)

I have been able to do:

return Articles.find( { $text: { $search: "apple" } },
{ sort: {"name":1}, limit:20});

However, searching for "pie apple" doesn't work - it only does exact matching. Neither does trying to sort by score.

I am using mongo 2.6.3 with a text index on the name field in articles. Searching from within mongo shell works perfectly.

Also, has anyone successfully implemented a text search with a different approach? My database has 10k entries and I only need to search within a single field, and return 20 best matches.

Upvotes: 2

Views: 990

Answers (2)

Flavien Volken
Flavien Volken

Reputation: 21299

3 steps (meteor 1.0.4+) now using MongoDB 3.0. Assuming you already have the YourCollection collection i.e.

YourCollection = new Meteor.Collection("yourCollection");

A. Index your collection (server side) here below is how to index all the fields, more info here

Meteor.startup(function (){
        YourCollection._ensureIndex(
                {"$**": "text"},
                {"name": "searchIndex"}
        ); }

B. Create the publication (server side)

Meteor.publish("search-yourCollection", function(searchField)
{
    return  YourCollection.find({"$text": {"$search": searchField}},
    {
        fields: {
            score: {$meta: "textScore"}
        },
        sort: {
            score: {$meta: "textScore"}
        }
    });
});

C. Subscribe to the publication and find (client side)

var whatToSearch = "abc"; // can be taken out of the session
Meteor.subscribe("search-yourCollection", whatToSearch);
var results = YourCollection.find({score:{"$exists":true}});

Remark: The publication will add a score property to all the returned items. Ensuring this property exists within the find function {"$exists":true} will make sure you are finding the elements returned by the search-yourCollection publication. This is mandatory if you are subscribing to another publication adding items into YourCollection published set.

Upvotes: 3

Ilmar
Ilmar

Reputation: 97

I've been able to get this to work using something similar to: Implementing MongoDB 2.4's full text search in a Meteor app

The differences are:

MongoInternals.defaultRemoteCollectionDriver().mongo.db.executeDbCommand

and the searchDinosaurs function in the link above looks like:

if (query && query !== '') {
var searchResults = _searchArticles(query);
var results = [];
for (var i = 0; i < searchResults.length; i++) { 
    results.push({
      id: searchResults[i].obj._id,
      score: searchResults[i].score});
}

var ids = [];

results.sort(function(a,b) { return a.score < b.score } );

for (var i = 0; i < 20; i++) {
  if (results[i]!=null){
    ids.push(results[i].id);
  }
}

return ids;

Here I'm sorting the results on score and returning the top 20 id's. The only issue is, once the user is subscribed to these 20 articles, I have to find and sort them once again client-side using regex search in minimongo. If anyone has suggestions or improvements, I'd love to hear them.

Upvotes: 1

Related Questions