Spring Data MongoDB: search like on multiple fields - java

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();

Related

MongoDB java driver passing undefined as value

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)));

JPA criteria builder: how to replace and cast a string to numeric in order-by?

Can someone please suggest me how to build up the following query using JPA Criteria builder API?
SELECT id, name, date, version FROM public.upgradeTable
order by (CAST(replace(version, '.', '')AS numeric)) desc;
Please note our version column have values like "11.0.2.213", "11.0.2.73"
We need to trim character '.' inside the version and then cast them as numeric and then sort by desc.
Currently JPA does not have APIs for replace() and cast(string as numeric). But you can use CriteriaBuilder.function(...) to create database native functions if database portability is not critical.
For MySQL, the order-by expression of your example would be:
Expression<String> replacedValue = criteriaBuilder.function("replace",
String.class, root.get("version"), criteriaBuilder.literal("."),
criteriaBuilder.literal(""));
Expression<String> lpadValue = criteriaBuilder.function("lpad",
String.class, replacedValue, criteriaBuilder.literal(20),
criteriaBuilder.literal("0"));
criteriaQuery.orderBy(criteriaBuilder.desc(lpadValue));
CriteriaBuilder.function(...) does not support such native functions as cast(value as type) or convert(value, type). So use lpad(...) to achieve the same orderBy results.
It works great with Cmobilecom JPA, a ligth-weight JPA implementation for both Java and Android.
Disclaimer: I am a developer of Cmobilecom JPA.

How to get the newest record for every user using spring data mongodb?

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.

Aggregation on spring mongo

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")
);

Regular expression Spring data mongodb repositories

Good morning,
I´m trying to combine regular expression with Spring data mongodb repository using Query annotation.
What I want is search one substring inside one string attribute of my mongo document.
I have been looking in google and here, but I did not find anything elegant, and I was wondering if Spring data has something official about this using the repositories.
Regards.
It seems like an old question, so maybe you've already had a solution but here how I handled the same issue :
#Query(value = "{'title': {$regex : ?0, $options: 'i'}}")
Foo findByTitleRegex(String regexString);
using the /?0/ notation won't work since Spring Data places a String value with quotes

Categories