I have the following tables where 1 page has many containables, and each containable has either lines or questions:
// Page model
class Page extends Model
{
protected $fillable = ['title'];
public function containables() {
return $this->morphMany(Containable::class, 'containable');
}
}
Example pages table data:
| id | title |
|----|--------|
| 1 | Page 1 |
// Containable model
class Containable extends Model
{
protected $table = 'containables';
protected $fillable = ['containable_id', 'containable_type'];
public function containable() {
return $this->morphTo('containable');
}
public function sequences() {
return $this->hasMany(ContainableSequence::class, 'containable_id', 'id');
}
Example containables table data:
| id | containable_id | containable_type |
|----|----------------|------------------|
| 2 | 1 | App\Page |
| 3 | 1 | App\Page |
// ContainableSequence model
class ContainableSequence extends Model
{
protected $table = 'containable_sequence';
protected $fillable = ['containable_id', 'sequencable_id',
'sequencable_type', 'order'];
public function sequencable() {
return $this->morphTo('sequencable');
}
}
Example containable_sequence table data:
| id | containable_id | sequencable_type | sequencable_id | order |
|----|----------------|------------------|----------------|-------|
| 4 | 2 | App\Line | 6 | 1 |
| 5 | 2 | App\Question | 8 | 2 |
| 6 | 3 | App\Question | 9 | 1 |
| 7 | 3 | App\Line | 7 | 2 |
// Line model
class Line extends Model
{
protected $table = 'lines';
protected $fillable = ['name'];
}
Example Lines table data:
| id | name |
|----|--------|
| 6 | line 1 |
| 7 | line 2 |
// Question model
class Question extends Model
{
protected $table = 'questions';
protected $fillable = ['name', 'marks'];
}
Example Questions table data:
| id | name | marks |
|----|------------|-------|
| 8 | Question 1 | 10 |
| 9 | Question 2 | 15 |
I am trying to perform a join the tables with polymorphic associations to get the count of line(s) and question(s) linked to a page.
Starting from a page, how can I get the counts of related lines and questions using eloquent? Thanks
Related
I need to allocate a same unique id (batchid) for each row inserted in a BD during a batch execution as illustrated below.
| id | batchid |
| -- | ------- |
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
| 6 | 3 |
Was wondering if there is an automated way to do it with jpa annotation, like with a sequence ?
Did it for now this way:
#Repository
public interface SeqRepository extends JpaRepository<CsvEntity, Long> {
#Query(value = "SELECT nextval('batch_id_seq')", nativeQuery = true)
Integer getNextBatchId();
}
schema.sql
CREATE SEQUENCE IF NOT EXISTS batch_id_seq
INCREMENT 1
START 1;
The example above can be done on a SQL Server. It is a function that performs the calculation on another table while getting the current table field Id to list data from other table, return a single value.
Question: how to do the exact thing with PostgreSQL
SELECT TOP(5) * FROM Artists;
+------------+------------------+--------------+-------------+
| ArtistId | ArtistName | ActiveFrom | CountryId |
|------------+------------------+--------------+-------------|
| 1 | Iron Maiden | 1975-12-25 | 3 |
| 2 | AC/DC | 1973-01-11 | 2 |
| 3 | Allan Holdsworth | 1969-01-01 | 3 |
| 4 | Buddy Rich | 1919-01-01 | 6 |
| 5 | Devin Townsend | 1993-01-01 | 8 |
+------------+------------------+--------------+-------------+
SELECT TOP(5) * FROM Albums;
+-----------+------------------------+---------------+------------+-----------+
| AlbumId | AlbumName | ReleaseDate | ArtistId | GenreId |
|-----------+------------------------+---------------+------------+-----------|
| 1 | Powerslave | 1984-09-03 | 1 | 1 |
| 2 | Powerage | 1978-05-05 | 2 | 1 |
| 3 | Singing Down the Lane | 1956-01-01 | 6 | 3 |
| 4 | Ziltoid the Omniscient | 2007-05-21 | 5 | 1 |
| 5 | Casualties of Cool | 2014-05-14 | 5 | 1 |
+-----------+------------------------+---------------+------------+-----------+
The function
CREATE FUNCTION [dbo].[ufn_AlbumCount] (#ArtistId int)
RETURNS smallint
AS
BEGIN
DECLARE #AlbumCount int;
SELECT #AlbumCount = COUNT(AlbumId)
FROM Albums
WHERE ArtistId = #ArtistId;
RETURN #AlbumCount;
END;
GO
Now, (at SQL Server), after update the first table fields with ALTER TABLE Artists ADD AlbumCount AS dbo.ufn_AlbumCount(ArtistId); whe can list and get the following result.
+------------+------------------+--------------+-------------+--------------+
| ArtistId | ArtistName | ActiveFrom | CountryId | AlbumCount |
|------------+------------------+--------------+-------------+--------------|
| 1 | Iron Maiden | 1975-12-25 | 3 | 5 |
| 2 | AC/DC | 1973-01-11 | 2 | 3 |
| 3 | Allan Holdsworth | 1969-01-01 | 3 | 2 |
| 4 | Buddy Rich | 1919-01-01 | 6 | 1 |
| 5 | Devin Townsend | 1993-01-01 | 8 | 3 |
| 6 | Jim Reeves | 1948-01-01 | 6 | 1 |
| 7 | Tom Jones | 1963-01-01 | 4 | 3 |
| 8 | Maroon 5 | 1994-01-01 | 6 | 0 |
| 9 | The Script | 2001-01-01 | 5 | 1 |
| 10 | Lit | 1988-06-26 | 6 | 0 |
+------------+------------------+--------------+-------------+--------------+
but how to achieve this on postgresql?
Postgres doesn't support "virtual" computed column (i.e. computed columns that are generated at runtime), so there is no exact equivalent. The most efficient solution is a view that counts this:
create view artists_with_counts
as
select a.*,
coalesce(t.album_count, 0) as album_count
from artists a
left join (
select artist_id, count(*) as album_count
from albums
group by artist_id
) t on a.artist_id = t.artist_id;
Another option is to create a function that can be used as a "virtual column" in a select - but as this is done row-by-row, this will be substantially slower than the view.
create function album_count(p_artist artists)
returns bigint
as
$$
select count(*)
from albums a
where a.artist_id = p_artist.artist_id;
$$
language sql
stable;
Then you can include this as a column:
select a.*, a.album_count
from artists a;
Using the function like that, requires to prefix the function reference with the table alias (alternatively, you can use album_count(a))
Online example
I currently have a simple stream of data, for example:
|-----|--------|-------|
| Key | TS(ms) | Value |
|-----|--------|-------|
| A | 1000 | 0 |
| A | 1000 | 0 |
| A | 61000 | 0 |
| A | 61000 | 0 |
| A | 121000 | 0 |
| A | 121000 | 0 |
| A | 181000 | 10 |
| A | 181000 | 10 |
| A | 241000 | 10 |
| A | 241000 | 10 |
| B | 1000 | 0 |
| B | 1000 | 0 |
| B | 61000 | 0 |
| B | 61000 | 0 |
| B | 121000 | 0 |
| B | 121000 | 0 |
| B | 181000 | 10 |
| B | 181000 | 10 |
| B | 1000 | 10 |
| B | 241000 | 10 |
| B | 241000 | 10 |
|-----|--------|-------|
this is also the order I publish the data in a topic, the value isn't really an integer but an avro value but the key is a string.
My code is this:
KStream<Windowed<String>, Long> aggregatedStream = inputStream
.groupByKey()
.windowedBy(TimeWindows.of(Duration.ofMinutes(1)).grace(Duration.ZERO))
.count()
.toStream();
aggregatedStream.print(Printed.toSysOut());
The output of the print is:
[KTABLE-TOSTREAM-0000000003]: [A#0/60000], 1
[KTABLE-TOSTREAM-0000000003]: [A#0/60000], 2
[KTABLE-TOSTREAM-0000000003]: [A#60000/120000], 1
[KTABLE-TOSTREAM-0000000003]: [A#60000/120000], 2
[KTABLE-TOSTREAM-0000000003]: [A#120000/180000], 1
[KTABLE-TOSTREAM-0000000003]: [A#120000/180000], 2
[KTABLE-TOSTREAM-0000000003]: [A#180000/240000], 1
[KTABLE-TOSTREAM-0000000003]: [A#180000/240000], 2
[KTABLE-TOSTREAM-0000000003]: [A#240000/300000], 1
[KTABLE-TOSTREAM-0000000003]: [A#240000/300000], 2
[KTABLE-TOSTREAM-0000000003]: [B#240000/300000], 1
[KTABLE-TOSTREAM-0000000003]: [B#240000/300000], 2
It seems that the grace period applies globally independently of the key of the stream, what I expect instead (if possible) is to receive all the 10 window counts of key A and the 10 window counts of key B.
In a way that the grace only closes windows based on the key of the stream.
Is that possible?
It seems that grace and suppress uses a global timestamp for each partition, so it's not possible to have a different one per each key.
What's possible instead is to disable the grace period and use a custom transformer instead of the regular suppress to do be able to suppress by key.
For example this is part of our code:
KStream<String, ...> aggregatedStream = pairsStream
.groupByKey()
.windowedBy(TimeWindows.of(Duration.ofMinutes(1)))
.aggregate(...your aggregation logic...)
.toStream()
.flatTransform(new TransformerSupplier<Windowed<String>, AggregateOutput, Iterable<KeyValue<String, SuppressedOutput>>>() {
#Override
public Transformer<Windowed<String>, AggregateOutput, Iterable<KeyValue<String, SuppressedOutput>>> get() {
return new Transformer<Windowed<String>, AggregateOutput, Iterable<KeyValue<String, SuppressedOutput>>>() {
KeyValueStore<String, SuppressedOutput> store;
#SuppressWarnings("unchecked")
#Override
public void init(ProcessorContext context) {
store = (KeyValueStore<String, SuppressedOutput>) context.getStateStore("suppress-store");
}
#Override
public Iterable<KeyValue<String, SuppressedOutput>> transform(Windowed<String> window, AggregateOutput sequenceList) {
String messageKey = window.key();
long windowEndTimestamp = window.window().endTime().toEpochMilli();
SuppressedOutput currentSuppressedOutput = new SuppressedOutput(windowEndTimestamp, sequenceList);
SuppressedOutput storeValue = store.get(messageKey);
if (storeValue == null) {
// First time we receive a window for that key
}
if (windowEndTimestamp > storeValue.getTimestamp()) {
// Received a new window
}
if (windowEndTimestamp < storeValue.getTimestamp()) {
// Window older than the last window we've received
}
store.put(messageKey, currentSuppressedOutput);
return new ArrayList<>();
}
#Override
public void close() {
}
};
}
}, "suppress-store")
I'm trying to get a list of records from a table, where the PK is not in a composite table. If you see consider below table, I'm trying to get all candidates that are not in a pool, so in this case I should get only Eric.
+-------------+ +-------------+
| candidates | | pools |
+-------------+ +-------------+
| id | name | | id | name |
+----+--------| +----+--------|
| 1 | John | | 1 | Pool A |
| 2 | Richard| | 2 | Pool B |
| 3 | Eric | | 3 | Pool C |
+----+--------+ +----+--------+
+--------------------------+
| candidate_pools |
+--------------------------+
| pool_id | candidate_id |
+---------+----------------|
| 1 | 1 |
| 1 | 2 |
| 3 | 2 |
| 2 | 1 |
+---------+----------------+
I've found this answer: https://laracasts.com/discuss/channels/eloquent/get-records-collection-if-not-exists-in-another-table
$crashedCarIds = CrashedCar::pluck('car_id')->all();
$cars = Car::whereNotIn('id', $crashedCarIds)->select(...)->get();
Which should work, however it seems extremely inefficient in our fairly big and rapidly growing database, on a page that is accessed often by a lot of people.
In normal MySQL you would do something like this:
WHERE candidates.id NOT IN
( SELECT candidate_id
FROM candidate_pools
LEFT JOIN pools
ON candidate_pools.pool_id = pool.id
)
Note: The tables are just a simple illustration, but the actual database set-up and queries are a lot bigger.
I managed to do it using scope, like this:
public function scopeNotInPool($query) {
$query->whereNotIn('candidate_id', function ($query) {
$query->select('candidate_id')
->from('candidate_pool')
->join('pool', function ($join) {
$join->on('pool.id', '=', 'candidate_pool.pool_id');
}
)
;
});
return $query;
}
Then call it like:
$candidates = Models\Candidate::notInPool()->get();
I have the following categories table:
ID | Category Name | Date Added | Parent
______________________________________________________________________
1 | 'Countries' | '2013-10-09 04:18:09' | 0
2 | 'Spain' | '2013-10-10 03:09:23' | 1
3 | 'Animals' | '2013-10-17 06:39:21' | 0
4 | 'Dogs' | '2013-10-16 07:31:52' | 3
5 | 'Saint-Bernard' | '2013-10-15 05:33:33' | 4
6 | 'Cats' | '2013-10-15 16:19:24' | 3
7 | 'Pitbull' | '2013-10-01 08:12:13 | 4
On each page in my list view (index.phtml) i want to list 20 main categories (parent 0) and all of their children in the custom hierarchy. So the result should be something like this:
Countries
-> Spain
Animals
-> Dogs
-> Saint-Bernard
-> Pitbull
-> Cats
In my CategoriesController on the indexAction i have build the following listing:
$categories = new admin_Model_DbTable_Categories();
$page = $this->_request->getParam('page');
if(empty($page)) { $page = 1; }
$query = $categories
->select()
->where("parent = 0");
$this->view->categories = $categories->paginate(
$query,
20,
$page
);
My problem is that i don't know where exactly and how to build a function that cycles through each of the parents categories and gets their children also in a hierarchy.