I am attempting to provide an API to search a MongoDB collection on various criteria, including a full-text search. Since this is a Scala project (in Play FWIW), I am using Salat, an abstraction around Casbah.
The following code works fine:
MySalatDao
.find(MongoDBObject("$text" -> MongoDBObject("$search" -> "Vidya")), MongoDBObject("score" -> MongoDBObject("$meta" -> "textScore")))
.sort(orderBy = MongoDBObject("score" -> MongoDBObject("$meta" -> "textScore")))
However, I will eventually need to search on multiple criteria and sort the results by their full-text search score, so I explored Casbah's MongoDBObject query builder functionality (at bottom).
So I tried to replicate the above like this:
val builder = MongoDBObject.newBuilder
builder += "$text" -> MongoDBObject("$search" -> "Vidya")
builder += "score" -> MongoDBObject("$meta" -> "textScore")
MySalatDao
.find(a.result())
.sort(orderBy = MongoDBObject("score" -> MongoDBObject("$meta" -> "textScore")))
This gives the following exception:
com.mongodb.MongoException: Can't canonicalize query: BadValue must have $meta projection for all $meta sort keys
at com.mongodb.QueryResultIterator.throwOnQueryFailure(QueryResultIterator.java:214)
at com.mongodb.QueryResultIterator.init(QueryResultIterator.java:198)
at com.mongodb.QueryResultIterator.initFromQueryResponse(QueryResultIterator.java:176)
at com.mongodb.QueryResultIterator.<init>(QueryResultIterator.java:64)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:86)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:66)
.
.
I've seen this error before--when I didn't include the score component in the query. But once I did that, it worked (as seen in the first code snippet), and I thought the version with the query builder is equivalent.
For that matter, a call to builder.result().toString() produces this:
{ "$text" : { "$search" : "Vidya"} , "score" : { "$meta" : "textScore"}}
Any help with getting the query builder to work for me would be much appreciated.
In your working query you are passing one DBObject for the "query predicate" and a second DBObject for "fields" or "projection" - find takes a second optional argument indicating which fields to return and in case of $text search, there is a special projection field $meta which allows you to get back the score of the matched document so that you can sort on it.
In your builder attempt you are adding the projection DBObject to the query criteria, and that gives you the same error you saw before when omitting the score component as the second argument to find.
Add MongoDBObject("score" -> MongoDBObject("$meta" -> "textScore")) as the second argument to find like you were doing before, and use builder for combining multiple query criteria.
In simple JSON terms, you are calling find like this:
db.coll.find( { "$text" : { "$search" : "Vidya"} , "score" : { "$meta" : "textScore"}} )
when you really want to call it like this:
db.coll.find( { "$text" : { "$search" : "Vidya"} } , { "score" : { "$meta" : "textScore"}} )
Related
what is wrong with this query ?
db.collection.find( { "name" : "/^test$/i", "group" : "/^Default$/i"} )
I am trying to find an object with name=test, group=default, but not case sensitive.
but I am not getting the result although I know I have this document in the database:
I used exactly as in mongo website it's explained:
In MongoDB, you can also use regular expression objects (i.e. /pattern/) to specify regular expressions:
{ <field>: /pattern/<options> }
The query in its essence is right, you just have a minor syntax error.
In javascript (Which Mongo shell is based on) a regex is of the form of /xxx/ and not "/xxx/", the ladder being a string expression.
So just change your query into this:
db.collection.find( { "name" : /^test$/i, "group" : /^Default$/i} )
I am new to Morphia and am trying to update a field in embedded document. This is the current structure
class A {
List<B> BList;
}
class B {
String field;
}
So My structure looks like the following in MongoDb :
{
"_id" : ObjectId("5bab8be0032945f6e9f91d98"),
"className" : "com.abc.A",
"BList" : [
{
"B" : {
"field" : "text"
}
}
}
Now I want to update B.field for all the matching queries.
I created the following UpdateOperations
UpdateOperations updateOps = datastore.createUpdateOperations(A.class);
my filter query was fine say filter returning me all elements in A.
updateOps.set("Blist.$[].B.field", "newtext");
when debugger reached this statement it shows org.mongodb.morphia.query.ValidationException: Could not resolve path 'BList.$[].B.field' against A`
I even added disableValidation But this doesnot run.
However if I ommit the positional operator it works fine but when on runnig datastore.update() it fails.
throwing this error.
Write failed with error code 28 and error message 'Cannot create field B.
Can Anyone suggest how to do this updation on second level in morphia ?
Hello I need some help with MongoDB:
My Document has the following property (generated by an Map with spring):
"filter":{"billingAccount_id":["multisim5"],"simulate":["true"]}
and i'm trying to find the document with the this code (generated by spring)
query.addCriteria(Criteria.where("filter").all(getMapWithValues()));
which results in
"filter" : { "$all" : [{ "billingAccount_id" : ["multisim5"] }] }
but i'm getting no result. What's to do here?
Thanks for help in advance
You're applying $all on an object (filter) instead of an array (filter.billingAccount_id). Change your request (and method getMapWithValues) to get something like :
{"filter.billingAccount_id" : { "$all" : ["multisim5"] }}
BUT $all is used to find array that contains, at least, all values provided in query. Since there's an unique element in $all param, $all is useless here, and the following query output the same result :
{"filter.billingAccount_id" : "multisim5"}
EDIT : if you want to query array with exact match ("multisim5" and nothin else), use the following :
{"filter.billingAccount_id" : { "$eq" : ["multisim5"] }}
I have created a collection and added just a name field and tried to apply the following index.
db.names.createIndex({"name":1})
Even after applying the index I see the below result.
db.names.find()
{ "_id" : ObjectId("57d14139eceab001a19f7e82"), "name" : "kkkk" } {
"_id" : ObjectId("57d1413feceab001a19f7e83"), "name" : "aaaa" } {
"_id" : ObjectId("57d14144eceab001a19f7e84"), "name" : "zzzz" } {
"_id" : ObjectId("57d14148eceab001a19f7e85"), "name" : "dddd" } {
"_id" : ObjectId("57d1414ceceab001a19f7e86"), "name" : "rrrrr" }
What am I missing here.
Khans...
the way you built your index is correct however building an ascending index on names wont return the results in ascending order.
if you need results to be ordered by name you have to use
{db.names.find().sort({names:1})}
what happens when you build an index is that when you search for data the Mongo process perform the search behind the scenes in an ordered fashion for faster outcomes.
Please note: if you just want to see output in sorted order. you dont even need an index.
You won't be able to see if an index has been successfully created (unless there is a considerable speed performance) by running a find() command.
Instead, use db.names.getIndexes() to see if the index has been created (it may take some time if you're running the index in the background for it to appear in the index list)
I would like to perform a query using casbah in order to find all objects that have a certain field not set (the field does not exist) or the field has a particular value.
I have tried using
val query = ("_id.serviceName" $in serviceNames) ++ ($or("element" $exists false), MongoDBObject("element" -> "value")))
but I obtain an error:
found com.mongodb.casbah.commons.Imports.DBObject
required (String, Any)
Is it possible to express such query?
Thanks
Looks like this may be a bug in the right-hand value filter for $or; it doesn't appear to be accepting a preconstructed DBObject from the $exists DSL statement. It definitely should --- I'm filing a bug internally to fix this; in the meantime you can construct this by doing the "$exists" statement by hand:
scala> val query = ("_id.serviceName" $in serviceNames) ++ ($or(("element" -> MongoDBObject("$exists" -> false)), ("element" -> "value")))
query: com.mongodb.casbah.commons.Imports.DBObject = { "$or" : [ { "element" : { "$exists" : false}} , { "element" : "value"}] , "_id.serviceName" : { "$in" : [ "foo" , "bar" , "baz" , "bah"]}}
Sorry for the trouble... I've created a bug entry for this to correct for the next release.