[WIP] Begin implementing support for nested/sequential queries

This commit is contained in:
Jeremy Dormitzer 2018-11-25 11:01:35 -05:00
parent 5d643fd3ec
commit 4398f543dc
2 changed files with 93 additions and 19 deletions

View File

@ -31,6 +31,7 @@ class ObjectsService
// TODO validate object fields?
// TODO don't create the object if it already exists
// i.e. (an 'id' field exists with the same value as this one)
// TODO attempt to fetch and create any values that are URLs
$object = new ActivityPubObject();
$this->entityManager->persist( $object );
foreach ( $fields as $name => $value ) {
@ -86,28 +87,66 @@ class ObjectsService
*/
public function query( $queryTerms )
{
// TODO make it search for nested objects like the comment says
$qb = $this->entityManager->createQueryBuilder();
$qb->select( 'object' )
->from( '\ActivityPub\Entities\ActivityPubObject', 'object' )
->join( 'object.fields', 'field' );
foreach ( $queryTerms as $fieldName => $fieldValue ) {
if ( is_array( $fieldValue ) ) {
// The following two branches will need to be recursive
if ( Util::isAssoc( $fieldValue ) ) {
// TODO support querying for associative arrays (nested objects)
} else {
// TODO support querying for sequential arrays
}
} else {
$qb->where( $qb->expr()->andX(
$qb->expr()->like( 'field.name', $qb->expr()->literal( $fieldName ) ),
$qb->expr()->like( 'field.value', $qb->expr()->literal( $fieldValue ) )
) );
}
}
$depth = 0;
$qb->select( "object$depth" )
->from( '\ActivityPub\Entities\ActivityPubObject', "object$depth" )
->join( "object$depth.fields", "field$depth" )
->where( $this->getWhereExpr( $qb, $queryTerms, $depth ) );
$query = $qb->getQuery();
return $query->getResult();
}
/**
* Generates the expression that gets passed into the query WHERE clause
*
* This function is recursive; it traverses the query tree to build up the
* final expression
*
* @param QueryBuilder $qb The query builder that the WHERE clause will be attached to
* @param array $queryTerms The query terms from which to generate the expressions
* @param int $depth The recursion depth
* @return Expr The expression
*/
protected function getWhereExpr( &$qb, $queryTerms, $depth = 0 )
{
$nextDepth = $depth + 1;
$exprs = array();
foreach( $queryTerms as $fieldName => $fieldValue ) {
if ( is_array( $fieldValue ) ) {
if ( Util::isAssoc( $fieldValue ) ) {
$subQuery = $this->entityManager->createQueryBuilder();
$subQuery->select( "object$nextDepth" )
->from( '\ActivityPub\Entities\ActivityPubObject', "object$nextDepth" )
->join( "object$nextDepth.fields", "field$nextDepth" )
->where( $this->getWhereExpr( $subQuery, $fieldValue, $nextDepth ) );
$exprs[] = $qb->expr()->in(
"field$depth.targetObject",
$subQuery->getDql()
);
} else {
$subExprs = array();
foreach ( $fieldValue as $subFieldName => $subFieldValue ) {
$subExprs[] = $this->getWhereExpr(
$qb, array( $subFieldName => $subFieldValue ), $nextDepth
);
}
$exprs[] = call_user_func_array(
array( $qb->expr(), 'orX' ),
$subExprs
);
}
} else {
$exprs[] = $qb->expr()->andX(
$qb->expr()->like( "field$depth.name", $qb->expr()->literal( $fieldName ) ),
$qb->expr()->like( "field$depth.value", $qb->expr()->literal( $fieldValue ) )
);
}
}
return call_user_func_array(
array( $qb->expr(), 'andX' ),
$exprs
);
}
}
?>

View File

@ -527,5 +527,40 @@ class ObjectsServiceTest extends SQLiteTestCase
$this->assertContainsOnlyInstancesOf( ActivityPubObject::class, $results );
$this->assertEquals( $object, $results[0] );
}
public function testMultiNestedSequentialObjectQueryResults()
{
$fields = array(
'id' => 'https://example.com/notes/1',
'type' => 'Note',
'content' => 'This is a note',
'attributedTo' => array(
'id' => 'https://example.com/actors/2',
'type' => 'Person',
'following' => array(
'id' => 'https://example.com/collections/1',
'type' => 'Collection',
'items' => array(
array( 'id' => 'https://example.com/actors/1' ),
),
),
),
);
$object = $this->objectsService->createObject( $fields );
$query = array(
'attributedTo' => array(
'following' => array(
'items' => array(
array( 'id' => 'https://example.com/actors/1' )
),
),
),
);
$results = $this->objectsService->query( $query );
$this->assertCount( 1, $results );
$this->assertContainsOnlyInstancesOf( ActivityPubObject::class, $results );
$this->assertEquals( $object, $results[0] );
$this->assertEquals( $fields, $results[0]->asArray() );
}
}
?>