Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EZP-31107: Built-in query types for site building #2938

Merged
merged 6 commits into from
Mar 16, 2020
Merged

Conversation

adamwojs
Copy link
Member

@adamwojs adamwojs commented Feb 3, 2020

Question Answer
JIRA issue EZP-31107
Bug/Improvement no
New feature yes
Target version master
BC breaks no
Tests pass yes
Doc needed yes

Implemented Query Types

Ancestors/Children/Siblings

Ancestors/Children/Siblings of content or location.

Name Required Description
location N -
content N -
query:
    query_type: 'Ancesors'
    parameters:
        location: '@=location'
    limit: 5
    assign_results_to: items
query:
    query_type: 'Children'
    parameters:
        # children of the main location
        content: '@=content'
    limit: 5
    assign_results_to: items
query:
    query_type: 'Siblings'
    parameters:
        location: '@=location'
        # Additional filters
        filter:
            content_type: ['article']  
            visible_only: false
    limit: 5
    assign_results_to: items

Subtree

Subtree of given location/content.

Name Required Description
location N -
content N -
depth N Depth relative to given location. -1 by default which means the whole subtree
query:
    query_type: 'Subtree'
    parameters:
        location: '@=location'
        depth: 2
    limit: 5
    assign_results_to: items

GeoLocation

Geographic proximity (map location)

Name Required Default Description
field Y - Field identfiier
distance Y - -
latitude Y - -
longitude Y - -
operator N <= -
query_type: GeoLocation
parameters:
    field: 'location'
    distance: 40
    latitude: '@=content.getFieldValue("location").latitude'
    longitude: '@=content.getFieldValue("location").longitude'
    filter:
        content_type: ['place']
        visible_only: false
limit: 5
assign_results_to: items

RelatedToContent

Related content

Name Required Default Description
content Y - -
field Y - -
query:
    query_type: 'RelatedToContent'
    parameters:
        content: '@=content.id'
        field: 'related'
    limit: 5
    assign_results_to: items

Common parameters

Parameters accepted by all build-in Query Types.

Name Required Description
filter N Addition query filters. See the "Common filters" section
limit N Limit for number of search hits to return
offset N Offset for search hits, used for paging the results
sort N Sorting options

Common filters

Name Default Example Description
content_type null ['article', 'blog_post'] Return only results of given content types
visible_only true false Return only visible results
siteaccess_aware true false Return only results limited to current siteaccess root

Sorting

Expected results order could be specified using sort option. Accepted values are \eZ\Publish\API\Repository\Values\Content\Query\SortClause / \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] or string which is sort specification. For example:

query:
    query_type: 'eZ:RelatedToContent'
    parameters:
        content: '@=content.id'
        field: 'related'
        sort: 'content_name asc, date_published desc'
    limit: 5
    assign_results_to: items

How to provide support for custom sort clause?

In case of simple sort clauses (accepts only sort direction in construtor parameter) use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser:

services:
    app.repository.content.query.sort_clause.custom:
        class: eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser
            arguments:
                $valueObjectClassMap:
                    custom: App\Repository\Values\Content\Query\SortClause\Custom
            tags:
                - { name: ezplatform.query_type.sort_clause_parser }

In more advanced cases implement \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface

<?php

namespace App\Repository\Values\Content\Query\SortClause;

use eZ\Publish\API\Repository\Values\Content\Query\SortClause;

final class Rate extends SortClause
{
    // ...

    public function __construct(string $typeIdentifier, string $fieldIdentifier, string $sortDirection = Query::SORT_ASC)
    {
        // ...
    }	
}
<?php 

namespace App\QueryType\SortSpec;

use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface;
use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Rate;

final class RateSortClauseParser implements SortClauseParserInterface
{
    public function parse(SortSpecParserInterface $parser, string $name): SortClause
    {
        $args = [];
        $args[] = $parser->match(Token::TYPE_ID)->getValue();
        $parser->match(Token::TYPE_DOT);
        $args[] = $parser->match(Token::TYPE_ID)->getValue();
        $args[] = $parser->parseSortDirection();

        return new Rate(...$args);
    }

    public function supports(string $name): bool
    {
        return $name === 'rate'
    }
}
services:
    App\QueryType\SortSpec\RateSortClauseParser:
        tags:
            - { name: ezplatform.query_type.sort_clause_parser }

