I need to divide my search result into two parts. 1 with those goods in which the number> 0 sort them by price and withdraw first. 2 products whose quantity = 0 sort by price and display at the end, after those products that are in stock. The main thing is that in the first group of goods (whose quantity> 0) there were no goods from the second group (whose quantity = 0) What unfortunately happens when I sort by two conditions
Use PHP 7.1
and Elastic Search 6.6.0
Small example, there is a table of goods
id | site_price | count
1 | 10 | 0
2 | 5 | 5
3 | 15 | 2
4 | 20 | 10
5 | 15 | 0
I need to sort first by quantity, and then by price (without losing the first sorting).
First sort: ('count'=>'desc').
Second sort: ('site_price'=>'asc').
Should get this result:
id | site_price | count
2 | 5 | 10
3 | 15 | 5
4 | 20 | 2
1 | 10 | 0
5 | 15 | 0
$this->params['body'] = array(
'from' => ($filters['page'] - 1) * 15,
'size' => 15,
'query' => array(
'bool' => array(
'must' => array(
"query_string" => array(
'query' => "*" . $filters['text'] . "*",
)
),
)
),
'sort' => array(
array("shops_count" => "desc"),
array("site_price" => "asc")
)
);
$result = $this->client->search($this->params);
It looks like that you want to achieve behavior similar to UNION in SQL, since you first want to split the result set into 2 groups, sort each group and then attach one group after another.
There are a few ways to do it.
1) By doing 2 queries
Like in this answer, it is suggested to do 2 queries:
POST /orders/_search
{
"query": {
"range": {
"count": {
"gt": 0
}
}
},
"sort" : [
{"site_price": "asc"},
]
}
POST /orders/_search
{
"query": {
"range": {
"count": {
"gte": 0,
"lte": 0
}
}
},
"sort" : [
{"site_price": "asc"},
]
}
And then joining them on the client side.
There is also a way to do it completely on the Elasticsearch side.
2) By using script sorting
We can use script based sorting and sort first on the availability (count > 0), then by price:
POST /orders/_search
{
"sort" : [
{
"_script" : {
"type" : "number",
"script" : {
"lang": "painless",
"source": "if (doc['count'].value > 0) { 1 } else { 0 } "
},
"order" : "desc"
}
},
{"site_price": "asc"}
]
}
However, scripting always has performance overhead. Solution #1 is more robust, although it performs 2 queries.
Here is another solution that uses single query and does not use expensive scripting.
3) Adding new field - for sorting
If we add a special field, "available", we will not need to use script sorting.
The documents might look like this:
doc1 = {
"id": 1,
"site_price": 10,
"count": 0,
"available": 0
}
doc2 = {
"id": 2,
"site_price": 5,
"count": 5,
"available": 1
}
Then the sorting will look like this:
POST /orders/_search
{
"sort" : [
{"available": "desc"},
{"site_price": "asc"}
]
}
This is a common pattern called denormalization which proves useful when tuning for best performance.
Hope that helps!
#Nikolay, thanks for the help.
Unfortunately, this did not help. I tried rewrote the query - but the result is the same. Here is an example: removed too much left only search and sorting
enter code here
$this->params['body'] = array(
'from' => ($filters['page'] - 1) * 15,
'size' => 15,
'query' => array(
'bool' => array(
'must' => array(
"query_string" => array(
'query' => "*" . $filters['text'] . "*",
)
),
)
),
'sort' => array(
array("shops_count" => "desc"),
array("site_price" => "asc")
)
);
$result = $this->client->search($this->params);
Related
So I'm calling in data from a DB where each record is associated with a (column) 'start_time', 'no_shows and 'cancelled'. Successful attendance is based on whether 'no_shows' && 'cancelled' == 0.
I want to go through the results of this SELECT and count the number of no shows, cancellations and attended on a WEEKLY basis (based on the start_time). How can I achieve this?
The user will provide a date range and the DB will select the records based on that date range. Now this date range will then have to be split by weeks and then get the count. I'm totally stuck on the counting by weeks part. This is what I have so far:
// Multidimensional Array with [boolean][week #]
$no_shows_weekly = [
'no_show' => [],
'week' => []
];
$cancelled_weekly = [
'cancelled' => [],
'week' => []
];
$attended_weekly = [
'attended' => [],
'week' => []
];
foreach($result as $r) {
$start_time = new DateTime($r['start_time']);
if($r['is_no_show'] == 0 && $r['is_cancelled'] == 0) {
array_push($attended_weekly['attended'], 1);
array_push($attended_weekly['week'], date_format($start_time, "W"));
}
else {
array_push($attended_weekly['attended'], 0);
array_push($attended_weekly['week'], date_format($start_time, "W"));
}
array_push($no_shows_weekly['no_show'], $r['is_no_show']);
array_push($no_shows_weekly['week'], date_format($start_time, "W"));
array_push($cancelled_weekly['cancelled'], $r['is_cancelled']);
array_push($cancelled_weekly['week'], date_format($start_time, "W"));
}
echo json_encode(array(
'success'=> 1,
'msg'=>
array(
'No Shows' => $no_shows_weekly,
'Cancellations' => $cancelled_weekly,
'Attendend' => $attended_weekly
)
));
I wasn't able to do any counting in this, rather I just pulled the data and separated into arrays with the corresponding week.
I want to pull data into something like this:
Week 50: {No_Shows: 10, Cancelled: 5, Attended: 25}
Week 52: {No_Shows: 10, Cancelled: 5, Attended: 25}
General DB Structure:
+------------+-----------+-----------+
| start_time | no_shows | cancelled |
+------------+-----------+-----------+
| 2019-12-20 | 1 | 0 |
| 2019-12-21 | 0 | 0 |
| 2019-12-22 | 0 | 1 |
I tried to do this in MySQL as well:
SELECT
WEEKOFYEAR('start_time') AS weekno,
SUM('is_no_show' = 1) AS no_shows,
SUM('is_cancelled' = 1) AS cancelled
FROM
myTable
WHERE
(
`start_time` > '2019-12-01' AND `start_time` < '2019-12-07'
) AND category LIKE 'Continued Probation'
GROUP BY
weekno
However, this statement is returning Null for me. Any suggestions is appreciated! Thank you
i need to know how i can make a array from db tables. i want something like that. i have 3 tables in data base country - state - city
data base structure is as follow
countries-table:
| id | country |
|----|----------------|
| 1 | united kingdom |
states-table
| id | state |
|----|----------------------|
| 1 | united westyorkshite |
cities-table
| id | city |
|----|----------------------|
| 1 | united wakefield |
The expected result:
{
value: '12',
label: 'United Kingdom',
children: [
{
values: '34',
labels: 'West YorkShire',
children: [
{
value: '400',
label: 'Dewsbury'
},
{
value: '401',
label: 'Wakefield'
}
]
}
This is what I've tried:
$country_array = array();
$array = Country::select('countries.country', 'countries.country_id')
->isDefault()
->active()
->sorted()
->pluck('countries.country', 'countries.country_id')
->toArray();
// print_r($array);
foreach($array as $key => $arr) {
$array_state = State::select('states.state', 'states.state_id')
->where('states.country_id', '=', $key)
->isDefault()
->active()
->sorted()
->pluck('states.state', 'states.state_id')
->toArray();
foreach($array_state as $key_1 => $arr_1) {
$array_city = City::select('cities.city', 'cities.city_id')
->where('cities.state_id', '=', $key_1)
->active()
->sorted()
->pluck('cities.city', 'cities.city_id')
->toArray();
$array_state_1 [] = array (
'value' => $key_1,
'label' => $arr_1,
'children' => $array_city
);
}
$country_array [] = array (
'value' => $key,
'label' => $arr,
'children' => $array_state
);
}
dd($country_array);
I need these labels as well like value and label. i cant remove that as i am using vuejs element ui component.
I try to use normal foreach loop but its doing time out. any idea how i can do that in laravel using maps or collection or what is the best solution.
I am using php 7 and laravel version is 5.6
To retrieve all necessary data in a single statement, I would use nested with's. For example:
$result = Country::where('country_id', 12)
->with(['states' => function ($query) {
$query->where('state_id', 34);
$query->with('cities');
}])
->get();
The next step is to modify the data. The map() function is indeed very useful for this purpose, although the nesting makes it look a little complex:
$mapped = $result->map(function ($country, $key) {
return [
'value' => $country->country_id,
'label' => $country->country,
'children' => $country->states->map(function ($state, $key) {
return [
'value' => $state->state_id,
'label' => $state->state,
'children' => $state->cities->map(function ($city, $key) {
return [
'value' => $city->city_id,
'label' => $city->city,
];
}),
];
}),
];
});
Finally to display it, we convert the whole thing to a JSON array.
echo '<pre>' . $mapped->toJson(JSON_PRETTY_PRINT) . '</pre>';
Which gives me:
[
{
"value": 12,
"label": "United Kingdom",
"children": [
{
"value": 34,
"label": "West YorkShire",
"children": [
{
"value": 400,
"label": "Dewsbury"
},
{
"value": 401,
"label": "Wakefield"
}
]
}
]
}
]
I have some data in mysql that I need to create some reports from.
My data are coming from the following query :
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl GROUP BY StoreNbr,StoreName,Date;
This results in the following data (just a small subset for my example):
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | Date | SUM(Sales) |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 2016-01-16 | 115.09 |
| 1112 | Store1 | 2016-01-17 | 81.00 |
| 1113 | Store2 | 2016-01-16 | 112.44 |
| 1113 | Store2 | 2016-01-17 | 56.61 |
I would like to transform my data to be this way :
+-----------+---------------------------+----------------------------+
| StoreNbr | StoreName | 2016-01-16 | 2016-01-17 |
+-----------+---------------------------+----------------------------+
| 1112 | Store1 | 115.09 | 81.00 |
| 1113 | Store2 | 112.44 | 56.61 |
Obviously there might be thousands of rows (stores) and unknown number of dates to be returned in the query as my query might be run like this (this will need to return 120+ number of columns for dates):
SELECT StoreNbr,StoreName,Date, SUM(`Sales`) FROM sales_tbl WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10') GROUP BY StoreNbr,StoreName,Date;
There are a few ways to do this, none very simple. I did some research and there are some that mention that mysql does not support pivoting. I am running mariadb though, and saw that mariadb supports pivoting through the connect engine. I was unable to make it work though (adjust their official examples on my data).
Another way is lots of IFs and Cases, but most of the answers I am finding are very difficult to adapt or are tailored only to the data the guy that asks provides.
Another approach would be to process the data as they come out on my array as I have a json response in the end that feeds a datatable. - This is another think I have not managed to figure out yet.
I am looking for a way to get the desired output independent on the amount of dates (and I guess dates could be replaced by weeks or whatever else). Can anyone help?
Select all distinct dates
SELECT DISTINCT Date
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
ORDER BY Date;
and initialize an array which is indexed by that dates storing zeros:
$dateIndexedArray = array();
while($row = $stmt1->fetch(PDO::FETCH_ASSOC) {
$dateIndexedArray[$row['Date']] = 0;
}
The arry will look like
[
'2016-01-16' => 0,
'2016-01-17' => 0
]
Then execute your query
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date;
And store the "Sales" in a date indexed array per store
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = array(
'StoreNbr' => $row['StoreNbr'],
'StoreName' => $row['StoreName'],
'Sales' => $dateIndexedArray
);
}
$report[$storeIndex]['Sales'][$row['Date']] = $row['Sales'];
}
The $report array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1',
'Sales' => [
'2016-01-16' => 115.09,
'2016-01-17' => 81.00
]
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'Sales' => [
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
]
Update:
If you need all data to be in one row for each store you can change the code to:
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
The resulting array will look like:
[
'1112:Store1' => [
'StoreNbr' => 1112,
'StoreName' => 'Store1'
'2016-01-16' => 115.09,
'2016-01-17' => 81.
],
'1113:Store2' => [
'StoreNbr' => 1113,
'StoreName' => 'Store2',
'2016-01-16' => 112.44,
'2016-01-17' => 56.61
]
]
Update 2: To get the total sales per store you can use WITH ROLLUP
SELECT StoreNbr, StoreName,Date, SUM(`Sales`) AS Sales
FROM sales_tbl
WHERE (Date BETWEEN '2016-01-10' AND '2016-05-10')
GROUP BY StoreNbr,StoreName,Date WITH ROLLUP;
$report = array();
while($row = $stmt2->fetch(PDO::FETCH_ASSOC)){
if ($row['StoreName'] === null) {
// Skip this row.
// It contains total sales grouped by StoreNbr
// (or not grouped if StoreNbr === null).
continue;
}
$storeIndex = $row['StoreNbr'] . ':' . $row['StoreName'];
if (!isset($report[$storeIndex])) {
$report[$storeIndex] = $dateIndexedArray;
$report[$storeIndex]['StoreNbr'] = $row['StoreNbr'];
$report[$storeIndex]['StoreName'] = $row['StoreName'];
}
if ($row['Date'] === null) {
// This row contains total sales grouped by StoreNbr & StoreName
$report[$storeIndex]['TotalSales'] = $row['Sales']
} else {
$report[$storeIndex][$row['Date']] = $row['Sales'];
}
}
Please note that i've never used WITH ROLLUP. So you might need to adjust the code.
Amazon's Product API limits us to get only 10 items per page, and only 10 pages at a certain query.
I have developed a code that would almost get all items;
first, I have supplied a params that looks like this:
$item_params = [
"Service" => "AWSECommerceService",
"Operation" => "ItemSearch",
"AWSAccessKeyId" => env('AWS_ACCESS_KEY_ID'),
"AssociateTag" => env('AWS_ASSOCIATE_TAG_ID'),
"SearchIndex" => "HomeGarden",
"ResponseGroup" => "ItemAttributes,SalesRank,Offers",
"Sort" => "-price",
"BrowseNode" => $item_params['BrowseNode'],
"MaximumPrice" => $max_price,
"MinimumPrice" => "0"
];
then, the code will get all items under that browse node (category), SORTED BY PRICE (desc) also by specifying the MAX and MIN Price of the items to limit the search.
the pseudo-code (original code is too long)
function getProducts($item_params, $max_price = null){
$products = //request to amazon
foreach ($product as $key=>$value){
//add product to db
}
// if the total number of results on the query is not equal to zero, continue looping
if (!$products->totalResults() == 0){
$product = //get the first lowest priced item on the db
$this->getProducts($item_params, $product->price);
}
}
however I am experiencing this scenario :
Sample request output (assuming all items from amazon):
ASIN(unique id) | Price
1 | 201
2 | 194
3 | 195
.
.
n | 33
n+1 | 33
n+2 | 33
.
n+120 | 33
n+121 | 34
n+122 | 35
wherein the products from n to n+120 are equal. This will create an infinite loop to my getProducts function. How can I avoid this? Knowing that only 10 items are returned on each request and only 10 pages.
How can I avoid this?
I don't think you can with just using price. You have to divide your search into multiple sub-searches by using additional keywords. For example, if you're searching for "laptop", instead do searches on "laptop asus", "laptop dell", etc.
You can also filter on Browse node IDs, so if your results come from multiple browse nodes, you can do two or more searches.
Add the ItemPage parameter and increment it in a loop. You should be able to get up to 100 unique ASINs (10 pages of 10 products per page).
$page = 1;
while($page <= 10) {
$item_params = [
"Service" => "AWSECommerceService",
"Operation" => "ItemSearch",
"AWSAccessKeyId" => env('AWS_ACCESS_KEY_ID'),
"AssociateTag" => env('AWS_ASSOCIATE_TAG_ID'),
"SearchIndex" => "HomeGarden",
"ResponseGroup" => "ItemAttributes,SalesRank,Offers",
"Sort" => "-price",
"BrowseNode" => $item_params['BrowseNode'],
"MaximumPrice" => $max_price,
"MinimumPrice" => "0",
"ItemPage" => $page
];
// execute query and save data
//increment page number
$page++;
}
I'm just starting to learn more advanced SQL along with PHP and I'm really struggling trying to find out how to query my database for a quiz I'm building.
Ultimately, I'm trying to return a json object with the following structure which gives me a list of questions and all possible answers as a multidimensional array:
{
"questions":
[
{
"question": "question text here",
"answers":
[
{ "answer": "answer text here", "points": 10 },
{ "answer": "answer text here", "points": 20 },
{ "answer": "answer text here", "points": 30 },
{ "answer": "answer text here", "points": 40 }
]
},
{
"question": "question text here",
"answers":
[
{ "answer": "answer text here", "points": 10 },
{ "answer": "answer text here", "points": 20 },
{ "answer": "answer text here", "points": 30 },
{ "answer": "answer text here", "points": 40 }
]
}
]
{
...from my mySQL tables of the following structure:
quiz
id | title
1 | quiz title here
quiz_question
id | quiz_id (FK) | question_text
1 | 1 | question text here
2 | 1 | question text here
quiz_answer
id | quiz_question_id (FK) | answer_text | points
1 | 1 | answer text here | 10
2 | 1 | answer text here | 20
3 | 1 | answer text here | 30
4 | 1 | answer text here | 40
...with the following foreign keys:
quiz_question.quiz_id is FK to quiz.id
quiz_answer.quiz_question_id is FK to quiz_question.quiz_id
...using the following PHP (in it's simplest form which is currently only returning my questions):
//query the db
$query = mysql_query("
SELECT quiz_question.question_text
FROM quiz_question
JOIN quiz ON quiz.id = quiz_question.quiz_id
WHERE quiz.id = 1;
");
$numrows = mysql_num_rows($query);
for ($i = 0; $i < $numrows; $i++) {
$row = mysql_fetch_assoc($query);
$quiz_data[$i] = array("question" => $row["question_text"]);
}
//echo JSON to page
$response = $_GET["jsoncallback"] . "(" . json_encode($quiz_data) . ")";
echo $response;
...and using jQuery's $.getJSON() in my JavaScript which gets my a JSON formatted object from my PHP which gets me back the following:
[
{"question":"question text here"},
{"question":"question text here"}
]
So my question is, how can I write my SQL and PHP to create a multidimensional array like the very above instead of a single array like I'm currently getting back now? I need to figure out how to include the questions and all associated answers as a multidimensional array.
You can't retrieve a multi-dimensional array purely with mysql (at least as far as I know). You will have to do some php processing. This doesn't sound too crazy.
First, update your query to select answers at the same time by joining quiz_answers on quiz_questions using the question ID. Then, in your loop:
$quiz = array();
while ($row = mysql_fetch_assoc($result)) {
// you don't need to check num_rows
// fetch_assoc returns false after the last row, so you can do this
// which is cleaner
if (!isset($quiz[$row['question_id'])) {
$quiz[$row['question_id']] = array(
'question' => $row['question_text']
, 'answers' => array()
);
}
$quiz[$row['question_id']]['answers'][] = $row['answer_text'];
}
$full = json_encode(array('questions' => $quiz'));
This will give you the array you want after it's json encoded.
Note that you will end up selecting the question text/id once per each answer, which is inefficient. You can use GROUP_CONCAT on the answers, but the above will still work almost identically, you just have to split the answer string.
I also suggest you use PDO or some other wrapper over mysql_*.
As far as I know, you'll need to build the multi-dimensional array after pulling the results from the database.
You could probably do a join on the questions and answers, so the resulting array would look something like this:
$results = array(
array( 'question' => 'question 1', 'answer' => 'answer 1', 'points' => 10 ),
array( 'question' => 'question 1', 'answer' => 'answer 2', 'points' => 30 ),
array( 'question' => 'question 2', 'answer' => 'answer 1', 'points' => 20 ),
array( 'question' => 'question 2', 'answer' => 'answer 2', 'points' => 50 )
);
Then from there you could build your json array by grouping the questions and answers together.
Basically, php and the standard mysql_query functions won't build the multi-dimensional arrays, so you'll have to pull the data and build it yourself.
You just run your queries and then create a compound data-structure based upon it ($questions); Example:
$questions= array();
$rowsQuestions = $gateway->findQuestions($quiz);
foreach($rowsQuestions as $row)
{
$questions[$row->id] = new stdClass;
$questions[$row->id]->question = $row->question_text;
}
$rowsAnswers = $gateway->findAnswers($quiz);
foreach($rowsAnswers as $row)
{
$answer = (object) array(
'answer' => $row->answer_text,
...
);
$questions[$row->quiz_question_id]->answers[] = $answer;
}
The previous answers will all boast greater performance than what I'm about to suggest, but this article offers a flexible solution for "joined" results, especially as nested joins build off one another.
PHP Rebuild Database Result Array
Run the result through json_encode() to convert it to a JSON object.