How to "group by" field "count" in mongodb querybuilder? - php

I want to classify cities with the products they have.
I have two documents: Product and city. The product has its ID and a reference to a city document:
Product(id) City
P1 ------------ Atlanta
P2 ------------ New York
P3 ------------ Michigan
P4 ------------ Atlanta
....
I want as result of the query
[Atlant => 23, New York => 35, Michigan => 23, etc..]
But I not being able to get the result.
My actual code is
public function countBestUsers()
{
return $this->createQueryBuilder()
->select('id', 'city')
->distinct('city')
->getQuery()
->execute()
;
}

You can try with group and reduce, this example works for me:
$count = $dm->createQueryBuilder()->hydrate(false)
->group(array('city.$id' => 1), array('value' => 0))
->reduce('function (curr,result) {
result.value++;
}')
->getQuery()
->execute()->toArray();
If you need more examples : http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/query-builder-api.html#group-queries
If you will sort it by the quantities, I recommend use aggregate Framework:
$pipeline = array('$group' => array(
'_id' => '$city',
'value' => array('$sum' => 1)
)));
array_push($pipeline, array( '$sort' => array('value' => -1) ) );
$count = $dm->getDocumentCollection('YourBundle:Product')
->aggregate($pipeline)->toArray();

Related

SQL query to take avg of column data into JSON for google pie chart in PHP

I need to create a SQL query and the PHP code to enter this data into JSON format for a pie chart using Google Charts API.
+--------+---------+---------+---------+
| City | P1 | P10 | P25 |
+--------+---------+---------+---------+
|Dubai | 45| 135| 136|
|SanDiego| 23| 34| 45|
|SanFran | 37| 39| 28|
+--------+---------+---------+---------+
This is the query I have already tried:
<?php
$rows2 = array();
$table2 = array();
$query2 = 'SELECT AVG(`P1`) AS avg_p1, AVG(`P10`) AS avg_p10, AVG(`P25`) (SELECT `P1`, `P10`, `P25`
FROM `INFORMATION_SCHEMA`.`COLUMNS` AS pmname
WHERE `TABLE_SCHEMA`='g1109689'
AND `TABLE_NAME`='realtime') AS avg_p25 FROM `realtime` WHERE `City`="Dubai"';
$result2 = mysqli_query($conn, $query2);
$table2['cols'] = array(
array(
'label' => 'PM Type',
'type' => 'string'
),
array(
'label' => 'PM Number',
'type' => 'number'
)
);
while($row2 = mysqli_fetch_array($result2))
{
$sub_array2 = array();
$sub_array2[] = array(
"v" => $row2["avg_p1"]
);
$sub_array2[] = array(
"v" => $row2["avg_p10"]
);
$sub_array[] = array(
"v" => $row2["avg_p25"]
);
$rows2[] = array(
"c" => $sub_array2
);
}
$table2['rows'] = $rows2;
echo $jsonTable2;
?>
I want the categories for the pie chart to be the averages of P1, P10, P25, respectively. So how do I create a SQL statement to select the averages and the name of the columns and how do I put that into a JSON table? Thanks!
I am guessing that you want average rowwise i.e. adding (p1+p10+p25)/3 for every city and not columnwise. So you can try the below query-
select city,(tablename.p1 + tablename.p10 + tablename.p25) / 3 as average from tablename
If you want to calculate avg columnwise for everycity you can use avg() method of sql.
select city, avg(p1),avg(p10),avg(p25) from tablename;
PS: you will only get name of one city if you use avg() function

How to seperate values from joined table result?

