I've below mongoDB aggregation which is filtering tests array in my mongodb collection item.
Sample collection item : {,...tests:[ {} , {"someField":"yesIamHere"} ] }
Below query worked well and returned only tests collection which contains someField
db.getCollection('yourcollection')
.aggregate([
{"$match": {"tests.someField": {"$exists": true}}},
{ $project:{"tests": {"$filter": {"input": "$tests", "as": "item",
"cond": {"$ne": ["$$item.someField", undefined]}}}}
},
])
However,
While using java BasicDBObject is taking "undefined" as string not JS undefined
BasicDBObject projectionFilterInput=new BasicDBObject("input","$tests")
.append("as", "item")
.append("cond",new BasicDBObject("$ne", Arrays.asList("$$item.someField","undefined")));
So, this interprets "cond": {"$ne": ["$$item.vidaptorCode", "undefined"]}}}} "undefined" not undefined. So, this doesn't filter the items as intended.
Is there any constant defined for this specific undefined value in mongodb java driver base ? This is the main question.
Whoever curious...
Why am I not using ODM ?
Actually we do use Spring Data for MongoDB, but it doesn't support this aggregation cond.
MatchOperation matchStage = Aggregation.match(new Criteria("tests.someField").exists(true));
ProjectionOperation projection = Aggregation.project("tests");
Aggregation aggregation
= Aggregation.newAggregation(matchStage, projection);
AggregationResults<LabConfiguration> output
= mongoTemplate.aggregate(aggregation, "yourcollection", YourClass.class);
Morphia ODM
I liked Morphia fluent syntax however they're using different annotations than Spring Data Mongo and its dependent MongoDB libs are different. In short, two ODM don't work together.
The biggest problem is for repository implementation you need to implement BasicDAO<C,K> and it's not very practical, it's mongo oriented and Spring Data Mongo does great job with MongoRepository<C,K>
Projections filterProjection = projection(
"tests",
expression(
"$filter",
new BasicDBObject("input","$tests")
.append("as", "item")
.append("cond",new BasicDBObject("$ne", Arrays.asList("$$item.someField","undefined")))
)
);
Hence, I ended up with Mongo driver base syntax for this problem that's why I need to pass undefined to BasiDBObject but not as a string covered by double quotes.
I'm also open to hear your overall advices. What we have now is QueryDSL and Spring Data for MongoDB.
As commented by others in my OP, the alternative is using $ifNull.
Actually, I was expecting using an {extists:true} but it's not a valid aggregation cond operator or something like $ifNotNull would be nice, however it's also achievable with $ifNull.
As you know projection works with 0 and 1, i.e. {name:1,_id:0}
So, I decided to return 0 when $ifNull and it worked!
Java MongoDB Core Driver way
BasicDBObject projectionFilterInput=new BasicDBObject("input","$tests")
.append("as", "item")
.append("cond",new BasicDBObject("$ifNull",Arrays.asList("$$item.someField",0)));
Related
I am struggling with a mongo query. I need to find a collection of documents in single query. The collection should contain document with newest date (field createdAt) for every user in single query.
There is a test case in Spock to demonstrate what I am trying to acheive:
def 'should filter the newest location for every user'() {
given:
List locationsInDb = [
buildLocation(USERNAME_1, '2017-02-03T10:37:30.00Z'),
buildLocation(USERNAME_1, '2017-03-04T10:37:30.00Z'),
buildLocation(USERNAME_2, '2017-02-05T10:37:30.00Z'),
buildLocation(USERNAME_2, '2017-03-06T10:37:30.00Z')
]
insertToMongo(locationsInDb)
when:
List filteredLocations = locationRepository.findLastForEveryUser()
then:
filteredLocations == [locationsInDb.get(1), locationsInDb.get(3)]
}
I found that distinct methods are a part of 2.1.0.M1 version so they are not available yet.
I was also trying with #Query annotation but the documentation (link below) does not specify how to create a query like mine.
https://docs.spring.io/spring-data/data-document/docs/current/reference/html/#d0e3309
Thanks for your help.
There are no means to express the query you are looking for via a derived query in Spring Data, nor using the MongoDB native query operators. Distinct as well will not do the job as it just extracts distinct values of a single field into an array.
Please consider using an Aggregation. Spring Data specifics can be found in the reference documentation.
I have a MongoDB collection containing User objects with two fields: Firstname and Lastname. I need a query that takes only one string (representing the user fullname) for a findLike research.
The problem is the same of this question but I do not know how translate that query for a MongoDB Repository in Spring Data using MongoTemplate or #Query annotation
EDIT:
Using project operator i have to specify all fields I want include in the stages. A better solution maybe could be use AddFields operator:
A similar question I found is that:
https://stackoverflow.com/a/40812293/6545142
How can I use the $AddFields operator with MongoTemplate?
You can use $expr ( 3.6 mongo version operator ) to use aggregation functions in regular query for only exact matches.
Spring #Query code
#Query("{$expr:{$eq:[{$concat:["$Firstname","$Lastname"]}, ?0]}}")
ReturnType MethodName(ArgType arg);
For find like searches or exact search you've to use aggregation via mongo template in lower versions.
AggregationOperation project = Aggregation.project().and(StringOperators.Concat.valueOf("Firstname").concatValueOf("Lastname")).as("newField");
for like matches
AggregationOperation match = Aggregation.match(Criteria.where("newField").regex(val));
for exact match
AggregationOperation match = Aggregation.match(Criteria.where("newField").is(val));
Rest of the code
Aggregation aggregation = Aggregation.newAggregation(project, match);
List<BasicDBObject> basicDBObject = mongoTemplate.aggregate(aggregation, colname, BasicDBObject.class).getMappedResults();
I am using Spring Data JPA with QueryDSL and trying to use Sum function in where condition, As i am using pagination so i have to get count first.
So i have java code like below :-
NumberPath<Double> path = entityPath.getNumber("qty", Double.class);
BooleanExpression exp = path.sum().loe(120);
JPQLQuery countQuery = from(stock).where(exp);
long count = countQuery.count();
its creating query like this :-
select count(stock0_.stock_id) as col_0_0_ from stock stock0_
where sum(stock0_.qty)>=120;
and i am getting Error Code: 1111. Invalid use of group function.
above query is not working in SQL as well because sum function cant be use with count in where condition. I have not idea how to deal with such problem when i have to get count first and then fetch the real data.
Can someone please help me out with what is JPA approach to deal with such issue.
Please don't suggested #Query annotation because i can not use it. Due to dynamic filtration requirement.
You are using aggregate functions (sum). Then you need having() instead where()
This example is a really large one. You only need to care about pagination and having elements.
If you got different predicates and a pageRequest object.
Page request example:
pageRequest = new PageRequest(
MI_PAGE,
LIMIT,
Sort.Direction.valueOf( ASC ),
ORDER_FIELD);
EntityQ represents a QueryDSL generated meta-entity.
Query example:
new JPAQuery(em).from(entityQ).where( entityQ.id.in(
new JPASubQuery()
.from(entity2Q).innerJoin(entity2Q.objEntityQ, entityQ)
.where(ENTITY, PREDICATE)
.groupBy(entity2Q.objEntityQ.id)
.having( Wildcard.count.goe(SIZE) )
.list(entity2Q.objEntityQ.id)
))
.offset(pageRequest.getOffset() )
.limit( pageRequest.getPageSize() )
.orderBy(ORDERS).list(entityQ);
EDIT 2 More specific for your example:
I usually have a persistence context in my custom repositories:
#PersistenceContext
EntityManager em
Then, you could create a simple JPAQuery:
new JPAQuery(em)
.from(YOUR_STOCK_QUERYDSL_ENTITY)
.groupBy(YOUR_STOCK_QUERYDSL_ENTITY.field_to_sum)
.having(YOUR_STOCK_QUERYDSL_ENTITY.field_to_sum.sum().as("alias")
.goe(100));
I am new to Jackson DB. Now I know that to get the entire document list of a collection using Jackson we need to do :
COllectionClass.coll.find().toArray();
which is Jackson DB equivalent to the mongodb command :
db.collection.find()
So What is the Jackson DB equivalent of say :
db.collection.find({},{"fieldName":1, "_id":0})
As given here, This might be helpful to you. (not tested)
coll.find(DBQuery.is(),//add your criteria for search here
DBProjection.include("fieldName")).toArray();
I am trying to recreate this query on spring mongo layer.
db.LASTVIEWED_TEST.aggregate([{$match: {'_id' : '12070'}},
{$unwind:"$value"},
{$match:{"value.Dockey":{"$in":["390", "539","626"]}}},
{$group:{_id:"$_id", "value":{$push:"$value"}}}
])
I have made some attempts using various methods however I came across the examples here:
https://github.com/spring-projects/spring-data-mongodb/blob/master/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java
and created this:
Aggregation agg = Aggregation.newAggregation(//
match(where("entityid").is(entityId)),//
unwind("$value"), //
match(where("value.Dockey").in(dockeyList)),//
group("id").push("$value").as("value")//
);
However this builder does not see to recognise the where, unwind keywords etc... and my compiler tells me my class does not have these methods.
What do I need to do to pass values to the newAggregation builder.
cheers,
You need to specify a Criteria object in the match, and generally all values are just represented as strings:
Aggregation agg = newAggregation(
match(Criteria.where("entityid").is(entityId)),
unwind("value"),
match(Criteria.where("value.Dockey").in(dockeyList)),
group("_id").push("value").as("value")
);