Reputation: 1245
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
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
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
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