Chuck M
Chuck M

Reputation: 1245

Include score in Morphia full text search

I am trying to use the MongoDB full text indices in Morphia. I need to return the score for each documents as well as have the results sorted. This is what my query looks like without Morphia:

   db.getCollection('disease').find( { $text: { $search: "brain" } }, 
                                     { score: { $meta: "textScore" } } )
                              .sort( { score: { $meta: "textScore" } } )

This works correctly and returns hits sorted by score.

I also can do this using the MongoDB Java driver directly without Morphia.

    // search with the Java driver
    BasicDBObject textSearch = new BasicDBObject("$search", "brain");
    BasicDBObject search = new BasicDBObject("$text", textSearch);

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

    List<DBObject> diseases = collection.find(search, score).sort(score).toArray();
    Assert.assertEquals(2, diseases.size());
    Assert.assertEquals("brain", diseases.get(0).get("name"));
    Assert.assertEquals("benign-brain", diseases.get(1).get("name"));

I can't figure out how to accomplish the same thing in Morphia. Here is the example from the Morphia documentation (http://mongodb.github.io/morphia/1.0/guides/querying/#text-searching):

List<Greeting> good = datastore.createQuery(Greeting.class)
                             .search("good")
                             .order("_id")
                             .asList();
Assert.assertEquals(4, good.size());

The example does not return score and is ordering by "_id". I don't see any way to handle the $meta operator in Morphia. Has anyone done something similar?

Upvotes: 5

Views: 1277

Answers (3)

M Poornima
M Poornima

Reputation: 151

Looks like this issue has been fixed with the commit https://github.com/mongodb/morphia/commit/af4d64f6de3c0b1437dd216f5762d03bf98cdcb0 and now you can just do:

List<Greeting> good = datastore.createQuery(Greeting.class)
                                .search("good")
                                .project(Meta.textScore("score"))
                                .order(Meta.textScore("score"))

The only caveat now is that as the project on score is mandatory to be able to sort by score, if no other projections are added, the result just contains the score field. So, projections for all needed fields have to be added into the query.

Hope this helps.

Upvotes: 0

ThinkBonobo
ThinkBonobo

Reputation: 16515

Following the advice from @evanchooly and the OP, I was able to resolve my version of this issue as follows:

first I created the query anlong with the search.

Datastore morphiaDS = ...;
Query<myMorphiaModel> query = morphiaDS.createQuery(myMorphiaModel.class)
                .field("helloField").equal("world")
                .search("yadayadayada"); // Search performs a text search

Next, I transitioned to using the direct Java Mongo drivers. It seems right now, while this issue by @evanchooly is still open, We'll need to use mongo drivers and can't go pure morphia.

BasicDBObject meta = new BasicDBObject("$meta", "textScore");
BasicDBObject score = new BasicDBObject("score", meta);
List<DBObject> results = query.getCollection()
            .find(query.getQueryObject(), score)
            .sort(score).toArray();

Finally I converted the list of generalized objects to my original morphia model

Morphia morphia = mongoService.getMorphia();
List<myMorphiaModel> searchDocs = results.stream()
            .map((result) -> morphia.fromDBObject(myMorphiaModel.class, result))
            .collect(Collectors.toList());

One troubleshooting issue to watch out for is that I had to make sure I had 'score' represented in the find, not just in the sort.

Hope this clarifies the helpful answers that others have presented....

Upvotes: 0

evanchooly
evanchooly

Reputation: 6233

Because Morphia maps back to your type, the only way to expose the score in this situation would be to add a score field to your entity type and map it back to that field. This isn't awesome because it starts to pollute your business types with database metadata fields. You could always try mapping back out a special value object type containing whatever metadata you'd like. That would at least keep your business objects free of this metadata.

Upvotes: 2

Related Questions