As far as I can see, the only way to make a query to ElasticSearch in Yii2 is to run ElasticModel::find()->query($query), where $query is a complex array containing the actual query written in ElasticSearch query DSL.
The query is huge and quickly becomes unmanageable. For SQL Yii2 provides a powerful query builder class that supports tons of useful methods like andWhere(). For ElasticSearch everything goes into one gigantic query expression, very much like building an SQL expression string by hand.
Is there any high-level wrapper for ElasticSearch query DSL for Yii2? If not, is there a standalone library with similar functionality?
If you intend to build for version 1.6 of elastic, I have created a query builder for my company and published it here
You will use it as a standalone query builder, and at the end you will need to get the final query array and pass it to the query executer.
To install it, you can simply use composer composer require itvisionsy/php-es-orm or download the zipped version here.
The link above contains some examples, and here is a copy:
//build the query using different methods
$query = \ItvisionSy\EsMapper\QueryBuilder::make()
->where('key1','some value') //term clause
->where('key2',$intValue,'>') //range clause
->where('key3','value','!=') //must_not term clause
->where('key4', ['value1','value2']) //terms clause
->where('email', '#hotmail.com', '*=') //wildcard search for all #hotmail.com emails
->sort('key1','asc') //first sort option
->sort('key2',['order'=>'asc','mode'=>'avg']) //second sort option
->from(20)->size(20) //results from 20 to 39
->toArray();
//modify the query as you need
$query['aggs']=['company'=>['terms'=>['field'=>'company']]];
//then execute it against a type query
$result = TypeQuery::query($query);
//i am not sure about Yii way to execute, according to the question, it should be:
$result = ElasticModel::find()->query($query);
The package also include a simple ElasticSearch ORM class which maybe useful for you. Take a look at it here.
Hope this helps you...
Related
I just started studying Symfony 3.4 and Doctrine ORM. I need to take count of users. I tested with the normal query it is working fine. How can I convert the normal query into Doctrine ORM? Any help would be appreciated.
"SELECT count(DATE_FORMAT(application.reviewed_at, '%Y-%m-%d')) AS count, user_id FROM application WHERE user_id = 6 AND DATE(reviewed_at) = CURDATE() GROUP BY DATE(reviewed_at)";
I tried below code. It is not working.
$qb = $em->createQueryBuilder();
$entries = $qb->select("count(DATE_FORMAT(application.reviewed_at, '%Y-%m-%d')) AS count", "user_id")
->where("user_id = 6", "DATE(reviewed_at) = CURDATE()")
->from('AppBundle\Entity\Application', 'u');
->groupBy("DATE(reviewed_at)");
->getQuery()
->getResult();
Simply put, Doctrine ORM is not intended to transform SQL queries back into DQL. DQL is a query language that can be translated into multiple vendor-specific SQL variants, so this is a one-way transformation from DQL to SQL.
Thus, if you have just a SQL query, it could be a good idea to use just that, using Doctrine DBAL. Of course, you'd probably have to deal with result set mappings, but there could be no need to drag all the ORM stuff into your code.
What you're doing here is essentially building a DQL query using the Doctrine's QueryBuilder API. The FROM part is absolutely correct, but those using the DATE_FORMAT and DATE functions look wrong to me. Since DQL translates to several different SQL implementations, depending on the driver you're using, it needs a compatibility layer because functions such as DATE_FORMAT, DATE and CURDATE are not common for all the platforms.
First, take a look at the documentation for user-defined functions. For example, the DATEDIFF() function in MySQL is a good example. If it turns out that no one has already implemented support for the functions you're using, that's a good place to start, since it basically describes how to implement an extension for the Doctrine Query Language (DQL).
Second, there's a good chance that the functions you're using are already implemented in some of the third-party libraries, such as OroCRM's or Benjamin Eberlei's Doctrine extension collections.
Also, if you're selecting an aggregation or a function call result, it's always a good idea to alias it. What you could try here is use the $qb->expr() function subset to limit the DQL to a more strict grammar.
It is important to remember that Doctrine DQL is not an SQL dialect. It is a custom language that attempts to look close to SQL, but it operates with Doctrine entities and associations, not with database tables and columns. Of course at the end it transforms into SQL, but until this transformation you need to stay within DQL syntax.
One of reasons why your query did not work is an attempt to use native SQL (MySQL to be true) functions inside DQL query. DQL have no such functions and you have 2 ways to go at this point:
Rewrite your query in a way that it will be compatible with Doctrine. It may involve updates for your database scheme. In your particular case you may want to convert reviewet_at column from whatever it is now (timestamp I suppose) into date type that allows direct date comparisons. It will let you to get rid of custom MySQL date transformation functions and will make your query compatible with Doctrine
You can choose a harder path and implement all necessary custom functions directly in Doctrine. It is not a simple thing, but Doctrine is flexible enough to provide you with such ability. There is good article into Doctrine documentation about this topic if you will be interested.
Try to give a go to this:
public function select()
{
$queryBuilder = $this->createQueryBuilder('a');
$queryBuilder
->addSelect(['a.user_id', $queryBuilder->expr()->count('a.reviewed_at')])
->where('a.user_id = :idUser')
->andWhere('a.reviewed_at = :date')
->setParameters([
'idUser' => 6,
'date' => new \DateTime(),
])
->groupBy('a.reviewed_at')
;
return $queryBuilder
->getQuery()
->getResult()
;
}
I tried searching an trying.
I need to group the nested fields.
The sql query is as follows:
SELECT p_application_category,
Sum(p_recv_bytes) as download,
p_date
FROM ZLog2 $w
group by p_application_category;
I solved the problem with elastic sql plugin. It makes regular sql statements available in elastic.
I am using Doctrine 2.5.x and I am having problems with getting the LIMIT clause to work for UPDATE queries. It always updates all matched records (i.e. it seems to ignore the LIMIT clause).
setMaxResults() seems to have no effect when used together with UPDATE queries.
As a quick workaround I am using a native MySQL query but that cannot be the best solution.
I tried these examples but none are working:
Doctrine update query with LIMIT
https://recalll.co/app/?q=doctrine2%20-%20Doctrine%20update%20query%20with%20LIMIT
QueryBuilder with setMaxResults() (does not work):
$qb = $em->createQueryBuilder();
$query = $qb->update('\Task\Entity', 't')
->set('t.ClaimedBy', 1)
->where('t.Claimed IS NULL')
->getQuery();
$query->setMaxResults(20);
$this->log($query->getSQL());
Hope someone can help in finding a better solution than a native query. It takes away the whole benefit of the ORM.
Is it even possible to use a LIMIT clause in an UPDATE statement?
In short, no, because the SQL specification does not support UPDATE ... LIMIT ..., so none of the ORM trying to achieve portability should allow you to do it.
Please also have a look at MySQL Reference Manual itself stating that UPDATE ... LIMIT ... is not a standard SQL construction:
MySQL Server supports some extensions that you probably will not find in other SQL DBMSs. Be warned that if you use them, your code will not be portable to other SQL servers. In some cases, you can write code that includes MySQL extensions, but is still portable, by using comments of the following form:
SQL statement syntax
The ORDER BY and LIMIT clauses of the UPDATE and DELETE statements.
So by essence because what you are trying to achieve is not standard SQL the ORM will not have a portable way to implement it and will probably not implement it at all.
Sorry, but what you are trying to achieve is not possible through DQL, because:
Ocramius commented on Sep 2, 2014
DQL doesn't allow limit on UPDATE queries, as it is not portable.
As suggested in this issue of DoctrineBundle repository by its owner, Marco Pivetta (he also happen to be the owner of the ORM repository).
Further information, although it might needs a good link to the right ISO specification documentation that is sadly not freely available:
The ISO standard of UPDATE instruction do not allow LIMIT in an UPDATE, where SELECT is, of course, an instruction that does allow it.
As you were raising it by yourself, the purpose of an ORM is to not write pure SQL in order to have it cross DBMS compatible. If there is no possibility to make it that way, then it makes sense that the ORM does not implement it.
Also note that on other SQL variant than MYSQL, the limit is actually part of the SELECT clause:
select * from demo limit 10
Would translate in a SQL Server to
select top 10 from demo
Or in Orcale to
select * from demo WHERE rownum = 1
Also see: https://stackoverflow.com/a/1063937/2123530
As b.enoit.be already stated in his answer, this is not possible in Doctrine because using LIMIT's in an UPDATE statement is not portable (only valid in MySQL).
Hope someone can help in finding a better solution than a native query. It takes away the whole benefit of the ORM.
I would argue that you are mixing business rules with persistence (and the ORM does not play well with that, luckily).
Let me explain:
Updating an entity's state is not necessarily a business rule. Updating max. 20 entities is (where does that 20 come from?).
In order to fix this, you should properly separate your business rules and persistence by separating it into a service.
class TaskService
{
private $taskRepository;
public function __construct(TaskRepository $taskRepository)
{
$this->taskRepository = $taskRepository;
}
public function updateClaimedBy()
{
$criteria = ['Claimed' => null];
$orderBy = null;
// Only update the first 20 because XYZ
$limit = 20;
$tasks = $taskRepository->findBy($criteria, $orderBy, $limit);
foreach($tasks as $task) {
$task->setClaimedBy(1)
}
}
}
I want to count the number of date appear in my table using Laravel 5 and SQL Query:
$tanggal = DB::table('entry')->count('*')->groupBy(DATE_FORMAT(created_at,'%d-%m-%Y'));
But it can't work. Help me.
When you call count(), it is returning the number (as you saw in your error). Try swapping the functions and setting the groupBy parameter to be calculated raw:
$tanggal = DB::table('entry')
->groupBy(DB::raw("DATE(created_at)")
->count('*');
When you call table() it starts a query builder instance. A lot of query builder methods return the query builder again for method chaining, but some of them are final and return a result like get(), first(), and in this case count().
I haven't tested the above code, but it should be close. You will also notice I changed it from DATE_FORMAT to just DATE. I can see you are trying to exclude the timestamp portion, and you can just use the latter instead of reformatting (which may slow down the query a tiny bit).
EDIT:
Ok, your question was not explained very well. From your comment, I think this is what you are looking for. Also, don't forget, Eloquent is a fantastic ORM, but it isn't a silver bullet and sometimes you may have to just raw plain SQL queries.
$results = DB::table('entry')
->addSelect(DB::raw('COUNT("*") as total)')
->groupBy(DB::raw("DATE(created_at)")
->get();
I was recently trying to do a project*, which caused me to ask this question. Although since then I've found an alternative solution, I am still curious if what I envisioned doing is, in any way, possible.
Essentially, I am wondering if there is anyway to perform a MySQL query on a MySQL query result in php. For example:
$result = mysql_query("SELECT * FROM foo WHERE bar=".$barValue);
AND THEN, be able to perform multiple queries on $result:
$newResult = mysql_query("SELECT * FROM $result WHERE otherBar=".$barValue);
OR
$otherNewResult = mysql_query("SELECT * FROM $result WHERE otherOtherBar=".$barValue." ORDER BY foobar ASC");
AND so on and so forth...
I realize that I could append the original query with my new WHERE statements and ORDER BYs, but that causes my to query the database unnecessarily and it prevents me from writing more objected oriented code (because I can't pass around a result to be queried, but rather have to requery the database in every function...)
Any advice, pieces of code, frameworks, or ramblings appreciated.
*BTW, my project was having to query a large database of people for people born in certain age groups and then query those age groups for different demographics.
Edit
No, writing a custom function to query the database is not worth the object-orientation (and modifiability) it would give me
You could do a nested query in the same SQL query and keep PHP out of it:
'SELECT * FROM (SELECT * FROM foo WHERE bar="something") AS q1 WHERE q1.bar2 = "something else"'
The question has already been answered. However following explanation will help someone who might be interested in knowing the details of it.
What are Nested query / subquery:
Subqueries are also known as nested queries. A subquery is a SELECT statement within another statement. MySQL supports all SQL standards and additionally provides MySQL specific features.
Why should I use Subquery:
Subquery is structured and it is possible to isolate each parts of statement
Subquery is more readable that complex joins and unions
Subquery provides alternative means to perform action which otherwise would require complex joins and unions
What Subquery returns:
A subquery can return a single value, a single row, a single column, or a table. These are called scalar, column, row, and table subqueries.
Reference: http://dev.mysql.com/doc/refman/5.7/en/subqueries.html
http://www.w3resource.com/sql/subqueries/nested-subqueries.php