TODO:

@adamwojs adamwojs self-assigned this Feb 3, 2020
@bdunogier
Copy link
Member

Suggestion for a description update: you could shorten the examples by limiting them to the parameters part. That way, they can be used as is in both content view and query field configurations:

content: '@=content.id'
field: 'related'
sort: 
    # Custom sort clause
    target: \App\Repository\Content\Query\CustomSortClause
    # Sorting direction
    direction: desc
    # Additional args passed to sort clause constructor
    data: ['foo', 'bar', 'baz']

eZ/Publish/Core/QueryType/BuildIn/AbstractQueryType.php Outdated Show resolved Hide resolved
eZ/Publish/Core/QueryType/BuildIn/AbstractQueryType.php Outdated Show resolved Hide resolved
eZ/Publish/Core/QueryType/BuildIn/AbstractQueryType.php Outdated Show resolved Hide resolved
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone;

final class AncestorsQueryType extends AbstractLocationQueryType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description.

use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId;

final class ChildrenQueryType extends AbstractLocationQueryType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description.

use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class GeoLocationQueryType extends AbstractQueryType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description.

use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

final class RelatedToContentQueryType extends AbstractQueryType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description.

use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone;

final class SiblingsQueryType extends AbstractLocationQueryType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing description.

@bdunogier
Copy link
Member

Note: in the examples, you could replace getField('identifier').value with getFieldValue('identifier')

@adamwojs adamwojs force-pushed the ezp_31107 branch 7 times, most recently from 45df5ca to f636bc9 Compare February 27, 2020 13:49
@adamwojs adamwojs marked this pull request as ready for review February 27, 2020 13:50
Copy link
Member

@bdunogier bdunogier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: it should be BuiltIn, with a t, not with a d.

A few suggestions for extensibility, but overall, it looks cool !


eZ\Publish\Core\QueryType\BuildIn\AncestorsQueryType:
tags:
- { name: ezpublish.query_type, alias: 'eZ:Ancestors' }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could get rid of most of that file with auto-configuration + a compiler pass:

  • map eZ\Publish\Core\QueryType\BuildIn\* to the eZ:query type namespace
  • use the file's name minus the QueryType prefix

By making the namespace mapping configurable and re-usable, we could even standardize automatic query type registration by naming convention. By default, the App\QueryType\* namespace could be mapped to
App:.

Copy link
Member

@alongosz alongosz Mar 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • map eZ\Publish\Core\QueryType\BuildIn\* to the eZ:query type namespace
  • use the file's name minus the QueryType prefix

By making the namespace mapping configurable and re-usable, we could even standardize automatic query type registration by naming convention. By default, the App\QueryType\* namespace could be mapped to
App:.

Please don't do magic here. Do not rely either on namespace or class name. This is anti-pattern and a maintenance hell in the long run (future refactoring-wise).
Both namespace and class name are things that could change. We should not force anyone to use such convention.
Architecture-wise it should be a contract, which we already have.

I actually think you can auto-configure it already thanks to #2834, if memory serves.

@adamwojs
Copy link
Member Author

adamwojs commented Mar 12, 2020

Rebased after #2958 merge

Copy link
Member

@alongosz alongosz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a full review, but in general I don't like naming convention for Query Types.
eZ is a company-wide prefix for a group of Products. Query Types are specific to eZ Platform product and naming should reflect that.

For better readability I could agree on eZ:Platform:*. This gives us future DXP expansion possibilities, e.g. eZ:Commerce:*.


eZ\Publish\Core\QueryType\BuildIn\AncestorsQueryType:
tags:
- { name: ezpublish.query_type, alias: 'eZ:Ancestors' }
Copy link
Member

@alongosz alongosz Mar 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • map eZ\Publish\Core\QueryType\BuildIn\* to the eZ:query type namespace
  • use the file's name minus the QueryType prefix

By making the namespace mapping configurable and re-usable, we could even standardize automatic query type registration by naming convention. By default, the App\QueryType\* namespace could be mapped to
App:.

Please don't do magic here. Do not rely either on namespace or class name. This is anti-pattern and a maintenance hell in the long run (future refactoring-wise).
Both namespace and class name are things that could change. We should not force anyone to use such convention.
Architecture-wise it should be a contract, which we already have.

I actually think you can auto-configure it already thanks to #2834, if memory serves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

3 participants