I am converting filters from the client to a QueryImpl using the setQueryObject method.
When I try to add another complex criteria to that query my original query is moved to a field named baseQuery and the new criteria is the query field.
When the query is executed only the new criteria is used and the baseQuery is not used.
It only happens when the client query is formatted like this: { "$or" : [{ "field1" : { "$regex" : "value1", "$options" : "i"}},...]}
and the new criteria is formatted in the same way(meaning an $or operation).
It seems that when I try to merge 2 $or queries it happens but when I merge $or with $and it concatenates the queries properly.
Am I using it wrong or is it a genuine bug?
Edit:
Code:
public static List<Entity> getData(client.Query query) {
QueryImpl<Entity> finalQuery = Morphia.realAccess().extractFromQuery(Entity.class,query);
finalQuery.and(finalQuery.or(finalQuery.criteria("field").equal(false), finalQuery.criteria("field").doesNotExist()));
return finalQuery.asList();
}
public <E> QueryImpl<E> extractFromQuery(Class<E> clazz, client.Query query) {
QueryImpl<E> result = new QueryImpl<E>(clazz,this.db.getCollection(clazz),this.db);
result.setQueryObject(query.getFiltersAsDBObject);
return result;
}
QueryImpl and setQueryObject() are internal constructs. You really shouldn't be using them as they may change or go away without warning. You should be using the public query builder API to build up your query document.
I am having the same problem, It seems to be working when I do this:
finalQuery.and();
finalQuery.and(finalQuery.or(finalQuery.criteria("field").equal(false), finalQuery.criteria("field").doesNotExist()));
It is kind of ugly and would love to hear if someone have a different approach, but the only way I was able to find to convert the data from the client side which is a DBObject is to use the setQueryObject() of QueryImpl.
Related
Using MongoDb and #RepositoryRestController and Pageable from Spring.
Controller method(passing in -- ?page=0&size=20&sort=events.field1,asc):
..WithCreator(#RequestBody ..., #ParameterObject final Pageable page)
I'm having a hard time finding out how to search for a nested array of objects' field using the Spring Pageable object.
I can sort in mongo by the array of objects' field. eg.:
// a document in the db
{
events:[
{field1:"Test"}
]
}
//can sort the above document like so:
{ "$sort" : { "events.field1" : -1}}
The problem is, the Pageable object actually looks through the persistent entity and filters out fields that it doesn't find in there.
In this case of "events.field1" the Sort object translates to UNSORTED in the end. It does find the "events" field, but that's where it ends.
Question:
Is there a way with Pageable around this, other than passing the sort as a separate query field, or am I missing something?
The translation/field filtering of the original "raw" Sort object happens here:
class org.springframework.data.rest.webmvc.json JacksonMappingAwareSortTranslator..
public Sort translateSort(Sort input, PersistentEntity<?, ?> rootEntity){..}
I am working on a nodejs/express app with Mongodb on the backend. In one of my API calls, depending on the presence of a particular querystring parameter or the other I want to issue a query to Mongodb with either a $gt or a $lt.
In some cases we want to ask for everything less than the tokenId using $lt, but in other cases we want everything greater than the tokenId using $gt. How do we do that without duplicating the queries?
Here's an example query:
collection.find({'film_id': {$in : genre}, '_id': {$lt: tokenId}}).sort({'_id': -1}).limit(25).toArray(function(error, films)
Is there a way to dynamically create the query without actually doing 2 different queries?
Build up your query object programmatically:
var query = {'film_id': {$in : genre}};
if (param) {
query._id = {$lt: tokenId};
} else {
query._id = {$gt: tokenId};
}
collection.find(query).sort({'_id': -1}).limit(25).toArray(function(error, films);
Update
Now that Node.js 4+ supports computed property names, you can create query in one step as:
var query = {
film_id: {$in: genre},
_id: {[param ? '$lt' : '$gt']: tokenId}
};
The Above code may not work. The above dynamic query will be resolved to following Ex. Token is an integer 35.
collection.find("_id":"{$gt: 35}")
.sort({'_id': -1}).limit(25).toArray(function(error, films);
resulting in Error
The correct syntax should be:
collection.find("_id":{$gt: 35}).
The double "" quotes should not be there.
I'm trying to put this into morphia query :
db.woot.find({
"bar.tables": {
$elemMatch: {
"tableId": {
$in: [3,
4]
},
"tab": {
$gte: 20000
}
}
}
})
So I have :
Query<Table> q
q.field("bar.table").hasThisElement()
And after this I don't know how to finish the query and still using FieldEnd which supports in(), gte() methods without writing whole query myself with BasicDBObjects.
Please help me transform above query to the nicest possible Morphia equivalent.
EDIT: bar.tables is an array so matching must be done with elemMatch or else it can match first condition from some element and the second condition from the other element, but only elements matching both conditions are valid.
Morhpia now has elemMatch so you can execute your original query.
Query<Example> tableQuery = mongoDatastore.createQuery(Example.class)
.disableValidation()
.field("tableId").hasAnyOf(Arrays.asList(3, 4))
.field("tab").greaterThanOrEq(20000);
Query<Table> query = getCDB().getDatastore()
.createQuery(Table.class)
.field("bar.table").elemMatch(tableQuery);
Something along these lines should work.
I would try something like this:
Query<Table> query = mongoDataStore.find(Table.class)
.field("bar.table.tableId").hasAnyOf(tableIdArrayList)
.field("bar.table.tab").greaterThan(20000);
You'll probably need some custom query builder, where you can set some conditions and it will then put together the right query for you — at least that's our approach.
I have an issue with a MongoDB query that I am having challenges with. In my class, I have a structure of [instance].events.destination.estimatedDateTime, and the value of estimatedDateTime is an ISO Date (eg. 2014-09-23 21:48:00.000Z). We are using org.springframework.data.mongodb.core.query.Criteria to append criteria to our query like this:
criteria = criteria.and("events.destination.estimatedDateTime").gte(today)
When this is generated, it looks like this when evaluated at runtime:
"events.destination.estimatedDateTime" : { "$gte" : { "$date" : "2014-10-28T05:00:00.000Z"} }
Unfortunately that is returning no records. I have taken that same condition and attempted to pull records in RoboMongo, but again there are no results. However, I have found that when I do the following in RoboMongo, I do indeed get results
"events.destination.estimatedDateTime" : { $gte : ISODate("2014-10-28T05:00:00.000Z") }
Is there a way using the Criteria class to generate ISODate(date) rather than "$date":date?
The mongo docs specify that you can specify a query hint for count queries using the following syntax:
db.orders.find(
{ ord_dt: { $gt: new Date('01/01/2012') }, status: "D" }
).hint( { status: 1 } ).count()
Can you do this using the mongo template? I have a Query object and am calling the withHint method. I then call mongoTemplate.count(query); However, I'm pretty sure it's not using the hint, though I'm not positive.
Sure, there are a few forms of this including going down to the basic driver, but assuming using your defined classes you can do:
Date date = new DateTime(2012,1,1,0,0).toDate();
Query query = new Query();
query.addCriteria(Criteria.where("ord_dt").gte(date));
query.addCriteria(Criteria.where("status").is("D"));
query.withHint("status_1");
long count = mongoOperation.count(query, Class);
So you basically build up a Query object and use that object passed to your operation, which is .count() in this case.
The "hint" here is the name of the index as a "string" name of the index to use on the collection. Probably something like "status_1" by default, but whatever the actual name is given.