Count By and Find By in same Query - jpa

I'm trying to build a single query using a JPA method name that finds all of the results based on a parameter, then counts based on a second parameter.
Say I have data that looks like this:
ID | Word | Who said it
1 | Apple | Person1
2 | Banana| Person1
3 | Apple | Person1
4 | Apple | Person2
I want to pass in a "Who said it" String and receive a histogram of unique words and how many times they said it. So, if I pass in "Person1", I want to receive:
Apple: 2
Banana: 1
How would I combine both the findByWhoSaidIt(String whoSaidIt) with a countByWord?

Just use a #Query annotation with a native SQL query:
#Query(value = "select word, count(*) where who_said_it = :person group by word",
nativeQuery=true)
Object[] whatWasSaidBy(String person)

Related

Aggregate function to extract all fields based on maximum date

In one table I have duplicate values ​​that I would like to group and export only those fields where the value in the "published_at" field is the most up-to-date (the latest date possible). Do I understand it correctly as I use the MAX aggregate function the corresponding fields I would like to extract will refer to the max found or will it take the first found in the table?
Let me demonstrate you this on simple example (in real world example I am also joining two different tables). I would like to group it by id and extract all fields but only relating to the max published_at field. My query would be:
SELECT "t1"."id", "t1"."field", MAX("t1"."published_at") as "published_at"
FROM "t1"
GROUP By "t1"."id"
| id | field | published_at |
---------------------------------
| 1 | document1 | 2022-01-10 |
| 1 | document2 | 2022-01-11 |
| 1 | document3 | 2022-01-12 |
The result I want is:
1 - document3 - 2022-01-12
Also one question - why am I getting this error "ERROR: column "t1"."field" must appear in the GROUP BY clause or be used in an aggregate function". Can I use MAX function on string type column?
If you want the latest row for each id, you can use DISTINCT ON. For example:
select distinct on (id) *
from t
order by id, published_at desc
If you just want the latest row in the whole result set you can use LIMIT. For example:
select *
from t
order by published_at desc
limit 1

Drools - Finding a single matching condition for a table of products ranked by consumers

I have a table displaying information for the top four ratings of produce in a store. I want to be able to find specific products in this rating table. Here is a structure of the table
----------------------------------------------------------------------------
sectId | product_code | product_category | consumer_raniking
10444 | 11222 | PRODUCE | RATING_1
10444 | 45555 | PRODUCE | RATING_1
10444 | 10005 | PR0DUCE | RATING_1
20555 | 11344 | PRODUCE | RATING_2
20555 | 94003 | PRODUCE | RATING_2
... and so on.
I wrote a rule to find inserted products which ins not working the way I want, i.e. to find the targetted fact that was inserted into the table. Here is the rule I put together:
rule "find by product codes rating_1"
when
$product_table: ProductRanking( $rank1: this.getProductCodesRankFirst())
$product1 : Product( this.product_code memberOf $rank1, $product_code: product_code )
$product2 : Product( this.product_code == 10444,this.product_code != $product_code ,$product_code2: product_code)
then
System.out.println("Found Products for product_codes "+$product_code+ " "+$product_code2 ) ;
end
Unfortunately, this returns 3 rows. I inserted into the session the product in row 2 i.e. product with ocde 45555 and it does find row 2. However, ir also brings in row 1 and row3.
I can see why it's doing that. It's because the skus are in the sectId with sectId 10444. However, I want to only bring in the row
that I inserted, which is sectionId(10444), product_code(45555). How can I achieve that?
I solved it by using a global to filter out the extra products. In the first line that brings the rankings, I eliminate the extra-matching products this way:
global ProductHelper productHelper
$product_table: ProductRanking( $rank1: productHelper.getProductCodesRankFirst(),
productCode != productHelper.getProductCodeFruitCategory() && productCode!=
productHelper.productCodeVegetableCategory())
The ProductHelper identifies the product codes I want to eliminate and hence the extra 2 products brought in are ignored, creating a single match. I'm sure there is a better way, but since I'm no expert, this is what I was able to come up with.

Postgres Query for Beginners

