From f721619bb1658018fafca3c9b6e68de40da1abf4 Mon Sep 17 00:00:00 2001 From: Gabriel Felipe Soares Date: Thu, 19 Oct 2023 13:41:29 +0200 Subject: [PATCH 1/7] feat: create new methods on resource to make ACL faster --- core/kernel/classes/class.Resource.php | 52 +++++++ .../persistence/smoothsql/class.Resource.php | 130 ++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/core/kernel/classes/class.Resource.php b/core/kernel/classes/class.Resource.php index 148e24b25..ad4a3835d 100644 --- a/core/kernel/classes/class.Resource.php +++ b/core/kernel/classes/class.Resource.php @@ -778,6 +778,58 @@ public function isInstanceOf(core_kernel_classes_Class $class): bool return (bool) $returnValue; } + public function getParentClassesIds(): array + { + $implementation = $this->getImplementation(); + + if ($implementation instanceof core_kernel_persistence_smoothsql_Resource) { + return $implementation->getParentClassesIds($this->getUri()); + } + + return []; + } + + /** + * @return array [ + * '{classUri}' => [ + * '{resourceUri_1}', + * '{resourceUri_N...}', + * ] + * ] + */ + public function getParentClassesResourceIds(array $classIds = null): array + { + $implementation = $this->getImplementation(); + + if ($implementation instanceof core_kernel_persistence_smoothsql_Resource) { + return $implementation->getClassesResourceIds($classIds ?? $this->getParentClassesIds()); + } + + return []; + } + + /** + * Returns a list of nested resources/classes under a resource + * + * @return array [ + * [ + * 'id' => '{resourceId}', + * 'isClass' => true|false, + * 'level' => 1..N, + * ] + * ] + */ + public function getNestedResources(): array + { + $implementation = $this->getImplementation(); + + if ($implementation instanceof core_kernel_persistence_smoothsql_Resource) { + return $implementation->getNestedResources($this->getUri()); + } + + return []; + } + public function getServiceManager(): ServiceLocatorInterface { return ($this->getModel() instanceof ServiceLocatorAwareInterface) diff --git a/core/kernel/persistence/smoothsql/class.Resource.php b/core/kernel/persistence/smoothsql/class.Resource.php index 533167d16..84e0a9b8d 100644 --- a/core/kernel/persistence/smoothsql/class.Resource.php +++ b/core/kernel/persistence/smoothsql/class.Resource.php @@ -25,6 +25,7 @@ */ use oat\generis\model\OntologyRdf; +use oat\generis\model\OntologyRdfs; use oat\oatbox\session\SessionService; use oat\oatbox\user\UserLanguageServiceInterface; use oat\generis\model\kernel\uri\UriProvider; @@ -102,6 +103,135 @@ public function getTypes(core_kernel_classes_Resource $resource) return (array) $returnValue; } + public function getParentClassesIds(string $resourceUri): array + { + $query = <<<'SQL' +WITH RECURSIVE statements_tree AS ( + SELECT + r.object + FROM statements r + WHERE r.subject = ? + AND r.predicate IN (?, ?) + AND r.object != ? + UNION ALL + SELECT + s.object + FROM statements s + JOIN statements_tree st + ON s.subject = st.object + AND s.predicate IN (?, ?) + AND s.object NOT IN (?, ?, ?, ?) +) +SELECT object FROM statements_tree; +SQL; + + $statement = $this->getPersistence()->query( + $query, + [ + $resourceUri, + OntologyRdfs::RDFS_SUBCLASSOF, + OntologyRdf::RDF_TYPE, + 'http://www.tao.lu/Ontologies/TAO.rdf#AssessmentContentObject', + OntologyRdfs::RDFS_SUBCLASSOF, + OntologyRdf::RDF_TYPE, + 'http://www.tao.lu/Ontologies/TAO.rdf#AssessmentContentObject', + 'http://www.tao.lu/Ontologies/TAO.rdf#TAOObject', + 'http://www.tao.lu/Ontologies/generis.rdf#generis_Ressource', + 'http://www.w3.org/2000/01/rdf-schema#Resource', + ] + ); + + return array_column($statement->fetchAll(), 'object'); + } + + /** + * @param array $classIds + * @return array [ + * '{classUri}' => [ + * '{resourceUri_1}', + * '{resourceUri_N...}', + * ] + * ] + */ + public function getClassesResourceIds(array $classIds): array + { + $inQuery = implode(',', array_fill(0, count($classIds), '?')); + $query = "SELECT subject, object FROM statements WHERE predicate IN (?, ?) AND object IN ($inQuery)"; + + $statement = $this->getPersistence()->query( + $query, + [ + OntologyRdfs::RDFS_SUBCLASSOF, + OntologyRdf::RDF_TYPE, + ...$classIds, + ] + ); + + $results = $statement->fetchAll(); + $resourceIds = []; + + foreach ($classIds as $classId) { + /** + * @TODO FIXME Better filter it instead of go through all results every time + */ + $resources = array_filter($results, static fn (array $result): bool => $result['object'] === $classId); + $resourceIds[$classId] = array_column($resources, 'subject'); + } + + return $resourceIds; + } + + /** + * Returns a list of nested resources/classes under a resource + * + * @param string $classId + * @return array [ + * [ + * 'id' => '{resourceId}', + * 'isClass' => true|false, + * 'level' => 1..N, + * ] + * ] + */ + public function getNestedResources(string $classId): array + { + $query = <<<'SQL' +WITH RECURSIVE statements_tree AS ( + SELECT + r.subject, + r.predicate, + 1 as level + FROM statements r + WHERE r.subject = ? + AND r.predicate IN (?, ?) + UNION ALL + SELECT + s.subject, + s.predicate, + level + 1 + FROM statements s + JOIN statements_tree st + ON s.object = st.subject + WHERE s.predicate IN (?, ?) +) +SELECT subject as id, IF(predicate = ?, 1, 0) as isClass, level FROM statements_tree; +SQL; + + $statement = $this->getPersistence()->query( + $query, + [ + $classId, + OntologyRdfs::RDFS_SUBCLASSOF, + OntologyRdf::RDF_TYPE, + OntologyRdfs::RDFS_SUBCLASSOF, + OntologyRdf::RDF_TYPE, + OntologyRdfs::RDFS_SUBCLASSOF, + ] + ); + + return $statement->fetchAll(); + } + /** * Short description of method getPropertyValues * From e077b8da13201c8ce52d22fff56f35420a09da95 Mon Sep 17 00:00:00 2001 From: Gabriel Felipe Soares Date: Thu, 19 Oct 2023 13:56:09 +0200 Subject: [PATCH 2/7] fix: validate parameter size to avoid errors --- core/kernel/persistence/smoothsql/class.Resource.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/kernel/persistence/smoothsql/class.Resource.php b/core/kernel/persistence/smoothsql/class.Resource.php index 84e0a9b8d..17959e992 100644 --- a/core/kernel/persistence/smoothsql/class.Resource.php +++ b/core/kernel/persistence/smoothsql/class.Resource.php @@ -155,6 +155,10 @@ public function getParentClassesIds(string $resourceUri): array */ public function getClassesResourceIds(array $classIds): array { + if (empty($classIds)) { + return []; + } + $inQuery = implode(',', array_fill(0, count($classIds), '?')); $query = "SELECT subject, object FROM statements WHERE predicate IN (?, ?) AND object IN ($inQuery)"; From b76f052022a2eccb859333d8236b6d0e5b17b934 Mon Sep 17 00:00:00 2001 From: Gabriel Felipe Soares Date: Thu, 19 Oct 2023 14:29:01 +0200 Subject: [PATCH 3/7] chore: optimize PHP code to be faster --- .../persistence/smoothsql/class.Resource.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/kernel/persistence/smoothsql/class.Resource.php b/core/kernel/persistence/smoothsql/class.Resource.php index 17959e992..e1c3c633a 100644 --- a/core/kernel/persistence/smoothsql/class.Resource.php +++ b/core/kernel/persistence/smoothsql/class.Resource.php @@ -159,8 +159,10 @@ public function getClassesResourceIds(array $classIds): array return []; } - $inQuery = implode(',', array_fill(0, count($classIds), '?')); - $query = "SELECT subject, object FROM statements WHERE predicate IN (?, ?) AND object IN ($inQuery)"; + $query = sprintf( + 'SELECT subject, object FROM statements WHERE predicate IN (?, ?) AND object IN (%s)', + implode(',', array_fill(0, count($classIds), '?')) + ); $statement = $this->getPersistence()->query( $query, @@ -174,12 +176,11 @@ public function getClassesResourceIds(array $classIds): array $results = $statement->fetchAll(); $resourceIds = []; - foreach ($classIds as $classId) { - /** - * @TODO FIXME Better filter it instead of go through all results every time - */ - $resources = array_filter($results, static fn (array $result): bool => $result['object'] === $classId); - $resourceIds[$classId] = array_column($resources, 'subject'); + foreach ($results as $result) { + $resourceId = $result['subject']; + $classId = $result['object']; + $resourceIds[$classId] = $resourceIds[$classId] ?? []; + $resourceIds[$classId][] = $resourceId; } return $resourceIds; From 4d95e7645b10c2a36bc74f25c7a8b16403630ff4 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Fri, 20 Oct 2023 15:30:04 +0300 Subject: [PATCH 4/7] feat: add new bulk event --- common/oatbox/event/BulkEvent.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 common/oatbox/event/BulkEvent.php diff --git a/common/oatbox/event/BulkEvent.php b/common/oatbox/event/BulkEvent.php new file mode 100644 index 000000000..60d039e4d --- /dev/null +++ b/common/oatbox/event/BulkEvent.php @@ -0,0 +1,28 @@ + Date: Tue, 24 Oct 2023 01:40:37 +0300 Subject: [PATCH 5/7] chore: use CASE instead of IF --- core/kernel/persistence/smoothsql/class.Resource.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/kernel/persistence/smoothsql/class.Resource.php b/core/kernel/persistence/smoothsql/class.Resource.php index 35c44cc99..750b4717a 100644 --- a/core/kernel/persistence/smoothsql/class.Resource.php +++ b/core/kernel/persistence/smoothsql/class.Resource.php @@ -219,7 +219,11 @@ public function getNestedResources(string $classId): array ON s.object = st.subject WHERE s.predicate IN (?, ?) ) -SELECT subject as id, IF(predicate = ?, 1, 0) as isClass, level FROM statements_tree; +SELECT + subject as id, + CASE WHEN predicate = ? THEN 1 ELSE 0 END as isClass, + level +FROM statements_tree; SQL; $statement = $this->getPersistence()->query( From 3a83478fbbada796aa80dd6a3560f785c0d2fdb1 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Thu, 26 Oct 2023 13:40:51 +0300 Subject: [PATCH 6/7] chore: keep original order --- core/kernel/persistence/smoothsql/class.Resource.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/kernel/persistence/smoothsql/class.Resource.php b/core/kernel/persistence/smoothsql/class.Resource.php index 750b4717a..1f0d48209 100644 --- a/core/kernel/persistence/smoothsql/class.Resource.php +++ b/core/kernel/persistence/smoothsql/class.Resource.php @@ -176,11 +176,10 @@ public function getClassesResourceIds(array $classIds): array $results = $statement->fetchAll(); $resourceIds = []; - foreach ($results as $result) { - $resourceId = $result['subject']; - $classId = $result['object']; - $resourceIds[$classId] = $resourceIds[$classId] ?? []; - $resourceIds[$classId][] = $resourceId; + // Iterate over the provided class IDs to keep the same order + foreach ($classIds as $classId) { + $resources = array_filter($results, static fn (array $result): bool => $result['object'] === $classId); + $resourceIds[$classId] = array_column($resources, 'subject'); } return $resourceIds; From e8881392852ea352aa88c5cc2ac1c0c605690db4 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Thu, 2 Nov 2023 14:46:56 +0300 Subject: [PATCH 7/7] chore: fix CS --- common/oatbox/event/BulkEvent.php | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/common/oatbox/event/BulkEvent.php b/common/oatbox/event/BulkEvent.php index 60d039e4d..48c5c9817 100644 --- a/common/oatbox/event/BulkEvent.php +++ b/common/oatbox/event/BulkEvent.php @@ -1,28 +1,28 @@ -