From Hibernate criteria to JPA criteria or QueryDSL - jpa-2.0

I'm thinking of porting an Hibernate criteria from an old DAO layer to either a JPA criteria or a QueryDSL one.
Since I've never used any of these two, I wonder which API I should use...
Here is the Hibernate criteria:
public Page<ElearningSubscription> findWithPatternLike(String searchPattern, int pageNumber, int pageSize) {
Criteria criteria = getSession().createCriteria(getPersistentClass(), "es");
criteria.createAlias(DB_TABLE_USER_ACCOUNT, "u", CriteriaSpecification.INNER_JOIN);
Conjunction conjunction = Restrictions.conjunction();
String pattern = "%" + searchPattern + "%";
Criterion firstname = Restrictions.ilike("u.firstname", pattern);
Criterion lastname = Restrictions.ilike("u.lastname", pattern);
Criterion email = Restrictions.ilike("u.email", pattern);
Disjunction disjunction = Restrictions.disjunction();
disjunction.add(firstname).add(lastname).add(email);
if (searchPattern.contains(" ")) {
String[] pieces = searchPattern.split(" ");
if (pieces[0] != null) {
Criterion firstnameBis = Restrictions.ilike("u.firstname", pieces[0]);
disjunction.add(firstnameBis);
}
if (pieces[1] != null) {
Criterion lastnameBis = Restrictions.ilike("u.lastname", pieces[1]);
disjunction.add(lastnameBis);
}
}
conjunction.add(disjunction);
OrderList orderList = new OrderList().add(Order.asc("u.firstname")).add(Order.asc("u.lastname")).add(Order.asc("u.email")).add(Order.desc("es.subscriptionDate"));
Page<ElearningSubscription> page = getPage(pageNumber, pageSize, criteria, orderList);
return page;
}
Thanks for any guidance.
Kind Regards,
Stephane Eybert

JPA 2 Criteria is an official standard, but Querydsl is superior in the following aspects
easier and less verbose syntax
customizable code generation
supports multiple backends
This answer is biased, since I am involved in the Querydsl development.

Related

Testing criteria creation in a Grails service

I have a Grails service that creates a criteria query with optional parameters like this:
List<Car> search(String make = "%", String model = "%", Integer year = null) {
def c = Car.createCriteria()
return c.list() {
if(make) {
like("make", make)
}
if(model) {
like("model", model)
}
if(year) {
eq("year", year)
}
}
}
(Also, is this the idiomatic way to do this in grails? I'm quite new to the framework and I'm trying to find the right way to do things)
I'd like to test that the proper criteria filters are set according to the values of the parameters of the search method but I'm having no success.
I tried some variations of this:
#TestFor(CarService)
#Mock(Car)
class CarServiceSpec extends Specification {
def car = Mock(Car)
void "empty filters"() {
when: service.search()
then:
with(car.createCriteria()) {
0 * like(*_)
0 * eq(*_)
}
}
}
But I can't seem to find a way to do assertions about the interactions between the CarService and the criteria object.
What am I missing?
The Grails Where query instead of the Criteria query seems to be better choice for an idiomatic way to do this in Grails:
Gorm Where Query

How do you specify multiple Sort fields with Solrj?

