Spring Data mongo case insensitive like query - full-text-search

I want to make a text search case insensitive with regex query with spring-data mongo .
For example in Oracle :
select * from user where lower(username) like '%ab%'
How can i make this query with spring-data mongo ?
Thanks in advance

You can try something like below. Assumes you have a User pojo class.
Using MongoTemplate
i option for case insensitive:
Criteria regex = Criteria.where("username").regex(".*ab.*", "i");
mongoOperations.find(new Query().addCriteria(regex), User.class);
Using MongoRepository (Case sensitive)
List<User> users = userRepository.findByUserNameRegex(".*ab.*");
interface UserRepository extends MongoRepository<User, String> {
List<User> findByUserNameRegex(String userName);
}
Using MongoRepository with Query dsl (Case sensitive)
List<User> users = userRepository.findByQuery(".*ab.*");
interface UserRepository extends MongoRepository<User, String> {
#Query("{'username': {$regex: ?0 }})")
List<User> findByQuery(String userName);
}
For Non-Regex based query you can now utilize case insensitive search/sort through collation with locale and strength set to primary or secondary:
Query query = new Query(filter);
query.collation(Collation.of("en").
strength(Collation.ComparisonLevel.secondary()));
mongoTemplate.find(query,clazz,collection);

I know this is an old question. I just found the solution in another post.
Use $regex and $options as below:
#Query(value = "{'title': {$regex : ?0, $options: 'i'}}")
Foo findByTitleRegex(String regexString);
see the original answer:
https://stackoverflow.com/a/19068401

#Repository
public interface CompetenceRepository extends MongoRepository<Competence, String> {
#Query("{ 'titre' : { '$regex' : ?0 , $options: 'i'}}")
List<Competence> findAllByTitreLikeMotcle(String motCle);
}
Should works perfectly ,it works in my projects.for words in French too

In repository pattern I tried this and worked
#Query("{ 'username' : ?0 }.collation( { locale: 'en', strength: 2 } )")
User findByUserName(String name);

Yes, you can, provided you have set up it right, I would add something like this in your repository method:
{'username': {$regex: /ab/i }})
#Query("{'username': {$regex: /?1/i }})")
List findUsersByUserName(String userName);

For like query on numeric fields (int or double)
db.players.find({
$and: [{
"type": {
$in: ["football"]
}
},
{
$where : "/^250.*/.test(this.salary)"
}]
})
See the detailed code: Spring Boot mongodb like query on int/double using mongotemplate

String tagName = "apple";
Query query = new Query();
query.limit(10);
query.addCriteria(Criteria.where("tagName").regex(tagName));
mongoOperation.find(query, Tags.class);
Reference - https://mkyong.com/mongodb/spring-data-mongodb-like-query-example/

Related

InvalidPathException while sorting with org.springframework.data.domain.Pageable

