I am using spring-data-elasticsearch and I have a class as follows,
#Document(indexName = "sample")
class Sample {
#Id
private String id;
private String name;
private Status status;
}
class Status {
private String id;
}
Above is persisted to elasticsearch. And I have the following method to find data in it.
List<Sample> getAll(String statusId, String text, Pageable pageable);
So here is the current code I am using to get the data,
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(text)
.field("name")
.type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX));
NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
What I need is I need to find results by statusId and then search for the texts containing in name field.
I tried the following way also but didn't work. I tried the criteria-api also but it is working for only words without space. If there is words with space it is throwing Cannot constructQuery '*\"text\"*'. Use expression or multiple clauses instead
.withFilter(QueryBuilders.termQuery("status.id", statusId))
So how can I build the query for the above scenario?
New query build after suggestions by P.J. Meisch
QueryBuilder query1 = QueryBuilders.matchQuery("status.id", statusId).operator(Operator.AND);
QueryBuilder query2 = QueryBuilders.multiMatchQuery(text).field("name").type(MultiMatchQueryBuilder.Type.PHRASE_PREFIX);
new NativeSearchQueryBuilder().withQuery(QueryBuilders.boolQuery().must(query1).must(query2));
Your top level query should be a bool query with two entries as must:
the first for the name that would be the one you already have.
the second for the "status.id" property
Related
I am creating a REST service using springboot and MONGO as database.
I have a StudentDTO class with the following fields :
Class StudentDTO{
#Id
int s_no;
String name;
String dept;
int dept_no;
String course;
//getter and setters
}
I have some criteria on which i need to fetch the data. These criteria may vary. Below are some example :
1. Can fetch data on name, dept
2. Can fetch data on name, id
3. May want data on name, dept and course. etc
There is no fixed combination of criteria on which I can build my query.
One of the solution which is not appropriate i try to write is :
Query query = new Query();
Criteria criteria = new Criteria().andOperator(
Criteria.where("id").is(Integer.parseInt(dto.getId()),
Criteria.where("name").is(dto.getName()),
Criteria.where("dept").exists(true).is(dto.getDept()),
Criteria.where("dept_no").is(dto.getDept_no()),
Criteria.where("course").is(dto.getSource()));
query.addCriteria(criteria);
List<StudentDTO> recordsList = mongoTemplate.find(query, StudentDTO.class, "student_collection");
In the above solution there is no accommodation for the scenario is any of the field is missing.
To check weather attribute exist or not i tried using the below query :
Criteria.where("id").exist(true).is(Integer.parseInt(dto.getId());
but how i can add criteria over the DTO fields.
You can use below code. Use orOperator which accepts the array of criteria. Prepare the criteria values dynamically inside if statements and add the criteria array to or criteria.
Query query = new Query();
Criteria criteria = new Criteria();
List<Criteria> orCriterias = new ArrayList<>();
if( dto.getId() != null) {
orCriterias.add(Criteria.where("id").is(Integer.parseInt(dto.getId())));
}
... so on for other fields
criteria.orOperator(orCriterias.toArray(new Criteria[orCriterias.size()]));
query.addCriteria(criteria);
List<StudentDTO> recordsList = mongoTemplate.find(query, StudentDTO.class, "student_collection");
I have the following method for searching in a Lucene database.
public List search(String search, String sortPlace) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Spot.class).get();
Query luceneQuery = qb.keyword().fuzzy().withEditDistanceUpTo(1).withPrefixLength(1).onFields("name", "description", "address", "website")
.matching(search).createQuery();
//Query must also match *sortPlace* and the boolean *enabled*.
javax.persistence.Query jpaQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, Spot.class);
List<Spot> spotList = null;
try {
spotList = jpaQuery.getResultList();
} catch (NoResultException nre) {
System.out.println("NRE: " + nre);
}
return spotList;
}
This function implements fuzzy search on the following fields:
name, description, address and website.
The issue is:
The search results must also match the value for the field enabled and sortPlace (which could be Restaurant, Bar, Hotel etc.) but doesn't.
I guess I need some sort of WHERE clause on top of the existing query.
Hopefully somebody can help me out with that, thank you!
Usually I use hibernate session.get(Class.class,id) to get entity class.
here in get method I gave 2nd arg as id value (i.e int id=7)
My question is how to use non id value in hibernate session.get(Class.class,non_id), I need to get values with some other column (non id column).
You can use Criteria Queries. So in your case maybe like this:
Criteria crit = session.createCriteria(Class.class);
crit.add(Restrictions.eq("non_id", "myNonIdValue"));
List results = crit.list();
Class myClassObj = (myClassObj) results.get(0);
You can't do a get with a non-id. You will have to query for the object you need, like you would in SQL.
String queryText = "from SomeObject where objectPropery = :VALUE"
Query queryObj = session.createQuery( queryText );
queryObj.setParameter( "VALUE", value );
return queryObj.uniqueResult()
You can read all about queryObjects here: Query
If you are querying for objects, you will have to get to know HQL.
You can use Hibernate with JPA 2
class MyClass { #Id long id; #Basic String description; }
TypedQuery<MyClass> query =
getEntityManager().createNamedQuery("select myClass from MyClass where description = :description", MyClass.class);
query.setParameter("description", "example");
List<MyClass> myClasses = query.getResultList();
Or a single entity:
MyClass myClass = query.getSingleResult();
I'm using an ebean query in the play! framework to find a list of records based on a distinct column. It seems like a pretty simple query but the problem is the ebean method setDistinct(true) isn't actually setting the query to distinct.
My query is:
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findList();
In my results I get duplicate artist names.
From what I've seen I believe this is the correct syntax but I could be wrong. I'd appreciate any help. Thank you.
I just faced the same issue out of the blue and can not figure it out. As hfs said its been fixed in a later version but if you are stuck for a while you can use
findSet()
So in your example use
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findSet();
According to issue #158: Add support for using setDistinct (by excluding id property from generated sql) on the Ebean bug tracker, the problem is that an ID column is added to the beginning of the select query implicitly. That makes the distinct keyword act on the ID column, which will always be distinct.
This is supposed to be fixed in Ebean 4.1.2.
As an alternative you can use a native SQL query (SqlQuery).
The mechanism is described here:
https://ebean-orm.github.io/apidocs/com/avaje/ebean/SqlQuery.html
This is from the documentation:
public interface SqlQuery
extends Serializable
Query object for performing native SQL queries that return SqlRow's.
Firstly note that you can use your own sql queries with entity beans by using the SqlSelect annotation. This should be your first approach when wanting to use your own SQL queries.
If ORM Mapping is too tight and constraining for your problem then SqlQuery could be a good approach.
The returned SqlRow objects are similar to a LinkedHashMap with some type conversion support added.
// its typically a good idea to use a named query
// and put the sql in the orm.xml instead of in your code
String sql = "select id, name from customer where name like :name and status_code = :status";
SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
sqlQuery.setParameter("name", "Acme%");
sqlQuery.setParameter("status", "ACTIVE");
// execute the query returning a List of MapBean objects
List<SqlRow> list = sqlQuery.findList();
i have a solution for it:-
RawSql rawSql = RawSqlBuilder
.parse("SELECT distinct CASE WHEN PARENT_EQUIPMENT_NUMBER IS NULL THEN EQUIPMENT_NUMBER ELSE PARENT_EQUIPMENT_NUMBER END AS PARENT_EQUIPMENT_NUMBER " +
"FROM TOOLS_DETAILS").create();
Query<ToolsDetail> query = Ebean.find(ToolsDetail.class);
ExpressionList<ToolsDetail> expressionList = query.setRawSql(rawSql).where();//ToolsDetail.find.where();
if (StringUtils.isNotBlank(sortBy)) {
if (StringUtils.isNotBlank(sortMode) && sortMode.equals("descending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
//expressionList.orderBy().asc(sortBy);
}else if (StringUtils.isNotBlank(sortMode) && sortMode.equals("ascending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"asc");
// expressionList.orderBy().asc(sortBy);
} else {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
}
}
if (StringUtils.isNotBlank(fullTextSearch)) {
fullTextSearch = fullTextSearch.replaceAll("\\*","%");
expressionList.disjunction()
.ilike("customerSerialNumber", fullTextSearch)
.ilike("organizationalReference", fullTextSearch)
.ilike("costCentre", fullTextSearch)
.ilike("inventoryKey", fullTextSearch)
.ilike("toolType", fullTextSearch);
}
//add filters for date range
String fromContractStartdate = Controller.request().getQueryString("fm_contract_start_date_from");
String toContractStartdate = Controller.request().getQueryString("fm_contract_start_date_to");
String fromContractEndtdate = Controller.request().getQueryString("fm_contract_end_date_from");
String toContractEnddate = Controller.request().getQueryString("fm_contract_end_date_to");
if(StringUtils.isNotBlank(fromContractStartdate) && StringUtils.isNotBlank(toContractStartdate))
{
Date fromSqlStartDate=new Date(AppUtils.convertStringToDate(fromContractStartdate).getTime());
Date toSqlStartDate=new Date(AppUtils.convertStringToDate(toContractStartdate).getTime());
expressionList.between("fmContractStartDate",fromSqlStartDate,toSqlStartDate);
}if(StringUtils.isNotBlank(fromContractEndtdate) && StringUtils.isNotBlank(toContractEnddate))
{
Date fromSqlEndDate=new Date(AppUtils.convertStringToDate(fromContractEndtdate).getTime());
Date toSqlEndDate=new Date(AppUtils.convertStringToDate(toContractEnddate).getTime());
expressionList.between("fmContractEndDate",fromSqlEndDate,toSqlEndDate);
}
PagedList pagedList = ToolsQueryFilter.getFilter().applyFilters(expressionList).findPagedList(pageNo-1, pageSize);
ToolsListCount toolsListCount = new ToolsListCount();
toolsListCount.setList(pagedList.getList());
toolsListCount.setCount(pagedList.getTotalRowCount());
return toolsListCount;
Let's say I have a class Article which is automatically mapped by Java Ebean as a database table.
For this table I wanted to retrieve entries via a RawSql query, because I find SQL more simple when it gets to complex queries with many joins and such. By far I have managed to give my SQL statement to the Parser. The query is correct, I already checked that.
The only problem is, that I don't know, how to map the database results to my Article class. I know, that there is a columnMapping(...) method but honestly, I am to lazy to map every single column manually...
Isn't there another way to just like myResults.mapToClass(Article.class) to retrieve something like a List<Article>?
This is the code I already have:
Finder<String, Article> find = new Finder<String, Article>(
String.class, Article.class);
String sql = "SELECT * FROM article [...]";
RawSql rawSql = RawSqlBuilder.parse(sql).create();
List<Article> returnList = find.setRawSql(rawSql).findList();
Alternatively:
Finder<String, Article> find = new Finder<String, Article>(
String.class, Article.class);
String sql = "SELECT id, title, sub_title FROM article [...]";
RawSql rawSql = RawSqlBuilder.parse(sql)
.columnMapping("id", "id")
.columnMapping("title", "title")
.columnMapping("sub_title", "subTitle")
.create();
List<Article> resultList = find.setRawSql(rawSql).findList();
A lot of happened in Ebean since the question was asked, but I think the problem is still valid. the new RawSqlBuilder.tableMapping() makes things easier as can be seen in the code below, but afaik it still needs manual mapping of all attributes (no SELECT table.* FROM table)
I have this exact problem, and worked around this by creating a helper object (#Entity/#Sql) that I map to. E.g. CustomerWithPurchaseStats.
Extract:
#Entity
#Sql
public class CustomerWithPurchaseStats {
#OneToOne
private Customer customer;
...
And in the DAO:
public List<CustomerWithPurchaseStats> getAllCustomersWithPurchaseStats() {
StringBuilder sql = new StringBuilder("SELECT cu.id, <manually add all fields you need mapped ").append(" FROM customer cu ");
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql.toString());
rawSqlBuilder.tableAliasMapping("cu", "customer").create();
return Ebean.find(CustomerWithPurchaseStats.class)
.setRawSql(rawSqlBuilder.create())
.findList();
}