I have a class
#Entity
public class Person{
...
#ElementCollection
private Set<String> tags;
...
}
I want to use the JPA 2.0 Criteria API to search over these tags - but in a case insensitive way. Thus I want to both set the search parameter to UPPER and the column to UPPER (Pseudo-SQL: select p.* from Person p join Tags t on p.id=t.pId where upper(t.name)=upper('searchParameter'); )
Here is my code without the UPPER on the tags:
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
return this.em.createQuery(query.select(root).where(
builder.isMember(builder.upper(builder.literal(searchTag)),
root.get(Person_.tags)))).getResultList();
where searchTag is the input parameter.
How can I assign the UPPER to the Person_.tags "Column"?
In other words I want to write this Query with Criteria API:
SELECT p FROM Person p JOIN p.tags t WHERE UPPER(t) = UPPER('searchString')
Thank you
Ok, I finally have the solution:
cQuery.where(
builder.equal(
builder.upper(cQuery.from(Relation.class).join(Relation_.aliase)
.as(String.class)),
builder.upper(builder.literal(alias))
)
);
One has to use the ".as(..)" method.
suburbCriteria = criteriaBuilder.equal(
criteriaBuilder.upper(root.get(Property_.suburb)),
criteriaBuilder.upper(criteriaBuilder.literal(searchBean.getSuburb())));
You need to call upper and literal property name on joined Tags from Person.
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaQuery<Person> query = builder.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
Join<Person, Tag> tags = root.join(Person_.tags);
query.where(builder.isMember(
builder.upper(builder.literal(searchTag)),
builder.upper(builder.literal(tags.get(Tag_.name)))
));
return this.em.createQuery(query).getResultList();
I omitted query.select(root), not sure if it is required.
Tou can also omit builder.literal() on Tag_.name since is String.
If I am wrong somewhere, please edit my answer for further users.
Hope this helps.
Related
I have written this SQL code
SELECT drugs.*, COUNT(*) as 'views' from drugs INNER JOIN drug_seen on drugs.id = drug_seen.drug_id GROUP BY drugs.id order by views ASC
And now I am trying to write in in the Laravel equolent but I am facing some troubles.
This is what I have tried
$drugs = Drug::select(DB::raw('drugs.*,count(*) as views'))
->join('drug_seen', 'drugs.id', 'drug_seen.drug.id')
->groupBy('drug.id')->orderByRaw('views');
I am having errors like column not found i think the code is not written properly
Drug class
class Drug extends Model
{
use HasFactory;
use SoftDeletes;
...
...
...
public function drugVisits()
{
return $this->hasMany(DrugSeen::class);
}
Hop this will solve your problem.
$drugs = Drug::with('drugVisits')->get();
$drugs->count(); //for total records in drugs table.
You have typo error in join instead on drug_id you use drug.id
Try this:
$drugs = Drug::select(DB::raw('drugs.*,count(*) as views'))
->join('drug_seen', 'drugs.id', 'drug_seen.drug_id')
->groupBy('drugs.id')->orderByRaw('views');
}
As soon as you use join() you're leaving Eloquent and entering Query\Builder, losing the benefits of Model configurations in the process. And with() eager-loads aren't the answer, if you're looking to filter the results by both tables. What you want is whereHas().
Also, as far as your grouping and count manipulation there, I think you're looking more for Collection handling than SQL groups.
$drugModel = app(Drugs::class);
$results = $drugModel->whereHas('drugVisits')->with('drugVisits')->get();
$organizedResults = $results
->groupBy($drugModel->getKey())
->sortyBy(function (Drugs $drugRecord) {
return $drugRecord->drugVisits->count();
});
If you want to have a 'views' property that carries the count in the root-level element, it would look like this:
$drugModel = app(Drugs::class);
$results = $drugModel->whereHas('drugVisits')->with('drugVisits')->get();
$organizedResults = $results
->groupBy($drugModel->getKey())
->map(function (Drugs $drugRecord) {
$drugRecord->views = $drugRecord->drugVisits->count();
return $drugRecord;
});
->sortyBy('views');
I'm not really sure how involved this might be, but could someone help me with below problem.
I'm trying to implement search functionality in my project based on employee firt and last name. I have used Spring Data REST and Hibernate Search for this purpose.
#Transactional
public search(String searchText) {
FullTextEntityManager fullTextEntityManager = org.hibernate.search.jpa.Search
.getFullTextEntityManager(entityManager);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Employee.class).get();
org.apache.lucene.search.Query luceneQuery = qb.keyword().wildcard()
.onFields("firstName", "middleName", "lastName").matching(searchText + "*").createQuery();
javax.persistence.Query jpaQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, Employee.class);
List result = jpaQuery.getResultList();
List<EmployeeSearchDTO> listOfDTO = new ArrayList<>();
EmployeeSearchDTO employeeDTO;
Iterator<Employee> itr = result.iterator();
while (itr.hasNext()) {
Employee employee = itr.next();
employeeDTO = new EmployeeSearchDTO(employee);
listOfDTO.add(employeeDTO);
}
}
When I search "john doe" i expect the results should match the below two
FirstName : John LastName : Doe
FirstName : johnathan LastName : Doe
But that is not the case and I'm able to search only based on FirstName["john"] or LastName["doe"] but not with both.
How do I solve this, any pointers would be greatly appreciated. Thanksin advance.
You really want to create two queries, one against the first name and one against the last name and then combine them via the SHOULD operator. Something like
Query combinedQuery = querybuilder
.bool()
.should( firstNameQuery )
.should( lastNameQuery )
.createQuery();
This means you are looking for results where either of the queries match.
In Groovy to set a bean you just need to give the GroovyRowResult while creating object.
Consider below People.groovy bean:
class People {
String name
int age
}
My sql query:
select * from People -- returns name and age of people
the GroovyRowResult is returned with column names (keys) in capitals like it:[NAME:"Alex", AGE: 21].
So when I try to set the bean like below:
le.rows(sqlQuery).each {
People p = new People(it)
}
I receive the Exception:
groovy.lang.MissingPropertyException: No such property: NAME for class: People. Possible solutions: name
I guess I can modify sql query to include double quotes on the alias, but have you guys handled it any different?
The rows() method returns a List<GroovyRowResult> where GroovyRowResult implements Map and then you can apply collectEntries to transform it, so that the keys are lowercase and you can use the resulting map:
sql.rows('select * from people').each {
People p = new People(it.collectEntries { k,v -> [k.toLowerCase(), v] })
println p.name
println p.age
}
I have a codeigniter app.
My active record syntax works perfectly and is:
function get_as_09($q){
$this->db->select('m3');
$this->db->where('ProductCode', $q);
$query = $this->db->get('ProductList');
if($query->num_rows > 0){
foreach ($query->result_array() as $row){
$row_set[] = htmlentities(stripslashes($row['m3'])); //build an array
}
return $row_set;
}
}
This is effectively
select 'm3' from 'ProductList' where ProductCode='$1'
What I need to do is convert the below query into an active record type query and return it to the controller as per above active record syntax:
select length from
(SELECT
[Length]
,CONCAT(([width]*1000),([thickness]*1000),REPLACE([ProductCode],concat(([width]*1000),([thickness]*1000),REPLACE((convert(varchar,convert(decimal(8,1),length))),'.','')),'')) as options
FROM [dbo].[dbo].[ProductList]) as p
where options='25100cr' order by length
I picture something like below but this does not work.
$this->db->select(length);
$this->db->from(SELECT [Length],CONCAT(([width]*1000),([thickness]*1000),REPLACE[ProductCode],concat(([width]*1000),([thickness]*1000),REPLACE((convert(varchar,convert(decimal(8,1),length))),'.','')),'')) as options
FROM [dbo].[dbo].[ProductList]);
$this->db->where(options, $q);
$this->db->order(length, desc);
Help appreciated as always. Thanks again.
You can use sub query way of codeigniter to do this for this purpose you will have to hack codeigniter. like this
Go to system/database/DB_active_rec.php Remove public or protected keyword from these functions
public function _compile_select($select_override = FALSE)
public function _reset_select()
Now subquery writing in available And now here is your query with active record
$select = array(
'Length'
'CONCAT(([width]*1000)',
'thickness * 1000',
'REPLACE(ProductCode, concat((width*1000),(thickness*1000),REPLACE((convert(varchar,convert(decimal(8,1),length))),'.','')),'')) as options'
);
$this->db->select($select);
$this->db->from('ProductList');
$Subquery = $this->db->_compile_select();
$this->db->_reset_select();
$this->db->select('length');
$this->db->from("($Subquery)");
$this->db->where('options','25100cr');
$this->db->order_by('length');
And the thing is done. Cheers!!!
Note : While using sub queries you must use
$this->db->from('myTable')
instead of
$this->db->get('myTable')
which runs the query.
Source
I've got a OneToMany relationship where one football team has many players. I want to list all football teams and display the name of the captain for each team.
Each player entity has a foreign key (team_id) and a field 'captain' which is set to 0 or 1. I'm currently running the following query:
$teams = $this
->getDoctrine()
->getRepository('FootballWebsiteBundle:Team')
->createQueryBuilder('t')
->setFirstResult(($pageNumber * $resultPerPage) - $resultPerPage)
->setMaxResults($resultPerPage)
->add('where','t.deleted = 0')
->add('orderBy', 't.name DESC')
->getQuery()->getResult();
Then when I loop through each team in twig I run team.getTeamCaptain().getName() which is a filter within my Team entity:
public function getTeamCaptain() {
$them = $this->players->filter(function($p) {
return $p->getCaptain() == 1;
});
return $them->first();
}
Is there a better way to run this query?
First of all, you may want to fetch-join the players of each retrieved team to avoid having them lazy loaded during rendering of the template. Here's the DQL:
SELECT
t, p
FROM
FootballWebsiteBundle:Team t
LEFT JOIN
t.players p
WHERE
t.deleted = 0
ORDER BY
t.name DESC
Which can be built with following query builder API calls:
$teamsQuery = $this
->getDoctrine()
->getRepository('FootballWebsiteBundle:Team')
->createQueryBuilder('t')
->addSelect('p')
->leftJoin('t.players', 'p')
->add('where','t.deleted = 0')
->add('orderBy', 't.name DESC')
->getQuery()
Then you wrap this query into a Paginator object (since setMaxResults and setFirstResult cannot be trusted when fetch-joining):
$paginator = new \Doctrine\ORM\Tools\Pagination\Paginator($teamsQuery, true);
$teamsQuery
->setFirstResult(($pageNumber * $resultPerPage) - $resultPerPage)
->setMaxResults($resultPerPage)
In your view you can then iterate on the teams like following pseudo-code:
foreach ($paginator as $team) {
echo $team->getTeamCaptain() . "\n";
}
You can also gain some extra performance in your getTeamCaptain method by using the Selectable API:
public function getTeamCaptain() {
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->andWhere($criteria->expr()->eq('captain', 1));
return $this->players->matching($criteria)->first();
}
The advantage here is mainly relevant when the association players is not yet initialized, since this will avoid loading it entirely. This is not the case, but I consider it a good practice (instead of re-inventing collection filtering logic).