I am trying to get count of rows of select with multiple joins that is grouped (same problem as here but mentioned answer is not fully suitable).
I have code like this:
$this->createQueryBuilder('table1')
->select('count(table1.id)')
->leftJoin('table1.col1', 'table2')
->leftJoin('table1.col2', 'table3')
->leftJoin('table1.col3', 'table4')
->groupBy('table1.id')
->getQuery()
->getScalarResult();
As a result i have array like:
[
["1" => "1"],
["1" => "2"],
["1" => "1"],
["1" => "3"],
["1" => "3"],
...
]
But i need number of rows of result array.
Is it possible to do it in single query?
Can you try using getSingleScalarResult()?
Related
I'm fairly new to ElasticSearch, currently using v6.2 and I seem to have run into a problem while trying to add some aggregations to a query. Trying to wrap my head around the various types of aggregation, as well as the best ways to store the data.
When the query runs, I have some variable attributes that I would like to aggregate and then return as filters to the user. For example, one character may have attributes for "size", "shape" and "colour", while another only has "shape" and "colour".
The full list of attributes is unknown so I don't think I would be able to construct the query that way.
My data is currently structured like this:
{
id : 1,
title : 'New Character 1',
group : 1,
region : 1,
attrs : [
moves : 2,
# These would be dynamic, would only apply to some rows, not others.
var_colours : ['Blue', Green', 'Red'],
var_shapes : ['Round', 'Square', 'Etc'],
effects : [
{ id : 1, value: 20},
{ id : 2, value: 60},
{ id : 3, value: 10},
]
]
}
I currently have an aggregation of groups and regions that looks like this. It seems to be working wonderfully and I would like to add something similar for the attributes.
[
'aggs' => [
'group_ids' => [
'terms' => [
'field' => 'group',
'order' => [ '_count' => 'desc' ]
]
],
'region_ids' => [
'terms' => [
'field' => 'region',
'order' => [ '_count' => 'desc' ]
]
]
]
]
I'm hoping to get a result that looks like the below. I am also not sure if the data structure is setup in the best way either, I can make changes there if necessary.
[aggregations] => [
[groups] => [
[doc_count_error_upper_bound] => 0
[sum_other_doc_count] => 0
[buckets] => [
[0] => [
[key] => 5
[doc_count] => 27
],
[1] => [
[key] => 2
[doc_count] => 7
]
]
],
[var_colours] => [
[doc_count_error_upper_bound] => 0
[sum_other_doc_count] => 0
[buckets] => [
[0] => [
[key] => 'Red'
[doc_count] => 27
],
[1] => [
[key] => 'Blue'
[doc_count] => 7
]
]
],
[var_shapes] => [
[doc_count_error_upper_bound] => 0
[sum_other_doc_count] => 0
[buckets] => [
[0] => [
[key] => 'Round'
[doc_count] => 27
],
[1] => [
[key] => 'Polygon'
[doc_count] => 7
]
]
]
// ...
]
Any insight that anyone could provide would be extremely appreciated.
You should do this within your PHP script.
I can think of the following:
Use the Dynamic field mapping for your index.
By default, when a previously unseen field is found in a document, Elasticsearch will add the new field to the type mapping. This behaviour can be disabled, both at the document and at the object level, by setting the dynamic parameter to false (to ignore new fields) or to strict (to throw an exception if an unknown field is encountered).
Get all the existing fields in your index. Use the Get mapping API for this.
Loop over the results of Step 2 so you can get all the existing fields in your index. You can store them in a list (or array), for example.
You can create a PHP Elasticsearch terms aggregation for each of the fields in your list (or array). This is: create an empty or base query with no terms aggregation and add one terms for each element you got from step 3.
Add to each terms, the missing field with an empty empty string ("").
That's it. Following this, you have creating a query in such way that, no matter what index you're searching, you'll get a terms agg with all the existing fields for it.
Advantages:
Your terms aggregations will be generated dynamically with all the existing fields.
For each of the doc that does not contain any of the fields, an empty string will be shown.
Disadvantages:
Looping through the GET mapping API's result could be a little frustrating (but I trust you).
Performance (time & resources) will be affected for every new field you find in your mappings.
I hope this is helpful! :D
I'm working on a PHP project to rank athletes based on their distance, age and finish time.
I'm storing the athletes data on a MongoDB Collection, retrieving their data and doing the calculations, which takes less than a second to do on over 10000 records.
After that, I have to update each one using the BIB number (which is unique an therefore can be used as an ID) and the calculated ranking.
After all the calculations I get an array like that, which extends for the 10k records.
{
[ bib =>"1922", category => "ELITF5", ranking => "1"],
[ bib =>"7996", category => "ELITF5", ranking => "2"],
[ bib =>"4361", category => "ELITF5", ranking => "3"],
[ bib =>"1954", category => "ELITM5", ranking => "1"],
[ bib =>"4812", category => "ELITM5", ranking => "2"],
[ bib =>"7916", category => "ELITM5", ranking => "3"],
[ bib =>"6628", category => "F0015", ranking => "1"],
[ bib =>"194", category => "F0015", ranking => "2"],
[ bib =>"767", category => "F0015", ranking => "3"],
[ bib =>"776", category => "F0015", ranking => "4"],
[ bib =>"5383", category => "F0015", ranking => "5"],
...
}
To update the whole 10k records one by one would take about two minutes using a foreach loop as we have in the snippet bellow.
foreach ($collection as $records) {
$update = $mongoCollection->updateOne(
['bib' => $records['bib']],
['$set' => ['ranking' => $records['ranking']]]
);
}
Is there any faster / more elegant way to update the whole collection filtering by the bib and setting the corresponding ranking field?
I am trying to get the number of unique Brands from my Products Table with their count from a Laravel collection.
I was able to do that using a specific query for the products but the reason i'm using a collection now is because i also want to get the Product Origins (Country), Conditions (Used / New) of the products and i thought it would be much better to use a collection from one query rather than have three separate queries for each data.
The code below works but it doesn't show the count for each unique Brand.
Here is the Table
Here is my Controller
$products = DB::table('products')
->select('products.*')
->whereNull('products.deleted_at')
->get();
$BrandCollection = collect($products);
$Brands = $BrandCollection->unique('Brand')->sortBy('Brand')->keyBy('Brand')->pluck('Brand');
So, the result i'm looking for is
HP 3
Toshiba 2
Lenovo 1
I thought it could be done using concat for the collection but since i'm on Laravel 5.2, i'm looking for other solutions.
If you really want to use collections (not Eloquent) you can do it like this:
$brandsWithCount = $BrandCollection->groupBy('Brand')->map(function($values) {
return $values->count();
})->sort()->reverse();
For example if you set $brandCollection like this:
$BrandCollection = collect([
['Brand' => 'HP'],
['Brand' => 'HP'],
['Brand' => 'HP'],
['Brand' => 'Toshiba'],
['Brand' => 'Toshiba'],
['Brand' => 'Lenovo'],
]);
result will be:
Collection {#372
#items: array:3 [
"HP" => 3
"Toshiba" => 2
"Lenovo" => 1
]
}
as expected.
There is a Collection Helper called CountBy, does exactly what you need.
Collections CountBy
$BrandCollection->countBy('Brand');
It will retourn as expected
#items: array:3 [
"HP" => 3
"Toshiba" => 2
"Lenovo" => 1
]
Simple :D
MySQL query:
SELECT
seats,
( SELECT tires, bumpers FROM exterior ) AS stuff
FROM
interior
The result I was expecting:
0 => [
'seats' => 'comfort',
'stuff' => [
0 => [
'tires' => 'winter',
'bumpers' => 'front'
],
1 => [
'tires' => 'summer',
'bumpers' => 'back'
],
...
]
],
1 => ...
However, this did not work. Error message: Operand should contain 1 column(s).
Is possible to get the subquery results returned as arrays, the way/format the base query is? Or grouping it this way is only possible with e.g. PHP? Thanks.
A solution for this would be the following:
SELECT seats,
( SELECT CONCAT_WS(',',tires, bumpers) FROM exterior ) AS stuff
FROM interior
// Where $array would be the result of the query
function stringtoarray($string) {
return explode(',',$string);
}
for($i=0;!empty($array);$i++) {
$array[''stuff'] = stringtoarray($array[''stuff']);
}
I realize they don't have the key value yet, but you should be able to fix this within this method as well.
As your "exterior" table has multiple rows, so you can't alias multiple row like this: ( SELECT tires, bumpers FROM exterior ) AS stuff
Either use limit in your sub-query or let me know where there is any relation between "interior" & "exterior" table.If any relation exists, then use join
I have this query in Laravel:
Models::select('*')->group_by('user_name')->order_by(DB::raw('count(user_name)'), 'desc')->take(3)->get();
It returns top 3 users by the number they appear in the table.
Q: How can I also get the count parameter(how many times they appear?)
Current response:
array(
"Tim","John","Luke"
);
I need something like this:
array(
array(
"user" => "Tim",
"count" => 3
),
array(
"user" => "John",
"count" => 2
),
array(
"user" => "Luke",
"count" => 1
)
);
Thanks!
You should do the COUNT in the select part of the query and alias it, then use that alias in the ordering.
Something like this, perhaps.
Models::select('*', DB::raw('count(user_name) AS user_count'))->group_by('user_name')->order_by(DB::raw('user_count'), 'desc')->take(3)->get();