I have a simple joined table that looks like this.
NO CLASS NAME STATUS
1 1A JOHN 1
2 1A SARA 1
3 1A LYOD 1
4 1B JOHN 1
5 1B SHIN 1
I use textbox to display the CLASS and a TEXTAREA to display the NAME. The output of CLASS 1A should like this:
Class : 1A
Name : JOHN, SARA, LYOD
How to do it using the joined table only (not multiple query)?
THis is how I loop the result:
$query = $this->xxx->yyy($class_name); //JOINED RESULT
$data = array(
'titlepage' => APP_TITLEPAGE,
'record' => $query, //This is the result that is sent to the view
'complete' => 'true',
'loadmethod' => 'add',
'contentpage' => 'test_page/detail'
);
$this->load->view('shared/master_app', $data);
so your query array is something like this:
$query = [
['NO' => 1, 'CLASS' => '1A', 'NAME' => 'JOHN', 'STATUS' => '1'],
['NO' => 2, 'CLASS' => '1A', 'NAME' => 'SARA', 'STATUS' => '1'],
['NO' => 3, 'CLASS' => '1A', 'NAME' => 'LYOD', 'STATUS' => '1'],
['NO' => 4, 'CLASS' => '1B', 'NAME' => 'JOHN', 'STATUS' => '1'],
];
You may make a function that arrange it or group it
lets name it groupResults($query)
function groupResults($query) {
$result = [];
foreach($query as $queryItem) {
$result[$queryItem['CLASS']][] = $queryItem['NAME'];
}
return $result;
}
now result is looks like this:
$result = [
'1A' => ['JOHN', 'SARA', 'LYOD'],
'1B' => ['JOHN']
]
this is a snapshot of what I got:
so now you can use it like this
$query = $this->xxx->yyy($class_name); //JOINED RESULT
$data = array(
'titlepage' => APP_TITLEPAGE,
'record' => groupResults($query), //This is the result that is sent to the view
'complete' => 'true',
'loadmethod' => 'add',
'contentpage' => 'test_page/detail'
);
$this->load->view('shared/master_app', $data);
now in the view you may loop on this array as:
foreach($record as $class => $names) {
echo "<input type='text' value='" . $class . "'>";
echo "<textarea>" . implode(', ', $names) . "</textarea>";
}
If you want to add all name in same Class in one column then it can be possible by GROUP_CONCAT() mysql function.
adjust your query with GROUP_CONCAT. For an example see below
SELECT CLASS, GROUP_CONCAT(NAME)
FROM table_name
GROUP BY CLASS;
It will Output like this
1A JOHN,SARA,LYOD
2A JOHN,SHIN
You also can put custom separator between names using GROUP_COCAT separator
SELECT CLASS, GROUP_CONCAT(NAME SEPARATOR ' -- ')
FROM table_name
GROUP BY CLASS;
For reference MySql GROUP_CONCAT()

Yii Framework : Help to translate relational Sql query

I encountered a problem to write this query in a Yii model oriented style.
I have a 3 main tables : questions, categories, countries
and relational tables : questions_has_categories, questions_has_countries.
Now i try to search questions that belongs to country, category.
With normal SQL statement i write :
SELECT q.id, q.question, c.name, co.name
FROM questions AS q,
categories AS c,
countries AS co,
questions_has_countries AS ta,
questions_has_categories AS qhc
WHERE
q.id = qhc.questions_id
AND c.id = qhc.categories_id
AND q.id = ta.questions_id
AND co.id = ta.countries_id
AND c.id = 1
AND co.id = 2
This works fine.
Now with Yii Active Record i try to search like this :
$model = Questions::model()->with(
array( 'categories' => array (
'select' => 'name',
'condition' => 'categories.id=:cat_id',
'params' => array (':cat_id' => $_POST["Questions"]["categories"])
)
),
array( 'countries' => array (
'select' => 'name',
'condition' => 'countries.id=:cou_id',
'params' => array (':cou_id' => $_POST["Questions"]["countries"])
)
)
)->findAll(array ('select' => 'question'));
This code respects only the categories, not countries ( returns for all countries, doesnt filter, narrow down ).
What am i doing wrong ?
My Question Model relations function looks like this :
public function relations()
{
return array('categories' => array(self::MANY_MANY, 'Categories', 'questions_has_categories(questions_id, categories_id)'),
'countries' => array(self::MANY_MANY, 'Countries', 'questions_has_countries(questions_id, countries_id)'),
);
}
Pls help :)
Thanks
try this
$model = Questions::model()->with(
array( 'categories' => array (
'select' => 'name',
'condition' => 'categories.id=:cat_id',
'params' => array (':cat_id' => $_POST["Questions"]["categories"])
),
'countries' => array (
'select' => 'name',
'condition' => 'countries.id=:cou_id',
'params' => array (':cou_id' => $_POST["Questions"]["countries"])
)
)
)->findAll(array ('select' => 'question'));

Best match using MySQL and PHP