I have an application using solr that needs to be able to sort on two fields. The Solrj api is a little confusing, providing multiple different APIs.
I am using Solr 4.10.4
I have tried:
for (int i = 0; i < entry.getValue().size();) {
logger.debug("Solr({}) {}: {} {}", epName, entry.getKey(), entry
.getValue().get(i), entry.getValue().get(i + 1));
if (i == 0) {
query.setSort(new SolrQuery.SortClause(entry.getValue().get(i++), SolrQuery.ORDER.valueOf(entry.getValue().get(i++))));
} else {
query.addSort(new SolrQuery.SortClause(entry.getValue().get(i++), SolrQuery.ORDER.valueOf(entry.getValue().get(i++))));
}
}
When I look at the generated URL I only see the last SortClause sort=sequence+asc
I also tried creating a List and the setSorts SolrQuery method and that too seems to output only as single sort field, always the last one.
I was able to create the correct sort clause by generating it manually with strings.
I have tried addOrUpdateSort as well. I think I've tried most of the obvious combinations. of methods in the Solrj API.
This does work:
StringBuilder sortString = new StringBuilder();
for (int i = 0; i < entry.getValue().size();) {
if (sortString.length() > 0) {
sortString.append(",");
}
logger.debug("Solr({}) {}: {} {}", epName, entry.getKey(), entry
.getValue().get(i), entry.getValue().get(i + 1));
sortString.append(entry.getValue().get(i++)).append(" ").
append(SolrQuery.ORDER.valueOf(entry.getValue().get(i++)));
}
query.set("sort",sortString.toString());
The sort clause I want to see is: sort=is_cited+asc,sequence+asc
The solrj API seems to only output the final clause.
I suspect a bug in solrj 4.10
can you substitute setSort with addSort ie
for (int i = 0; i < entry.getValue().size();) {
logger.debug("Solr({}) {}: {} {}", epName, entry.getKey(), entry
.getValue().get(i), entry.getValue().get(i + 1));
if (i == 0) {
query.addSort(new SolrQuery.SortClause(entry.getValue().get(i++), SolrQuery.ORDER.valueOf(entry.getValue().get(i++))));
} else {
query.addSort(new SolrQuery.SortClause(entry.getValue().get(i++), SolrQuery.ORDER.valueOf(entry.getValue().get(i++))));
}
}
And let me know if this worked
Check out addOrUpdateSort()
Updates or adds a single sort field specification to the current sort
information. If the sort field already exist in the sort information map,
its position is unchanged and the sort order is set; if it does not exist,
it is appended at the end with the specified order..
#return the modified SolrQuery object, for easy chaining
#since 4.2

How do I query multiple IDs via the ContentSearchManager?

When I have an array of Sitecore IDs, for example TargetIDs from a MultilistField, how can I query the ContentSearchManager to return all the SearchResultItem objects?
I have tried the following which gives an "Only constant arguments is supported." error.
using (var s = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
rpt.DataSource = s.GetQueryable<SearchResultItem>().Where(x => f.TargetIDs.Contains(x.ItemId));
rpt.DataBind();
}
I suppose I could build up the Linq query manually with multiple OR queries. Is there a way I can use Sitecore.ContentSearch.Utilities.LinqHelper to build the query for me?
Assuming I got this technique to work, is it worth using it for only, say, 10 items? I'm just starting my first Sitecore 7 project and I have it in mind that I want to use the index as much as possible.
Finally, does the Page Editor support editing fields somehow with a SearchResultItem as the source?
Update 1
I wrote this function which utilises the predicate builder as dunston suggests. I don't know yet if this is actually worth using (instead of Items).
public static List<T> GetSearchResultItemsByIDs<T>(ID[] ids, bool mustHaveUrl = true)
where T : Sitecore.ContentSearch.SearchTypes.SearchResultItem, new()
{
Assert.IsNotNull(ids, "ids");
if (!ids.Any())
{
return new List<T>();
}
using (var s = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var predicate = PredicateBuilder.True<T>();
predicate = ids.Aggregate(predicate, (current, id) => current.Or(p => p.ItemId == id));
var results = s.GetQueryable<T>().Where(predicate).ToDictionary(x => x.ItemId);
var query = from id in ids
let item = results.ContainsKey(id) ? results[id] : null
where item != null && (!mustHaveUrl || item.Url != null)
select item;
return query.ToList();
}
}
It forces the results to be in the same order as supplied in the IDs array, which in my case is important. (If anybody knows a better way of doing this, would love to know).
It also, by default, ensures that the Item has a URL.
My main code then becomes:
var f = (Sitecore.Data.Fields.MultilistField) rootItem.Fields["Main navigation links"];
rpt.DataSource = ContentSearchHelper.GetSearchResultItemsByIDs<SearchResultItem>(f.TargetIDs);
rpt.DataBind();
I'm still curious how the Page Editor copes with SearchResultItem or POCOs in general (my second question), am going to continue researching that now.
Thanks for reading,
Steve
You need to use the predicate builder to create multiple OR queries, or AND queries.
The code below should work.
using (var s = Sitecore.ContentSearch.ContentSearchManager.GetIndex("sitecore_master_index").CreateSearchContext())
{
var predicate = PredicateBuilder.True<SearchResultItem>();
foreach (var targetId in f.Targetids)
{
var tempTargetId = targetId;
predicate = predicate.Or(x => x.ItemId == tempTargetId)
}
rpt.DataSource = s.GetQueryable<SearchResultItem>().Where(predicate);
rpt.DataBind();
}

