1

Im trying to create a query that finds a collection of documents that match a criteria, a simple search system. The problem is that the assets inside the collection are like this:

{ 
 fristName: "foo",
 lastName: "bar",
 description: "mega foo",
},
{ 
 fristName: "Lorem",
 lastName: "Ipsum",
 description: "mega Lorem bla bla",
},

If the user wants all the assets that contains the word bar i should show the asset 1 of the example, and that is not a problem, but what if he inputs foo mega?, in that case i need also to show the asset 1 because foo and mega are present in asset 1 (both of them), if he searchs only mega, the output is asset 1 and 2, and if he search mega ipsum, the result is asset 2. I have no idea how to write this query in mongoDb.

DomingoSL
  • 14,920
  • 24
  • 99
  • 173
  • 1
    Did you try to use [a text index and text search](http://docs.mongodb.org/manual/core/index-text/)? – Philipp Jan 27 '15 at 16:12

2 Answers2

3

Mongodb 2.6+ has built in support for text searches using the $text operator. Here's how to use it.

  1. Build an text index on the desired searchable fields. Note: For MongoDB 2.6 you can only have 1 text index on a collection.

    Create text index on one field

    db.test.ensureIndex({ 
        "field1" : "text"
     }, { name : "Field1TextIndex"});
    

    Create text index on two fields

    db.test.ensureIndex({ 
        "field1" : "text",
        "field2" : "text"
     }, { name : "Field12TextIndex"});
    

    Create text index for any string field

    db.test.ensureIndex({ 
        "$**" : "text" 
    }, { name : "AllTextIndex"});
    
  2. Query the collection using the $text operator.

    Here's the format for $text

    { $text: { $search: <string of keywords>, $language: <string> } }
    

Example code

Setup

use test;
var createPerson = function(f,l,d){
    return { firstName : f, lastName: l, description : d};
};
db.test.remove({});
db.test.insert(createPerson("Ben", "Dover", "Real Estate Agent"));
db.test.insert(createPerson("James", "Bond", "secret agent, ben's friend"));

Creating an text index on all string fields in a document.

db.test.ensureIndex({ "$**" : "text" }, { name : "AllTextIndex"});

Query all fields for keywords

Searching for ben

db.test.find({  
    $text : {
        $search : "ben"
    }
});

Output

{ "_id" : "...", "firstName" : "James", "lastName" : "Bond", "description" : "secret agent, ben's friend" }
{ "_id" : "...", "firstName" : "Ben", "lastName" : "Dover", "description" : "Real Estate Agent" }

The search for "ben" returned both documents since one had Ben as the firstName, and the other had ben's in the description field.

Querying for real friend produces the same result.

db.test.find({  
    $text : {
        $search : "real friend"
    }
});

More info here:

Larry Battle
  • 9,008
  • 4
  • 41
  • 55
2

Possible solution is to search via keywords. I mean you have to add keywords field to each object, like:

{ 
 fristName: "foo",
 lastName: "bar",
 description: "mega foo",
 keywords: ["foo", "bar", "mega"]
},
{ 
 fristName: "Lorem",
 lastName: "Ipsum",
 description: "mega Lorem bla bla",
 keywords: ["mega", "Lorem", "Ipsum", "bla"]
},

The you have to split request string to keywords, e.g.

"foo mega"

will be converted into

["foo", "mega"]

and then you can search objects by keywords field.

Alexander Perechnev
  • 2,797
  • 3
  • 21
  • 35
  • I cant modify the structure of the assets, so including the field keywords is not possible – DomingoSL Jan 27 '15 at 16:18
  • 1
    Without considering the "stemming" options of Full Text Search then I consider this to be a very valid option. Certainly a common sense approach. – Neil Lunn Jan 27 '15 at 16:19
  • 1
    @DomingoSL But the first rule of working with data is to normalize it properly. So it is up to you :). – Alexander Perechnev Jan 27 '15 at 16:19
  • 1
    @DomingoSL In this domain "I can't modify" is not an acceptible response. Talk to your boss, who clearly needs to be educated. Send him my way. – Neil Lunn Jan 27 '15 at 16:20