I am trying to sort my table's content on the backend side, so I am sending org.springframework.data.domain.Pageable object to controller. It arrives correctly, but at the repository I am getting org.hibernate.hql.internal.ast.InvalidPathException. Somehow the field name I would use for sorting gets an org. package name infront of the filed name.
The Pageable object logged in the controller:
Page request [number: 0, size 10, sort: referenzNumber: DESC]
Exception in repository:
Invalid path: 'org.referenzNumber'","logger_name":"org.hibernate.hql.internal.ast.ErrorTracker","thread_name":"http-nio-8080-exec-2","level":"ERROR","level_value":40000,"stack_trace":"org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'org.referenzNumber'\n\tat org.hibernate.hql.internal.ast.util.LiteralProcessor.lookupConstant(LiteralProcessor.java:111)
My controller endpoint:
#GetMapping(value = "/get-orders", params = { "page", "size" }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<PagedModel<KryptoOrder>> getOrders(
#ApiParam(name = "searchrequest", required = true) #Validated final OrderSearchRequest orderSearchRequest,
#PageableDefault(size = 500) final Pageable pageable, final BindingResult bindingResult,
final PagedResourcesAssembler<OrderVo> pagedResourcesAssembler) {
if (bindingResult.hasErrors()) {
return ResponseEntity.badRequest().build();
}
PagedModel<Order> orderPage = PagedModel.empty();
try {
var orderVoPage = orderPort.processOrderSearch(resourceMapper.toOrderSearchRequestVo(orderSearchRequest), pageable);
orderPage = pagedResourcesAssembler.toModel(orderVoPage, orderAssembler);
} catch (MissingRequiredField m) {
log.warn(RESPONSE_MISSING_REQUIRED_FIELD, m);
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok(orderPage);
}
the repository:
#Repository
public interface OrderRepository extends JpaRepository<Order, UUID> {
static final String SEARCH_ORDER = "SELECT o" //
+ " FROM Order o " //
+ " WHERE (cast(:partnerernumber as org.hibernate.type.IntegerType) is null or o.tradeBasis.account.retailpartner.partnerbank.partnerernumber = :partnerernumber)"
+ " and (cast(:accountnumber as org.hibernate.type.BigDecimalType) is null or o.tradeBasis.account.accountnumber = :accountnumber)"
+ " and (cast(:orderReference as org.hibernate.type.LongType) is null or o.tradeBasis.referenceNumber = :orderReference)"
+ " and (cast(:orderReferenceExtern as org.hibernate.type.StringType) is null or o.tradeBasis.kundenreferenceExternesFrontend = :orderReferenceExtern)"
+ " and (cast(:dateFrom as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp > :dateFrom) "
+ " and (cast(:dateTo as org.hibernate.type.DateType) is null or o.tradeBasis.timestamp < :dateTo) ";
#Query(SEARCH_ORDER)
Page<Order> searchOrder(#Param("partnerernumber") Integer partnerernumber,
#Param("accountnumber") BigDecimal accountnumber, #Param("orderReference") Long orderReference,
#Param("orderReferenceExtern") String orderReferenceExtern, #Param("dateFrom") LocalDateTime dateFrom,
#Param("dateTo") LocalDateTime dateTo, Pageable pageable);
}
Update:
I removed the parameters from the sql query, and put them back one by one to see where it goes sideways. It seems as soon as the dates are involved the wierd "org." appears too.
Update 2:
If I change cast(:dateTo as org.hibernate.type.DateType) to cast(:dateFrom as date) then it appends the filed name with date. instead of org..
Thanks in advance for the help
My guess is, Spring Data is confused by the query you are using and can't properly append the order by clause to it. I would recommend you to use a Specification instead for your various filters. That will not only improve the performance of your queries because the database can better optimize queries, but will also make use of the JPA Criteria API behind the scenes, which requires no work from Spring Data to apply an order by specification.
Since your entity Order is named as the order by clause of HQL/SQL, my guess is that Spring Data tries to do something stupid with the string to determine the alias of the root entity.

MongoDB embedded Document Array: Get only one embedded document with a spezific attribute

I want to get one Embedded Document with a specific field (version) from an array with mongodb and spring boot.
This is the data structure:
{
"_id": 5f25882d28e40663719d0b52,
"versions": [
{
"versionNr": 1
"content": "This is the first Version of some Text"
},
{
"versionNr": 2
"content": "This is the second Version of some Text"
},
...
]
...
}
Here are my entities:
#Data
#Document(collection = "letters")
public class Letter {
#Id
#Field("_id")
private ObjectId _id;
#Field("versions")
private List<Version> versions;
}
//There is no id for embedded documents
#Data
#Document(collection = "Version")
public class Version{
#Field("content")
private String content;
#Field("version")
private Long version;
}
And this is the query that doesn't work. I think the "join" isn't correct. But can't figure out the right way.
public Optional<Version> findByIdAndVersion(ObjectId id, Long version) {
Query query = new Query(Criteria.where("_id").is(id).and("versions.version").is(version));
return Optional.ofNullable(mongoTemplate.findOne(query,Version.class,"letters"));
}
}
EDIT: This is a working Aggregation, I'm sure it isn't a pretty solution but it works
#Override
public Optional<Version> findByIdAndVersion(ObjectId id, Long version) {
MatchOperation match = new MatchOperation(Criteria.where("_id").is(id).and("versions.version").is(version));
Aggregation aggregate = Aggregation.newAggregation(
match,
Aggregation.unwind("versions"),
match,
Aggregation.project()
.andInclude("versions.content")
.andInclude("versions.version")
);
AggregationResults<Version> aggregateResult = mongoTemplate.aggregate(aggregate, "letters", Version.class);
Version version = aggregateResult.getUniqueMappedResult();
return Optional.ofNullable(mongoRawPage);
}
Query query = new Query(Criteria.where("_id").is(id).and("versions.version").is(version));
return Optional.ofNullable(mongoTemplate.findOne(query,Version.class,"letters"));
You are querying the Letter document but your entity class is specified as Version.class, since findOne from MongoDB doesn't return the subdocument by itself but rather the whole document, you need to have Letter.class as return type and filter (project) what fields to get back. So you can either project the single version subdocument that you want to receive, like so:
Query query = new Query()
.addCriteria(Criteria.where("_id").is(id).and("versions.version").is(version))
.fields().position("versions", 1);
Optional.ofNullable(mongoTemplate.findOne(query, Letter.class))
.map(Letter::getVersions)
.findFirst()
.orElse(null);
or use aggregation pipeline:
newAggregation(
Letter.class,
match(Criteria.where("_id").is(id)),
unwind("versions"),
replaceRoot("versions"),
match(Criteria.where("version").is(version))),
Version.class)
Note -- I typed this on a fly.

