Skip to content

Searching

This section covers how the SearchService can be used to search for Content, by using a Query and a combinations of Criteria you will get a SearchResult object back containing list of Content and count of total hits. In the future this object will also include facets, spell checking etc. when running on a backend that supports it (for instance Solr).

Note

To be able to use search API described in this section, you need to create some content (articles, folders) under eZ Platform root.

Difference between filter and query

Query object contains two properties you can set criteria on filter and query. You can mix and match use or use both at the same time, there is one distinction between the two:

  • query Has an effect on scoring (relevancy) calculation, and also on the default sorting if sortClause is not specified, when used with Solr and Elastic. Typically query is used for FullText search criterion, otherwise you can place everything else on filter.

Checking feature support per search engine

To find out if a given search engine supports advanced full text capabilities, use the $searchService->supports($capabilityFlag) method.

In this recipe, you will run a simple full text search over every compatible attribute.

Query and Criterion objects

Described aboveQuery object is used to build up a Content query based on a set of Criterion objects.

1
2
3
$query = new \eZ\Publish\API\Repository\Values\Content\Query();
// Use 'query' over 'filter' for FullText to get hit score (relevancy) with Solr/Elastic
$query->query = new Query\Criterion\FullText( $text );

Multiple criteria can be grouped together using "logical criteria", such as LogicalAnd or LogicalOr. Since in this case you only want to run a text search, simply use a FullText criterion object.

The full list of criteria can be found on your installation in the following directory vendor/ezsystems/ezpublish-kernel/eZ/Publish/API/Repository/Values/Content/Query/Criterion. Additionally you may look at integration tests like vendor/ezsystems/ezpublish-kernel/eZ/Publish/API/Repository/Tests/SearchServiceTest.php for more details on how these are used.

Running the search query

The Query object is given as an argument to SearchService::findContent(). This method returns a SearchResult object. This object provides you with various information about the search operation (number of results, time taken, spelling suggestions, or facets, as well as, of course, the results themselves).

1
2
3
4
5
6
$result = $searchService->findContent( $query );
$output->writeln( 'Found ' . $result->totalCount . ' items' );
foreach ( $result->searchHits as $searchHit )
{
    $output->writeln( $searchHit->valueObject->contentInfo->name );
}

The searchHits properties of the SearchResult object is an array of SearchHit objects. In valueObject property of SearchHit, you will find the Content object that matches the given Query.

Tip

If you you are searching using a unique identifier, for instance using the Content ID or Content remote ID criterion, then you can use SearchService::findSingle(), this takes a Criterion and returns a single Content item, or throws a NotFound exception if none is found.

Retrieving Sort Clauses for parent Location

You can use the method $parentLocation->getSortClauses() to return an array of Sort Clauses for direct use on LocationQuery->sortClauses.

As explained in the previous chapter, Criterion objects are grouped together using logical criteria. You will now see how multiple criteria objects can be combined into a fine grained search Query.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query;

// [...]

$query = new Query();
$criterion1 = new Criterion\Subtree( $locationService->loadLocation( 2 )->pathString );
$criterion2 = new Criterion\ContentTypeIdentifier( 'folder' );
$query->filter = new Criterion\LogicalAnd(
    array( $criterion1, $criterion2 )
);

$result = $searchService->findContent( $query );

A Subtree criterion limits the search to the subtree with pathString, which looks like: /1/2/. A ContentTypeId Criterion to limit the search to Content of Content Type 1. Those two criteria are grouped with a LogicalAnd operator. The query is executed as before, with SearchService::findContent().

Fine-tuning search results

$languageFilter

The $languageFilter parameter provides a prioritized list of languages for the current SiteAccess. Passing it is recommended for front-end use, because otherwise all languages of the Content items will be returned.

Additionally, you can make use of the useAlwaysAvailable argument of the $languageFilter. This in turn uses the alwaysAvailable flag which by default is set on Content Type. When it is set to true, it ensures that when a language from the prioritized list can't be matched, the Content will be returned in its main language.

Criterion\Visibility

Criterion\Visibility enables you to ensure that only visible content will be returned.

Note that the criterion behaves differently depending on the method you use, because Locations have visibility, but Content does not. This means that when using the LocationQuery (findLocations($query)), the method will return the Location, if it is visible. When used with the Query (findContent($query)), however, the Content item will be returned even if one of its Locations is visible (although others may be hidden). That is why using Criterion\Visibility is recommended with LocationQuery.

This example shows the usage of both $languageFilter and Criterion\Visibility:

1
2
3
4
5
6
7
8
9
$query = new LocationQuery([
    'filter' => new Criterion\LogicalAnd([
    new Criterion\Visibility(Criterion\Visibility::VISIBLE),
    new Criterion\ParentLocationId($parentLocation->id),
    ];),
    'sortClauses' => $parentLocation->getSortClauses(),
]);
$searchService->findLocations($query,
    ['languages' => $configResolver->getParameter('languages')]);

