mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
optimize query for current address + documentation
This commit is contained in:
parent
f63d4fcfba
commit
50b7554aea
@ -8,6 +8,29 @@ use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* This class links a person to the history of his addresses, through
|
||||
* household membership.
|
||||
*
|
||||
* It is optimized on DB side, and compute the start date and end date
|
||||
* of each address by the belonging of household.
|
||||
*
|
||||
* **note**: the start date and end date are the date of belonging to the address,
|
||||
* not the belonging of the household.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* * person A is member of household W from 2021-01-01 to 2021-12-01
|
||||
* * person A is member of household V from 2021-12-01, still present after
|
||||
* * household W lives in address Q from 2020-06-01 to 2021-06-01
|
||||
* * household W lives in address R from 2021-06-01 to 2022-06-01
|
||||
* * household V lives in address T from 2021-12-01 to still living there after
|
||||
*
|
||||
* The person A will have those 3 entities:
|
||||
*
|
||||
* 1. 1st entity: from 2021-01-01 to 2021-06-01, household W, address Q;
|
||||
* 2. 2st entity: from 2021-06-01 to 2021-12-01, household W, address R;
|
||||
* 3. 3st entity: from 2021-12-01 to NULL, household V, address T;
|
||||
*
|
||||
* @ORM\Entity(readOnly=true)
|
||||
* @ORM\Table(name="view_chill_person_household_address")
|
||||
*/
|
||||
@ -45,11 +68,23 @@ class PersonHouseholdAddress
|
||||
*/
|
||||
private $address;
|
||||
|
||||
/**
|
||||
* The start date of the intersection address/household
|
||||
*
|
||||
* (this is not the startdate of the household, not
|
||||
* the startdate of the address)
|
||||
*/
|
||||
public function getValidFrom(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->validFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* The end date of the intersection address/household
|
||||
*
|
||||
* (this is not the enddate of the household, not
|
||||
* the enddate of the address)
|
||||
*/
|
||||
public function getValidTo(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->validTo;
|
||||
|
@ -35,6 +35,7 @@ use Chill\PersonBundle\Entity\Household\HouseholdMember;
|
||||
use Chill\MainBundle\Entity\HasCenterInterface;
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
|
||||
use Chill\PersonBundle\Entity\Person\PersonCurrentAddress;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
@ -412,6 +413,15 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
*/
|
||||
private $addresses;
|
||||
|
||||
/**
|
||||
* The current person address.
|
||||
*
|
||||
* This is computed through database and is optimized on database side.
|
||||
*
|
||||
* @var PersonCurrentAddress|null
|
||||
*/
|
||||
private ?PersonCurrentAddress $currentPersonAddress = null;
|
||||
|
||||
/**
|
||||
* fullname canonical. Read-only field, which is calculated by
|
||||
* the database.
|
||||
@ -564,6 +574,8 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $participation->getAccompanyingPeriod();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1242,13 +1254,31 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
return $this->addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `getCurrentPersonAddress` instead
|
||||
* @param DateTime|null $from
|
||||
* @return false|mixed|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getLastAddress(DateTime $from = null)
|
||||
{
|
||||
$from ??= new DateTime('now');
|
||||
return $this->getCurrentPersonAddress($from);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the address associated with the person at the given date
|
||||
*
|
||||
* @param DateTime|null $at
|
||||
* @return Address|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCurrentPersonAddress(?\DateTime $at = null): ?Address
|
||||
{
|
||||
$at ??= new DateTime('now');
|
||||
|
||||
/** @var ArrayIterator $addressesIterator */
|
||||
$addressesIterator = $this->getAddresses()
|
||||
->filter(static fn (Address $address): bool => $address->getValidFrom() <= $from)
|
||||
->filter(static fn (Address $address): bool => $address->getValidFrom() <= $at)
|
||||
->getIterator();
|
||||
|
||||
$addressesIterator->uasort(
|
||||
@ -1496,7 +1526,16 @@ class Person implements HasCenterInterface, TrackCreationInterface, TrackUpdateI
|
||||
|
||||
public function getCurrentHouseholdAddress(?\DateTimeImmutable $at = null): ?Address
|
||||
{
|
||||
$at = $at === null ? new \DateTimeImmutable('today') : $at;
|
||||
if (
|
||||
NULL === $at
|
||||
||
|
||||
$at->format('Ymd') === (new \DateTime('today'))->format('Ymd')
|
||||
) {
|
||||
return $this->currentPersonAddress instanceof PersonCurrentAddress
|
||||
? $this->currentPersonAddress->getAddress() : NULL;
|
||||
}
|
||||
|
||||
// if not now, compute the date from history
|
||||
$criteria = new Criteria();
|
||||
$expr = Criteria::expr();
|
||||
|
||||
|
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Chill\PersonBundle\Entity\Person;
|
||||
|
||||
use Chill\MainBundle\Entity\Address;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Entity which associate person with his current address, through
|
||||
* household participation.
|
||||
*
|
||||
* The computation is optimized on database side.
|
||||
*
|
||||
* The validFrom and validTo properties are the intersection of
|
||||
* household membership and address validity. See @link{PersonHouseholdAddress}
|
||||
*
|
||||
* @ORM\Entity(readOnly=true)
|
||||
* @ORM\Table("view_chill_person_current_address")
|
||||
*/
|
||||
class PersonCurrentAddress
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\OneToOne(targetEntity=Person::class)
|
||||
*/
|
||||
protected Person $person;
|
||||
|
||||
/**
|
||||
* @ORM\OneToOne(targetEntity=Address::class)
|
||||
*/
|
||||
protected Address $address;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="valid_from", type="date_immutable")
|
||||
*/
|
||||
protected \DateTimeImmutable $validFrom;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="valid_to", type="date_immutable")
|
||||
*/
|
||||
protected ?\DateTimeImmutable $validTo;
|
||||
|
||||
/**
|
||||
* @return Person
|
||||
*/
|
||||
public function getPerson(): Person
|
||||
{
|
||||
return $this->person;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Address
|
||||
*/
|
||||
public function getAddress(): Address
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
/**
|
||||
* This date is the intersection of household membership
|
||||
* and address validity
|
||||
*
|
||||
* @return \DateTimeImmutable
|
||||
*/
|
||||
public function getValidFrom(): \DateTimeImmutable
|
||||
{
|
||||
return $this->validFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* This date is the intersection of household membership
|
||||
* and address validity
|
||||
*
|
||||
* @return \DateTimeImmutable|null
|
||||
*/
|
||||
public function getValidTo(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->validTo;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\Migrations\Person;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Create view for PersonCurrentAddress and related indexes
|
||||
*/
|
||||
final class Version20210915093624 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create view for PersonCurrentAddress and related indexes';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql("CREATE VIEW view_chill_person_current_address AS
|
||||
SELECT
|
||||
cphm.person_id AS person_id,
|
||||
cma.id AS address_id,
|
||||
CASE WHEN cphm.startdate > COALESCE(cma.validfrom, '-infinity'::date) THEN cphm.startdate ELSE cma.validfrom END AS valid_from,
|
||||
CASE WHEN COALESCE(cphm.enddate, 'infinity'::date) < COALESCE(cma.validto, 'infinity'::date) THEN cphm.enddate ELSE cma.validto END AS valid_to
|
||||
FROM chill_person_household_members AS cphm
|
||||
LEFT JOIN chill_person_household_to_addresses AS cphta ON cphta.household_id = cphm.household_id
|
||||
LEFT JOIN chill_main_address AS cma ON cphta.address_id = cma.id
|
||||
WHERE
|
||||
cphm.sharedhousehold IS TRUE
|
||||
AND
|
||||
current_date between cphm.startdate AND coalesce(enddate, 'infinity'::date)
|
||||
AND
|
||||
current_date between cma.validfrom AND coalesce(validto, 'infinity'::date)
|
||||
");
|
||||
|
||||
$this->addSql("CREATE INDEX chill_custom_main_address_filtering_idx ON chill_main_address USING btree (id, validfrom ASC, validto DESC)");
|
||||
$this->addSql("CREATE INDEX chill_custom_person_household_members_sharing_idx ON chill_person_household_members USING btree (person_id, startdate ASC, enddate DESC, household_id) WHERE sharedhousehold IS TRUE");
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql("DROP VIEW view_chill_person_current_address");
|
||||
$this->addSql("DROP INDEX chill_custom_main_address_filtering_idx");
|
||||
$this->addSql("DROP INDEX chill_custom_person_household_members_sharing_idx");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user