JpaRepository Distinct doesn't work with more than one field

I have this query that works fine
#Query(value = "SELECT DISTINCT taxonomyGroup FROM sagePrices AS sP WHERE sP.brand = :brand AND sP.taxonomyGroup IS NOT NULL", nativeQuery = true)
List<String> findTaxonomyByBrandMatching(String brand);
But I want to use JPARepository. I tried several options already, but nothing really worked, I'm getting all the correct records, but they are still duplicated
These are the several options that I tried.
List<SagePricesEntity> findTaxonomyGroupDistinctByBrandAndTaxonomyGroupNotNull(String brand);
List<SagePricesEntity> findDistinctTaxonomyGroupByBrandAndTaxonomyGroupNotNull(String brand);
List<SagePricesEntity> findDistinctTaxonomyGroupByTaxonomyGroupNotNullAndBrand(String brand);
Does anyone knows if what I'm trying to do is possible?
This is the response that I get when using JPARepository queries
[
{
"taxonomyGroup": "rtrtr"
},
{
"taxonomyGroup": "rtrtr"
},
{
"taxonomyGroup": "fhfdhfdhdfh"
},
{
"taxonomyGroup": "ydtyjtyjetyj"
}
]
you have to use spring projection for get only one column value
interface TaxonomyGroupOnly{
String getTaxonomyGroup();
}
List<TaxonomyGroupOnly> findDistinctTaxonomyGroupByBrandAndTaxonomyGroupNotNull(String category);
more about Spring Projection - https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections

Not able to get array object response in #Query annotation in Spring

My Repository
#Repository
public interface TestNativeQRepository extends CrudRepository<TestNativeQ, String> {
#Query( value="SELECT qplt.name price_list_name, qplab.status_code, qplab.start_date, (SELECT charge_definition_code FROM oalfsaas_repl.QP_CHARGE_DEFINITIONS_B WHERE charge_definition_id=qplab.charge_definition_id ) chargedefinitioncode "
+ "FROM pricelistsall qplab, PRICELISTSTL qplt "
+ " WHERE qplab.price_list_id =qplt.price_list_id ", nativeQuery = false)
public List<TestNativeQDTO> getAllDetails();
}
Actual Result:
[{"ABC", "DEF", "15/05/2018", "XXZ"}]
Expected Result
[{name: "ABC", statuscode: "DEF", startDate: "15/05/2018", chargedefintioncode: "XXZ"}]
As #Nikolay gave hint in comment.
The result of native query is not automatically transformed to an Entity, you must do it manually or define mappings via #SqlResultSetMapping and #ColumnResult.
to make that work follow the below code.
#Entity
#Configurable
#SqlResultSetMapping(name = "someName", entities = #EntityResult(entityClass = SamplePojo.class), columns = #ColumnResult(name = "columnName"))
public class SamplePojo{
//fields and getters/setters
}
and Then in query
List<SamplePojo> list = entityManager().createNativeQuery("Select ......", "someName").getResultList();
Note : someName should be same in both places.
Refer this-question

Mongodb query for embedded doc

I am having trouble to query that, I want to find all the docs that contains the id "5418a26ce4b0e4a40ea1d548" in the individualUsers field. would be esp useful if you know how to do that in Spring Data MongoDB query.
db.collection.find({individualUser:{"5418a26ce4b0e4a40ea1d548"}})
example of one doc
{ "_id" : ObjectId("5418c3b9e4b03feec4345602"), "creatorId" : "5418a214e4b0e4a40ea1d546", "individualUsers" : { "5418a26ce4b0e4a40ea1d548" : null, "5418a278e4b0e4a40ea1d54a" : null } }
Update #001
Entity code
#Document
class Idea{
#Id
String id;
String creatorId;
Map<String,String> individualUsers;
/*getter and setter omitted*/
}
Interface
public interface IdeaRepository extends MongoRepository<Idea,String> {
}
Update #002
So when spring-mongodb saves hashmap to json, it will look like
"individualUsers" : { "5418a26ce4b0e4a40ea1d548" : null, "5418a278e4b0e4a40ea1d54a" : null }
In java program I can easily get the data using the key value. but in the mongodb query, I can't query the key?
so the question is can I query inside the "individualUsers": {} with the key ??

Resources