A search isn't only meant for searching, it also provides the interface for what was called "fetch" in previous versions. As this is back-end agnostic, eZ Platform "ezfind" fetch functions are now powered by Solr (or ElasticSearch in experimental, unsupported setups).

Following the examples above you now change it a bit to combine several criteria with both an AND and an OR condition.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query;

// [...]

$query = new Query();
$query->filter = new Criterion\LogicalAnd(
    array(
        new Criterion\ParentLocationId( 2 ),
        new Criterion\LogicalOr(
            array(
                new Criterion\ContentTypeIdentifier( 'folder' ),
                new Criterion\ContentTypeId( 2 )
            )
        )
    )
);

$result = $searchService->findContent( $query );

ParentLocationId criterion limits the search to the children of Location 2. An array of ContentTypeId Criteria to limit the search to Content of Content Type's with ID 1 or 2 grouped in a LogicalOr operator. Those two criteria are grouped with a LogicalAnd operator. As always the query is executed as before, with SearchService::findContent().

Change the Location filter to use the Subtree criterion filter as shown in the advanced search example above.

Using in() instead of OR

The above example is fine, but it can be optimized by taking advantage of the fact that all filter criteria support being given an array of values (IN operator) instead of a single value (EQ operator).

You can also use the ContentTypeIdentifier Criterion:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query;

// [...]

$query = new Query();
$query->filter = new Criterion\LogicalAnd(
    array(
        new Criterion\ParentLocationId( 2 ),
        new Criterion\ContentTypeIdentifier( array( 'article', 'folder' ) )
    )
);

$result = $searchService->findContent( $query );

Tip

All filter criteria are capable of doing an "IN" selection, the ParentLocationId above could, for example, have been provided array( 2, 43 ) to include second level children in both your content tree (2) and your media tree (43).

Note

Faceted search is only available with Solr search engine.

To perform a faceted search, set the Query->facetBuilders property. Relevant facets will be returned in SearchResult->facets.

All facet builders share the following properties:

Property Description
name Recommended, sets the human-readable name of the returned facet for use in UI. If you need translation this value should already be translated.
minCount Optional, the minimum number of hits to create a group, e.g. minimum number of Content items in a given facet for it to be returned.
limit Optional, the maximum number of facets to be returned; only this number of facets with the greatest number of hits will be returned.

As an example, apply UserFacet to be able to group content according to the creator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder;

// [...]

$query = new Query();
$query->filter = new Criterion\ContentTypeIdentifier(['article']);
$query->facetBuilders[] = new FacetBuilder\UserFacetBuilder(
    [
        'name' => 'Document owner',
        // Specific to UserFacetBuilder, one of: OWNER, GROUP or MODIFIER
        'type' => FacetBuilder\UserFacetBuilder::OWNER,
        'minCount' => 2,
        'limit' => 5
    ]
);

$result = $searchService->findContentInfo( $query );
list( $userId, $articleCount ) = $result->facets[0]->entries;

Available FacetBuilders

ContentTypeFacetBuilder

Arguments:

  • name: string
  • minCount (optional): integer
  • limit (optional): integer

Example:

1
2
3
4
5
    $query->facetBuilders[] = new FacetBuilder\ContentTypeFacetBuilder(
        [
            'name' => 'ContentType',
        ]
    );

SectionFacetBuilder

Arguments:

  • name: string
  • minCount (optional): integer
  • limit (optional): integer

Example:

1
2
3
4
5
    $query->facetBuilders[] = new FacetBuilder\SectionFacetBuilder(
        [
            'name' => 'Section',
        ]
    );

UserFacetBuilder

Arguments:

  • name: string
  • type: string [OWNER = 'owner', GROUP = 'group', MODIFIER = 'modifier']
  • minCount (optional): integer
  • limit (optional): integer

Example:

1
2
3
4
5
6
    $query->facetBuilders[] = new FacetBuilder\UserFacetBuilder(
        [
            'name' => 'User',
            'type' => FacetBuilder\UserFacetBuilder::GROUP,
        ]
    );

Performing a pure search count

In many cases you might need the number of Content items matching a search, but with no need to do anything else with the results.

Thanks to the fact that the searchHits property of the SearchResult object always refers to the total amount, it is enough to run a standard search and set $limit to 0. This way no results will be retrieved, and the search will not be slowed down, even when the number of matching results is huge.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use eZ\Publish\API\Repository\Values\Content\Query;

// [...]

$query = new Query();
$query->limit = 0;

// [...] ( Add criteria as shown above )

$resultCount = $searchService->findContent( $query )->totalCount;
Read the Docs