Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB Java API: Full-text search

I have a problem using Java API for MongoDB. I have created a query using Robomongo:

db.collection.find(
    {$text : {$search : "\"expression\" keyword"}},
    {score : {$meta : "textScore"}}
).sort({score : {$meta : "textScore"}})

Now I would like to create the same query using Java API:

DBObject searchCommand = new BasicDBObject(
    "$text", new BasicDBObject("$search", "\"expression\" keyword")
).append(
    "score", new BasicDBObject("'$meta'", "textScore")
);

DBObject sorting = new BasicDBObject(
    "score", new BasicDBObject("'$meta'", "textScore")
);

DBCursor result = collection.find(searchCommand).sort(sorting);

The problem is that this code does not work. The query:

DBObject searchCommand = new BasicDBObject(
    "$text", new BasicDBObject("$search", "\"expression\" keyword")
);

works perfectly. After appending the second part all results become invisible. What's more, this line:

DBCursor result = collection.find(searchCommand).sort(sorting);

throws MongoException (BadValue bad sort specification). When I remove sort() method invocation the Exception is not present, but still I don't have any results (if I append "score").

I have found a solution to this problem, but using Spring. I wouldn't like to use any other libraries. Also, I am a beginner in MongoDB. Thank you for your help and time, cheers.


UPDATE. Problem solved. Appending "score" to query searchCommand passed as first parameter of find() is wrong. "Score" should be passed in separate DBObject as the second parameter of find() method as follows:

DBObject search = new BasicDBObject(
    "$text", new BasicDBObject("$search", "\"expression\" keyword")
);

DBObject project = new BasicDBObject(
    "score", new BasicDBObject("$meta", "textScore")
);

DBObject sorting = new BasicDBObject(
    "score", new BasicDBObject("$meta", "textScore")
);

DBCursor result = collection.find(search, project).sort(sorting);
like image 984
bargro Avatar asked Dec 12 '15 22:12

bargro


1 Answers

You got really close, with what you have tried.

Note that in,

db.collection.find(
    {$text : {$search : "\"expression\" keyword"}},
    {score : {$meta : "textScore"}}
).sort({score : {$meta : "textScore"}})
  • {$text : {$search : "\"expression\" keyword"}} - is the query portion.

  • {score : {$meta : "textScore"}} - is the projection part.

In what you have tried to implement using the Java driver,

DBObject searchCommand = new BasicDBObject(
    "$text", new BasicDBObject("$search", "\"expression\" keyword")
).append(
    "score", new BasicDBObject("'$meta'", "textScore")
);

would end up producing,

{$text:{$search:"\"expression\" keyword"},"score":{"meta":"textscore"}}

which is not equivalent to the native query. Even the intended projection statement has been a part of the query itself.

Note that, this ends up looking for a field named score, since it has now become a part of the query and not the projection.

You can easily modify your DBObject instances, to make it a part of the projection parameter and it would work:

DBObject findCommand = new BasicDBObject(
    "$text", new BasicDBObject("$search", "keyword")
);

DBObject projectCommand =  new BasicDBObject(
    "score", new BasicDBObject("$meta", "textScore"));

DBObject sortCommand = new BasicDBObject(
    "score", new BasicDBObject("$meta", "textScore")
);
DBCursor result = collection.find(
                                  findCommand ,projectCommand)
                                  .sort(sortCommand );
like image 77
BatScream Avatar answered Nov 15 '22 06:11

BatScream