Ok, I deleted previous post and will try this again. I am sure I don't know the topic and I'm not sure if this is a loop or if I should use a stored function or how to get what I'm looking for. Here's sample data and expected output;
I have a single table A. Table has following fields; date created, unique person key, type, location.
I need a Postgres query that says for any given month(parameter, based on date created) and given a location(parameter based on location field), provide me fieds below where unique person key may be duplicated + or – 30 days from the date created within the month given for same type but all locations.
Example Data
Date Created | Unique Person | Type | Location
---------------------------------------------------
2/5/2017 | 1 | Admit | Hospital1
2/6/2017 | 2 | Admit | Hospital2
2/15/2017 | 1 | Admit | Hospital2
2/28/2017 | 3 | Admit | Hospital2
3/3/2017 | 2 | Admit | Hospital1
3/15/2017 | 3 | Admit | Hospital3
3/20/2017 | 4 | Admit | Hospital1
4/1/2017 | 1 | Admit | Hospital2
Output for the month of March for Hospital1:
DateCreated| UniquePerson | Type | Location | +-30days | OtherLoc.
------------------------------------------------------------------------
3/3/2017 | 2 | Admit| Hospital1 | 2/6/2017 | Hospital2
Output for the month of March for Hospital2:
None, because no one was seen at Hospital2 in March
Output for the month of March for Hospital3:
DateCreated| UniquePerson | Type | Location | +-30days | otherLoc.
------------------------------------------------------------------------
3/15/2017 | 3 | Admit| Hospital3 | 2/28/2017 | Hospital2
Version 1
I would use a WITH clause. Please, notice that I've added a column id that is a primary key to simplify the query. It's just to prevent the rows to be matched with themselves.
WITH x AS (
SELECT
id,
date_created,
unique_person_id,
type,
location
FROM
a
WHERE
location = 'Hospital1' AND
date_trunc('month', date_created) = date_trunc('month', '2017-03-01'::date)
)
SELECT
x.date_created,
x.unique_person_id,
x.type,
x.location,
a.date_created AS "+-30days",
a.location AS other_location
FROM
x
JOIN a
USING (unique_person_id, type)
WHERE
x.id != a.id AND
abs(x.date_created - a.date_created) <= 30;
Now a little bit of explanations:
First we select, let's say a reference data with a WITH clause. Think of it as a temporary table that we can reference in the main query. In given example it could be a "main visit" in given hospital.
Then we join "main visits" with other visits of the same person and type (JOIN condition) that happen in date difference of 30 days (WHERE condition).
Notice that the WITH query has the limits you want to check (location and date). I use date_trunc function that truncates the date to specified precision (a month in this case).
Version 2
As #Laurenz Albe suggested, there is no special need to use a WITH clause. Right, so here is a second version.
SELECT
x.date_created,
x.unique_person_id,
x.type,
x.location,
a.date_created AS "+-30days",
a.location AS other_location
FROM
a AS x
JOIN a
USING (unique_person_id, type)
WHERE
x.location = 'Hospital1' AND
date_trunc('month', x.date_created) = date_trunc('month', '2017-03-01'::date) AND
x.id != a.id AND
abs(x.date_created - a.date_created) <= 30;
This version is shorter than the first one but, in my opinion, the first is easier to understand. I don't have big enough set of data to test and I wonder which one runs faster (the query planner shows similar values for both).

Having(count) in a Cakephp 3 query fails on PostgreSQL