Faceted search on multilist in sitecore 7

I'm able to generate the facet and bind the result in Linkbutton within repeater control. Now I'm facing problem in generating the facet query with OR operator when the user selects more than 1 value of same facet type in Sitecore 7.
What can be done to solve it?
Thanks
Here's a blog post about solving this using PredicateBuilder with a specific code example:
http://www.nttdatasitecore.com/en/Blog/2013/November/Building-Facet-Queries-with-PredicateBuilder.aspx
To implement the Facet search in Sitecore use PredicateBuilder class to build the filter query and then add the filter query to the base query. Code is mentioned below:
List<PeopleFields> objPeoplefields = new List<PeopleFields>();
IQueryable<PeopleFields> Query = null;
var predicatePractice = Sitecore.ContentSearch.Utilities.PredicateBuilder.False<PeopleFields>();
var predicateOffice = Sitecore.ContentSearch.Utilities.PredicateBuilder.False<PeopleFields>();
using (var context = ContentSearchManager.GetIndex(SITECORE_WEB_INDEX).CreateSearchContext())
{
//Base query
Query = context.GetQueryable<PeopleFields>().Where(i => i.FirstName.StartsWith(txtFirstName.Text)).Where(i => i.LastName.StartsWith(txtLastName.Text));
foreach (string strselecteFacet in lstPracticefacetSelected)
{
//filter query
predicatePractice = predicatePractice.Or(x => x.Practice.Like(strselecteFacet));
}
foreach (string strselecteFacet in lstOfficefacetSelected)
{
//Filter query
predicateOffice = predicateOffice.Or(x => x.Office.Like(strselecteFacet));
}
//Joining the filter query alongwith base query
if (lstPracticefacetSelected.Count > 0 && lstOfficefacetSelected.Count > 0)
Query = Query.Filter(predicatePractice).Filter(predicateOffice);
else if (lstPracticefacetSelected.Count > 0 && lstOfficefacetSelected.Count == 0)
Query = Query.Filter(predicatePractice);
else if (lstPracticefacetSelected.Count == 0 && lstOfficefacetSelected.Count > 0)
Query = Query.Filter(predicateOffice);
objPeoplefields = Query.ToList();
}
As a quick way, if you're ok with having SitecoreUISearchResultItem as the query result type, you may be able to utilize the same method Sitecore 7 uses to parse queries entered in the content editor search:
Sitecore.Buckets.Util.UIFilterHelpers.ParseDatasourceString(string query)
If that's not up to what you're after, reading how it's implemented with a decompiler (ILSpy, DotPeek, Reflector, Resharper, etc) may help you in composing an expression manually based on dynamic criteria.

Hibernate Regex

I am trying to build an API which can search by HQL regex keywords,
EDITED:
The best way to perform regex search in HQL is to use criteria, Restrictions.like() or Restrictions.ilike().
public static List<Object> createQueryAnd(Criteria cri,
ArrayList<Parameters> list) {
for (Parameters p : list) {
String value = (String) p.value;
if (value.contains("*")) {
value = value.replace("*", "%");
} else {
value += "%";
}
Criterion c1 = Restrictions.ilike(p.property, value);
cri.add(c1);
}
return cri.list();
}
Hope this helps someone
HQL does not have regular expressions. If you want to use database provider specific constructs for regular expression, Dialect should be modified. This question contains discussion about how to do that with Oracle database.