Below is my fetch code for sphinx. We have a filter named typeId which we use to compare bureau type. Now I have added one more filter named as altTypeId. Now I have to compare typeId and altTypeId both for same value such typeId = 6 OR altTypeId = 6.
I have tried several solutions but no one is working. I followed the solution on this link: https://sphx.org/forum/view.html?id=13474 but its not working as well.
protected function fetch() {
$this->_searchCriteria->orders = $this->sort;
$this->pagination->validateCurrentPage = false;
$this->_searchCriteria->paginator = $this->pagination;
$this->_searchCriteria->query.=" profileTypePlaceholderTexxxxxt";
Yii::app()->search->SetLimits($this->pagination->offset, $this->pagination->limit);
Yii::app()->search->SetFieldWeights($this->_fieldWeights);
if ($this->_searchCriteria->query)
Yii::app()->search->setMatchMode(SPH_MATCH_ALL);
else
Yii::app()->search->setMatchMode(SPH_MATCH_FULLSCAN);
Yii::app()->search->setFieldWeights(array(
'primary_comp' => 100,
'premium' => 80,
'standard' => 60,
'free' => 40,
'comp' => 20,
'title' => 5,
'description' => 5,
));
//Yii::app()->search->SetSortMode(SPH_SORT_EXTENDED, '#weight DESC');
Yii::app()->search->SetSortMode(SPH_SORT_EXTENDED,'#weight DESC, random DESC');
foreach ($this->_searchCriteria->filters as $filter) {
if ($filter[0] == 'range')
Yii::app()->search->SetFilterFloatRange($filter[1], (float) $filter[2] + 0.01, (float) $filter[3]);
else
Yii::app()->search->SetFilter($filter[0], array($filter[1]));
}
$results = Yii::app()->search->Query($this->_searchCriteria->query, $this->_searchCriteria->from);
$this->_data = array();
$this->_keys = array();
$this->_itemCount = $results['total'];
$this->pagination->itemCount = $this->_itemCount;
if ($this->_itemCount) {
$this->_populateItems($results['matches']);
}
}
Frankly putting both values in the same sphinx attrbute, seems easiest. MVA is perfect for this!
Couple of ways could be done, but just...
sql_query = SELECT id,title, CONCAT_WS(',',typeId,altTypeId) AS typeids FROM ...
sql_attr_multi = uint typeids from field
then just
->SetFilter('typeids', array(6));
finds results from EITHER column.
Otherwise if really want to only do it at query time, its something like
if ($filter[0] == 'typeid') {
Yii::app()->search->SetSelect("*, (typeid = {$filter[1]} OR altTypeId = {$filter[1]}) AS myfilter");
Yii::app()->search->SetFilter('myfilter', array(1));
} else ...
Related
I want to create a sql statement with cakePHP 3.6.10:
SELECT id FROM table_xy WHERE (status != 1 OR name IS NULL) AND id IN(1,2,3);
Now, copying from the sophistated examples in the cookbook I got this:
$userIds = [2,212,232];
$table = TableRegistry::getTableLocator()->get('TableXY');
$query = $table->find()
->select(['id'])
->where(function(QueryExpression $exp) {
$orConditions = $exp->or_(function($or) {
return $or->isNull('name')
->notEq('status', 1);
});
return $exp
->add($orConditions)
->in('id', ':id');
})
->bind(':id', $userIds, 'int[]');
$results = $query->all();
This results in an error, saying "unknown type int[]". But this is exactly the same as described in the documentation
$query->bind(':id', [1, 2, 3], 'int[]');
Any ideas?
Instead of such sophisticated example, you can try like this:
// an array is not automatically converted
$result = $this->table_xy->find('all')->where(['id IN' => $userIds,
'OR' => [
'status !=' => 1,
'name is NULL'
]
])->select(['id']);
CAKEPHP > Query Builder > Advanced Conditions
I'm currently trying to generate a unique order number when the user reaches the create method. The order numbers are generated like this in the seed and need to look like this as well
Seed
foreach(range(1,25) as $index)
{
DB::table('orders')->insert([
'user_id' => rand(1,25),
'order_nr' => '#' . sprintf("%08d", $index),
'price_sum' => $faker->randomNumber($nbDigits = 4, $strict = false) . '.' . $faker->randomNumber($nbDigits = 2, $strict = false),
'status' => $faker->randomElement(['paid', 'pending', 'failed']),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
}
The order numbers look like this #00000001 or #00000002. Now when the user reaches the create method in my controller a new unique order number in this sequence needs to be created. How can I achieve that? The controller currently looks like this:
public function create()
{
$order = new Order;
$order->user_id = Auth()->id();
$order->order_nr =
dd($order);
return view('steps.order');
}
It needs to check the latest order number and create one with +1 on that order number. Say for instance there are 25 orders and the last one is #00000025 the one that needs to be created next needs to be #00000026. How can I achieve that?
Try doing as following
$order = new Order;
$order->user_id = Auth()->id();
$latestOrder = App\Order::orderBy('created_at','DESC')->first();
$order->order_nr = '#'.str_pad($latestOrder->id + 1, 8, "0", STR_PAD_LEFT);
$order->save();
Here I am assuming that the id is auto-incrementing. See the str_pad method for more details
Use the auto column id value of your rows to generate the order number. However, do not create an extra column for your order number, because this would give you an unnormalized DB, since the order-column is completely dependent of the id column.
Instead, add this method to you order model
public function get_order_number()
{
return '#' . str_pad($this->id, 8, "0", STR_PAD_LEFT);
}
If your last order had the id 5 and you would delete it, then the next order would have the id 6.
The only possible exception is if you create orders within an transaction. If a transaction is rolled back, the associated order id would be skipped.
You can use the Laravel ID generator.
First Install it:
composer require haruncpi/laravel-id-generator
Import the class in your controller.
use Haruncpi\LaravelIdGenerator\IdGenerator;
Now simply use it
$prefix = "#";
$id = IdGenerator::generate(['table' => 'your_table_name', 'length' => 9, 'prefix' =>$prefix]);
Output
#00000001
#00000002
#00000003
...
There is a problem in reading the maximum id and increment it.
In heavy servers or parallel requests, two requests may read the same max(id) from MySQL.
The best way to create unique sequence number is use a sequence table with just one auto increment field.
First insert new record in the sequence table.
Then read the last inserted id from db by LAST_INSERT_ID() MySQL function.
At last you can remove old records lower than your id.
Laravel way:
$id = DB::table('seq_foo')->insertGetId(['id' => null]);
DB::table('seq_foo')->where('id', '<', $id)->delete();
$id is your unique sequence number.
You may try with this:
public function generateOrderNR()
{
$orderObj = \DB::table('orders')->select('order_nr')->latest('id')->first();
if ($orderObj) {
$orderNr = $orderObj->order_nr;
$removed1char = substr($orderNr, 1);
$generateOrder_nr = $stpad = '#' . str_pad($removed1char + 1, 8, "0", STR_PAD_LEFT);
} else {
$generateOrder_nr = '#' . str_pad(1, 8, "0", STR_PAD_LEFT);
}
return $generateOrder_nr;
}
You can generate order_nr by using this: $this->generateOrderNR(); in your create() function.
In addition mt_rand() is 4 times faster than rand() you may use this for better user experience.
I have found a better solution:
$paynowbillprefix1="ADDON-";
$paynowbillprefix1=strlen($paynowbillprefix1);
$query2 = "select gen_id from user_cart_addon order by id desc limit 0, 1";
$exec2 = mysqli_query($conn,$query2) or die ("Error in Query2".mysqli_error());
$res2 = mysqli_fetch_array($exec2);
$num2 = mysqli_num_rows($exec2);
$billnumber = $res2["gen_id"];
$billdigit=strlen($billnumber);
if ($billnumber == '')
{
$billnumbercode ='ADDON-'.'1';
}
else
{
$billnumber = $res2["gen_id"];
$billnumbercode = substr($billnumber,$paynowbillprefix1, $billdigit);
$billnumbercode = intval($billnumbercode);
$billnumbercode = $billnumbercode + 1;
$maxanum = $billnumbercode;
$billnumbercode = 'ADDON-'.$maxanum;
}
I used to group on mongoDB via PHP to get the max date of my items.
As i have too many items (more than 10 000), i read i must use MapReduce.
Here's my past group function :
$keys = array('ItemDate'=> true);
$initial = array('myLastDate' => 0);
$reduce = "function(obj, prev) {
if (myLastDate < obj.ItemDate) {
myLastDate = ItemDate;
}
}";
$conds = array( 'ItemID' => (int) $id );
$results = $db->items->group($keys, $initial, $reduce,
array('condition'=> $conds ) );
I've tried something but seems not to work ...
$map = new MongoCode("function() {
emit(this.ItemID,this.ItemDate);
}");
$reduce = new MongoCode("function(obj, prev) {
if(prev.myLastDate < obj.ItemDate) {
prev.myLastDate = obj.ItemDate;
}
}");
$items = $db->command(array(
"mapreduce" => "items",
"map" => $map,
"reduce" => $reduce,
"query" => array("ItemID" => $id);
$results = $db->selectCollection($items['result'])->find();
Can you please help ?
Solution
You don't need to use map/reduce for that. Provided your date field contains an ISODate, a simple query does the trick:
db.yourColl.find({},{_id:0,ItemDate:1}).sort({ItemDate:-1}).limit(1)
In order to have this query done efficiently, you need to set an index on ItemDate
db.yourColl.createIndex({ItemDate:-1})
Explanation
The query
Let us dissect the query. db.yourColl...
.find({} The default query
,{_id:0,ItemDate:1} We want only ItemDate to be returned. This is called a projection.
.sort({ItemDate:-1}) The documents returned should be sorted in descending order on ItemDate, making the document with the newest date the first to be returned.
.limit(1) And since we only want the newest, we limit the result set to it.
The index
We create the index in descending order, since this is the way you are going to use it. However, if you need to change the default query to something else, the index you create should include all fields you inspect in the query, in the exact order.
I am making an application using CakePHP. I made an action which uses saveAll function.
And I thought it works well, because it doesn't need so much data, but it took over 3 minutes to save using saveAll, or other save function.
Does anyone find my mistakes?
phpMyadmin's columns:
id, rank, school_detail_id, total_score, school_name,
(there is about 300~400 data)
public function rank_update(){
$check_scores = $this->ClubScore->find('all', array('fields'=>array('id','total_score')));
$check_scores2 = Set::sort($check_scores, "{n}.ClubScore.total_score","DESC");
$rank_id=0;
$temp_score=0;
$temp = null;
$for_count=0;
foreach ($check_scores2 as $check_score):
if($temp_score != $check_score['ClubScore']['total_score']){
$rank_id++;
$temp_score = $check_score['ClubScore']['total_score'];
// make ranking by score. same score is same ranking.
}
$this->ClubScore->id = $check_score['ClubScore']['id'];
$this->ClubScore->saveField('rank', $rank_id);
endforeach;
}
Divide the query from foreach to simpler approach
get distinct total_score in desc order
$data = $this->ClubScore->find('all', array('fields'=>array('DISTINCT total_score'), 'order' => 'total_score DESC'));
and then simply save the key as rank for each total_score using updateAll and foreach
Thank you very much Abhishek and AgRizzo ,Nunser!!
Now, I've completely solved this problem. It takes only 1 or 2 seconds!!!!!
Here is the source code.
public function rank_update(){
$data = $this->ClubScore->find('all', array('fields'=>array('DISTINCT total_score'), 'order' => 'total_score DESC'));
$check_scores = $this->ClubScore->find('all', array('fields'=>array('id','total_score')));
$check_scores2 = Set::sort($check_scores, "{n}.ClubScore.total_score","DESC");
$ii = 0;
$temp = 0;
foreach($check_scores2 as $scores):
if($data[$ii]['ClubScore']['total_score']
== $scores['ClubScore']['total_score']){
$temp=$ii+1;
}else{
$ii++;
$temp=$ii+1;
}
$update_arr[] = array(
'ClubScore' => array(
'id' => $scores['ClubScore']['id'],
'rank' =>$temp,
)
);
endforeach;
$update_arr = Set::sort($update_arr, "{n}.ClubScore.id","ASC");
var_dump($update_arr);
foreach($update_arr as $update_arrs):
$this->ClubScore->updateAll(
array(
'ClubScore.rank' => $update_arrs['ClubScore']['rank'],
),
array(
'ClubScore.id' => $update_arrs['ClubScore']['id'],
)
);
endforeach;
}
Thank you very much.
Best regards.
I have reached very strange problem using Zend Framework 1 and binding the resultset to BvbGrid.
We have a method which queries an external API. You need to specify the following params: methodName, params.
So the controller recieves a resultset as an array from the external API.
The resultset (in the database) is 4 columns - 1 string, 2 DateTime, 3 int, 4 int. And is with about 3000 rows.
When I recieve the resultset in the controller I bind it to Bvb Grid, which should evaluate in the view as columns with search boxes.
So far, so good, when I search in the first or in the last column (string, int), it searches in the whole resultset.
But when I search 2, 3 (DateTime, int) it searches only in the current page.
I don't understand why is that. There is no difference in data binding, or something.
The code is as follows
public function indexAction() {
$pagesize = $this->_request->getPost('rows');
$pagenum = $this->_request->getPost('page');
$pagesize = ($pagesize) ? $pagesize : 30;
$pagenum = ($pagenum) ? $pagenum : 1;
$params = array();
$params['page_num'] = $pagenum;
$params['page_limit'] = $pagesize;
if ($this->_request->isXmlHttpRequest()) {
// additional search filter
if ($this->_request->getPost('username')) {
$params['username'] = $this->_request->getPost('username');
}
if ($this->_request->getPost('lastlogin')) {
$params['lastlogin'] = $this->_request->getPost('lastlogin');
}
if ($this->_request->getPost('type')) {
$params['type'] = $this->_request->getPost('type');
}
if ($this->_request->getPost('level')) {
$params['level'] = $this->_request->getPost('level');
}
}
if (($order_by = $this->_request->getPost('sidx')) && ($order_type = $this->_request->getPost('sord'))) {
$params['order_by'] = $order_by;
$params['order_by_type'] = $order_type;
}
$resultset = $this->web_service->_apiRequest('SearchUsers', $params);
/**
* The resultset is like
* array(
'found_rows' => 3000,
'body' => array(
'username' => 'blabla',
'lastlogin' => '2014-02-25 13:33:38.1234',
'type' => 1,
'level' => 199
)
);
*/
$this->view->count = $resultset['found_rows'];
if ($resultset['found_rows'] > 0 || $this->_request->isXmlHttpRequest()) {
$grid = new Bvb_Grid_Deploy_JqGrid($this->_gridconfig);
$grid->setSource(new Bvb_Grid_Source_Array($resultset['body']));
$grid->getSource()->setTotalRecords($resultset['found_rows']);
$grid->updateColumn('username', array('width' => '110',));
$grid->updateColumn('lastlogin', array('width' => '110',));
$grid->updateColumn('type', array('width' => '110',));
$grid->updateColumn('level', array('width' => '110',));
$grid->setJqgParams(array('caption' => 'resultset'
, 'forceFit' => true
, 'viewrecords' => true
, 'rowList' => array(15, 30, 50)
, 'altRows' => false
)
);
$grid->ajax(get_class($grid));
$grid->setNumberRecordsPerPage(30);
$grid->setExport(array());
$this->view->grid = $grid->deploy();
}
I have tried to examine the BvbGrid's code, but found nothing strange.
In another project the same code is used, and it works fine, without any problems on searching in any column. I have dump'd the API response to be sure it gives me 3000 records, and it actually is and they are in the way as in the comment. I really cannot find the reason why i.e. the lastlogin column searches only on the current page.
If someone can help me, or give me directions where to look at to fix the issue (maybe somewhere in the BvbGrid's code?) I will appreciate it.
Thank you in advance.