I have the following table structure with matching relations:
,---------. ,--------------. ,---------.
| Threads | | ThreadsUsers | | Users |
|---------| |--------------| |---------|
| id | | id | | id |
'---------' | thread_id | '---------'
| user_id |
'--------------'
This custom query in ThreadsTable is meant to find threads with a given number of participants. It works fine on mysql
public function findWithUserCount(Query $query, array $options)
{
return $query
->matching('Users')
->select([
'Threads.id',
'count' => 'COUNT(Users.id)'
])
->group('Threads.id HAVING count = ' . $options['count']);
}
However it fails on postgresql with the following error
PDOException: SQLSTATE[42703]: Undefined column: 7
ERROR: column "count" does not exist
LINE 1: ...ThreadsUsers.user_id)) GROUP BY Threads.id HAVING count = 2
The HAVING clause cannot reference column aliases defined in the SELECT clause. The documentation says:
Each column referenced in condition must unambiguously reference a grouping column, unless the reference appears within an aggregate function or the ungrouped column is functionally dependent on the grouping columns.
Since count is neither a "grouping column" (i.e. the subject of the GROUP BY clause) nor an aggregate function, it can't be used there.
So the correct form would presumably be (I don't know CakePHP, and the fact that you can inject SQL into the group call at all seems like a massively broken design for a query builder):
->group('Threads.id HAVING COUNT(Users.id) = ' . $options['count']);

zend search lucene

I have a database that I would like to leverage with Zend_Search_Lucene. However, I am having difficulty creating a "fully searchable" document for Lucene.
Each Zend_Search_Lucene document pulls information from two relational database tables (Table_One and Table_Two). Table_One has basic information (id, owner_id, title, description, location, etc.), Table_Two has a 1:N relationship to Table_One (meaning, for each entry in Table_One, there could be one or more entries in Table_Two). Table_Two contains: id, listing_id, bedrooms, bathrooms, price_min, price_max, date_available. See Figure 1.
Figure 1
Table_One
id (Primary Key)
owner_id
title
description
location
etc...
Table_Two
id (Primary Key)
listing_id (Foreign Key to Table_One)
bedrooms (int)
bathrooms (int)
price_min (int)
price_max (int)
date_available (datetime)
The problem is, there are multiple Table_Two entries for each Table_One entry. [Question 1] How to create a Zend_Search_Lucene document where each field is unique? (See Figure 2)
Figure 2
Lucene Document
id:Keyword
owner_id:Keyword
title:UnStored
description:UnStored
location: UnStored
date_registered:Keyword
... (other Table_One information)
bedrooms: UnStored
bathrooms: UnStored
price_min: UnStored
price_max: UnStored
date_available: Keyword
bedrooms_1: <- Would prefer not to have do this as this makes the bedrooms harder to search.
Next, I need to be able to do a Range Query on the bedrooms, bathrooms, price_min and price_max fields. (Example: finding documents that have between 1 and 3 bedrooms) Zend_Search_Lucene will only allow ranged searches on the same field. From my understanding, this means each field I want to do a ranged query on can only contain one value (example: bedrooms:"1 bedroom");
What I have now, within the Lucene Document is the bedrooms, bathrooms, price_min, price_max, date_available fields being space delimited.
Example:
Sample Table_One Entry:
| 5 | 2 | "Sample Title" | "Sample Description" | "Sample Location" | 2008-01-12
Sample Table_Two Entries:
| 10 | 5 | 3 | 1 | 900 | 1000 | 2009-10-01
| 11 | 5 | 2 | 1 | 800 | 850 | 2009-08-11
| 12 | 5 | 1 | 1 | 650 | 650 | 2009-09-15
Sample Lucene Document
id:5
owner_id:2
title: "Sample Title"
description: "Sample Description"
location: "Sample Location"
date_registered: [datetime stamp YYYY-MM-DD]
bedrooms: "3 bedroom 2 bedroom 1 bedroom"
bathrooms: "1 bathroom 1 bathroom 1 bathroom"
price_min: "900 800 650"
price_max: "1000 850 650"
date_available: "2009-10-01 2009-08-11 2009-09-15"
[Question 2] Can you do a Range Query search on the bedroom, bathroom, price_min, price_max, date_available fields as they are shown above or does each range query field have to contain only one value (e.g. "1 bedroom")? I have not been able to get the Range Query to work in its current form. I am at a lose here.
Thanks in advance.
I suggest you create a separate Lucene document for each entry in Table_Two. This will cause some duplication of the Table_One information common to these entries, but this is not a high price to pay for much easier index structure in Lucene.
Use a boolean query to combine several range queries. The number-valued fields should be something like this:
bedrooms: 3
price_min: 900
and a sample query in Lucene syntax will be:
date_available:[20100101 TO 20100301] AND price_min:[600 TO 1000]