I'm tackling my first project using PHP/MySQL in which I have a list of cities and ratings from 1-5 in certain categories (Food, Shopping, etc.). What I'm wanting to do is evaluate each row (each City), when a form is submitted on whether the categories are important or not.
This is how I want it to work.
Say, for example:
1. Chicago Food: 4, Shopping: 4, Nightlife: 4
2. New York Food: 4, Shopping: 5, Nightlife: 5
3. Boston Food: 5, Shopping: 4, Nightlife: 3
(the ratings are just for example)
And the user says that Food isn't important. Therefore the code will only evaluate Shopping and Nightlife... New York ends with 10, Chicago with 8 and Boston with 7.
As I have a list of around 35-40 cities that I want to evaluate on each category (if the user deems it "important") dynamically, and the winner will be the highest number at the end of the evaluation.
Does anyone have any ideas on how to go about this? I have the table built in MySQL with all the ratings, just need to write the code out now.
What I've tried: bringing in all of the values using arrays, but I've found it difficult to loop through each of the rows... help!
You can accomplish this task with just a little bit of PHP code and an appropiate SQL statement.
Here is a possible solution:
$important_cat = $_POST['categories']; //which is an array
$sql = "SELECT city, sum(".implode(' + ',$important_cat).") AS cat
FROM tbl
ORDER BY cat DESC";
//query sql
Assuming database tables similar to this (at least, they should be normalized in this fashion):
city ( id, name );
category ( id, name );
rating ( city_id, category_id, rating );
... with an array of interests similar to this:
$interests = array(
'Food',
'Shopping'
);
... the following sql:
$sql = 'SELECT
city.name as city,
GROUP_CONCAT( category.name || ": " || rating.rating, ", " ) as ratings,
SUM( rating.rating ) as totalRating
FROM
rating
JOIN
city
ON city.id = rating.city_id
JOIN
category
ON category.id = rating.category_id
WHERE
category.name IN( ' . implode( ',', array_map( array( $db, 'quote' ), $interests ) ) . ' )
GROUP BY
city.name
ORDER BY
totalRating DESC';
(I assumed the use of PDO, utilizing PDO::quote() for escaping here, but substitute the callback array( $db, 'quote' ) with whatever quoting/escape mechanism your mysql library offers)
... will yield a result set similar to this (I've populated random rating data for my example):
array (
0 => array (
'name' => 'Chicago',
'ratings' => 'Food: 3, Shopping: 3',
'totalRating' => '6'
),
1 => array (
'name' => 'New York',
'ratings' => 'Food: 1, Shopping: 4',
'totalRating' => '5'
),
2 => array (
'name' => 'Seattle',
'ratings' => 'Food: 4, Shopping: 1',
'totalRating' => '5'
),
3 => array (
'name' => 'Los Angeles',
'ratings' => 'Food: 2, Shopping: 2',
'totalRating' => '4'
),
4 => array (
'name' => 'Boston',
'ratings' => 'Food: 1, Shopping: 2',
'totalRating' => '3'
),
5 => array (
'name' => 'San Francisco',
'ratings' => 'Food: 1, Shopping: 1',
'totalRating' => '2'
)
)
If you only need the first result, append LIMIT 1 to the sql query.
This should give you an idea of how to go about accomplishing what you want.
Above all: let MySQL do all the work (filtering, sorting) — not PHP.

Group results after join?

So, I have three tables. Movies, movies_genres and genres. I want to get a movie by its Id, and also join its genres in the result. I managed to join the results, but it doesn't display as i want it to. I'm not sure if what I'm asking is possible.
This is my query:
SELECT `movies`.*, GROUP_CONCAT(genres.id) AS genre_id, GROUP_CONCAT(genres.name) AS genre_name
FROM (`movies`)
INNER JOIN `movies_genres`
ON `movies_genres`.`movie_id` = `movies`.`id`
INNER JOIN `genres`
ON `genres`.`id` = `movies_genres`.`genre_id` WHERE `movies`.`id` = 19908
GROUP BY `movies`.`id`
The query was generated by Codeigniters Active Record class, here is the Codeigniter code if that helps:
$this->db->select('movies.*, GROUP_CONCAT(genres.id) AS genre_id, GROUP_CONCAT(genres.name) AS genre_name');
$this->db->from('movies');
$this->db->where('movies.id', $movie_id);
$this->db->join('movies_genres', 'movies_genres.movie_id = movies.id', 'inner');
$this->db->join('genres', 'genres.id = movies_genres.genre_id', 'inner');
$this->db->group_by('movies.id');
Here is the result i'm currently getting:
Array
(
[id] => 19908
[movie_title] => Zombieland
[overview] => An easily spooked guy...
[genre_id] => 28,12,35,27
[genre_name] => Action,Adventure,Comedy,Horror
)
And this is what I want:
Array
(
[id] => 19908
[movie_title] => Zombieland
[overview] => An easily spooked guy...
[genres] => array(
0 => array(
'id' => 28,
'name' => Action
),
1 => array(
'id' => 12,
'name' => Adventure
),
1 => array(
'id' => 35,
'name' => Comedy
),
1 => array(
'id' => 27,
'name' => Horror
)
)
)
Is this possible, and if so, how?
The query you listed will have n rows (where n = # of movies) whereas the query it seems you want will have many more rows (# of movie_genre's entries). You're probably better off leaving that query as it is, and doing some post processing.
Consider:
After you get it, just run your result (e.g. $result) array through something like:
foreach($result as &$row)
{
// Split over commas
$gi_elements = explode(',', $row['genre_id']);
$gn_elements = explode(',', $row['genre_name']);
// Build genre
$row['genre'] = array();
for($i=0; $i<count($gi_elements); $i++)
{
$row['genre'][] = array('id' => $gi_elements[$i], 'name' => $gn_elements[$i]);
}
// Cleanup
unset($row['genre_id']);
unset($row['genre_name']);
}
Afterwards, $results will look exactly as you wish without extra database work.
EDIT: Fixed some typos.

Categories