What is the correct way to use a datastore cursor in a GQL query? The following code works until I add the cursor:
$q = $db->gqlQuery('SELECT * FROM Post ORDER BY time DESC LIMIT #limit OFFSET #cursor', [
'bindings' => [
"limit" => 3,
// Cursor retrieved using $posts[2]->cursor();
"cursor" => 'CjgKEwoJdGltZXN0YW1wEgYIhPWOyQUSHWoIY2NhbGJsb2dyEQsSBFBvc3QYgICAgICAwAsMGAAgAQ=='
]
]);
$posts = $db->runQuery($q);
This results in the error:
{"error":{"message":"Binding site #cursor for offset bound to non-integer value parameter.","status":"INVALID_ARGUMENT","code":400}}
The docs clearly state:
OFFSET: Specifies offsets into the result set: either a cursor, or a
count, or both
What am I doing wrong?
Related
I made two requests. The first one gives me 2419 results and I store the result in $requestFirst. The second, 1 result and I store the result in $requestTwo.
I make a union :
$requestTot = $requestFirst->union($requestTwo);
The total of the $requestTot is 2420 results so all is well so far.
Then :
$request = $this->paginate($requestTot);
$this->set(compact('request'));
And here I don't understand, on each page of the pagination I find the result of $requestTwo. Moreover the pagination displays me :
Page 121 of 121, showing 20 record(s) out of 2,420 total
This is the right number of results except that when I multiply the number of results per page by the number of pages I get 2540. This is the total number of results plus one per page.
Can anyone explain?
Check the generated SQL in Debug Kit's SQL panel, you should see that the LIMIT AND OFFSET clauses are being set on the first query, not appended as global clauses so that they would affect the unionized query.
It will look something like this:
(SELECT id, title FROM a LIMIT 20 OFFSET 0)
UNION
(SELECT id, title FROM b)
So what happens then is that pagination will only be applied to the $requestFirst query, and the $requestTwo query will be unionized on top of it each and every time, hence you'll see its result on every single page.
A workaround for this current limitation would be to use the union query as a subquery or a common table expression from which to fetch the results. In order for this to work you need to make sure that the fields of your queries for the union are being selected without aliasing! This can be achieved by either using Table::subquery():
$requestFirst = $this->TableA
->subquery()
->select(['a', 'b'])
// ...
$requestTwo = $this->TableB
->subquery()
->select(['c', 'd'])
// ...
or by explicitly selecting the fields with aliases equal to the column names:
$requestFirst = $this->TableA
->find()
->select(['a' => 'a', 'b' => 'b'])
// ...
$requestTwo = $this->TableB
->find()
->select(['c' => 'c', 'd' => 'd'])
// ...
Then you can safely use those queries for a union as a subquery:
$union = $requestFirst->union($requestTwo);
$wrapper = $this->TableA
->find()
->from([$this->TableA->getAlias() => $union]);
$request = $this->paginate($wrapper);
or as a common table expression (in case your DBMS supports them):
$union = $requestFirst->union($requestTwo);
$wrapper = $this->TableA
->find()
->with(function (\Cake\Database\Expression\CommonTableExpression $cte) use ($union) {
return $cte
->name('union_source')
->field(['a', 'b'])
->query($union)
})
->select(['a', 'b'])
->from([$this->TableA->getAlias() => 'union_source']);
$request = $this->paginate($wrapper);
I am trying to get some data in CakePHP 3.6. The condition is based on 2 column in a same table. first column is status as second column is is_done_by_user. So I want to fetch data if status is 1 or 2. Also if the status is 3. But for status 3 there need to be a check if the column is_done_by_user have value 1. So the final thing is. I want to get all the data with status 1 and 2. And all the data with status 3 where is_done_by_user is 1.
I have written the query but it is not working the way I am wanting. Here is the query that I had tried till now.
$query = $this->find('all',[
'conditions' => [
'Appointments.is_active' => 1,
'Appointments.status IN' => (1,2,3),
'Appointments.is_done_by_user' => 1
]
]);
Maybe I am far away from the actual query.
Your query as written will find anything where all of the stated conditions are true, which is obviously not what you want. I think that this may be what you're looking for?
$query = $this->find('all',[
'conditions' => [
// This must always be true
'Appointments.is_active' => 1,
// One of the conditions in this list must be true
'OR' => [
// Either the status is 1 or 2
'Appointments.status IN' => (1,2),
// Or the status is 3 AND the user is 1
[
'Appointments.status' => 3,
'Appointments.is_done_by_user' => 1,
],
],
]
]);
I have been stuck in this issue
$resource(BASE_URL, {
snId: '#snId',
id: '#id',
},
i am passing parametrs in this order in query
'request_type':$stateParams['request_type'],
'screen_name': 'abx',
'graph_type': 'dddd',
'hide_users': 0,
'auto_tag' : 0
I should get url as activity?request_type=abc&screen_name=abx&graph_type=dddd&hide_users=0&auto_tag=0 but instead of this i am getting url in alphabetical order as
auto_tag=0…hide_users=0&request_type=abc&screen_name=abx
Hope anybody knowing this can help
I'm new in Cakephp 3.x and I'm having some trouble to create a subquery in the new ORM format. I have this report in my application, that needs to return the follow result:
1. There are three entities - Users, Calls, CallStatus.
2. Users hasMany Calls, Calls hasMany CallStatus.
3. I need to count how many CallStatus each user has in Calls.
Now follow the query that I need to put on new ORM format:
SELECT U.name,
(SELECT COUNT(*) FROM calls as C WHERE C.call_status_id =1 and C.user_id=U.id) AS 'Unavailable',
(SELECT COUNT(*) FROM calls as C WHERE C.call_status_id =2 and C.user_id=U.id) AS 'Busy',
(SELECT COUNT(*) FROM calls as C WHERE C.call_status_id =3 and C.user_id=U.id) AS 'Contacted',
(SELECT COUNT(*) FROM calls as C WHERE C.call_status_id =4 and C.user_id=U.id) AS 'Error'
FROM `users` AS U
WHERE U.profile=3 and U.is_active=1
Could someone give me a help, please? Thanks
If I understand you correctly, you want to see the number of calls for every callstatus you have for a specific user.
Try the following. Note that I used the CakePHP convention for naming the callstatuses (which is plural).
// get the tableregistry
use Cake\ORM\TableRegistry;
$callstatuses = Cake\ORM\TableRegistry::get('Callstatuses');
// for user with id 2, get the number of calls for each callstatus
$callstatuses->find()
->contain(['Calls'])
->where(['Calls.user_id' => 2, 'User.is_active' => 1])
->countBy('name')
->toArray();
// output could be:
//[ 'Unavailable' => 2, 'Busy' => 1 ]
You can find information about creating queries in the CakePHP book: see 'Query Builder'.
If you want to know more about working with/on queries, note that queries are Collections. Anything you can do on a Collection object, you can also do in a Query object. See the Collection section in the CakePHP book.
You have to use subqueries, as many as you want!
Here is an example for your case:
$q = $this->Calls->find();
$q1->select([$q->func()->count('*')])
->where(['Calls.user_id = Users.id', 'call_status_id' => 1]);
$q2->select([$q->func()->count('*')])
->where(['Calls.user_id = Users.id', 'call_status_id' => 2]);
$q3->select([$q->func()->count('*')])
->where(['Calls.user_id = Users.id', 'call_status_id' => 3]);
$q4->select([$q->func()->count('*')])
->where(['Calls.user_id = Users.id', 'call_status_id' => 4]);
$qUsers = $this->Users->find()
->select([
'id',
'first_name',
'Unavailable' => $q1,
'Busy' => $q2,
'Contacted' => $q3,
'Error' => $q4
])
->where(['profile' => 3, 'active' => 1])
->all();
Note: That nicer if you use a loop to create suqueries in this case.
How to print ORM query
$query = $articles->find('all')->contain(['Comments']);
For example print =>
SELECT * FROM comments WHERE article_id IN (comments);
Wrapping your ORM query result with the debug function will show the SQL and bound params:
debug($query);
You can also similarly look at the query results with the debug function.See CakePHP 3: retrieving data and result sets — Debugging Queries and ResultSets
what about $query->sql()?
$qb = $this->Person->find()->select(["id", "text" => "concat(Name,' ',Family)"])
->where(['id >' => 0])
->where($query ? ["OR" => $filters] : null)
->limit(10);
dd($qb->sql());
and result:
.../src/Controller/ClientController.php (line 86)
'SELECT Person.id AS `Person__id`, concat(Name,' ',Family) AS `text` FROM person Person WHERE (id > :c0 AND (Family like '%sam%' OR Name like '%sam%' OR Family like '%sam%' OR Name like '%sam%')) LIMIT 10'
I prefer this:
public function __debugInfo()
{
return [
'query' => $this->_query,
'items' => $this->toArray(),
];
}
// Print the query
debug($query->__debugInfo()['sql']);
// Prints this
SELECT * FROM comments WHERE article_id IN (comments);