Search

Version

6.0.0 or newer

Table of contents

Search

The search is one of the most powerful yet fastest modules in Shopware.

Fields

Every field in an EntityDefinition is searchable unless they are marked with the WriteOnly flag. This empowers you to use the search for literally everything that's in the storage. To get an overview of all available fields, you can open the definitions of the defined entities or open the entity schema provided via API.

Deep Fields

In addition to that, you can get very specific about the fields you are filter or sort on. That means, that you can get products but filter on any related entity. Here are some examples to show you the idea of deep fields:

Example 1: Get products which manufacturer's name is "shopware AG"

$criteria->addFilter(
    new EqualsFilter('product.manufacturer.name', 'shopware AG')
);

Example 2: Get categories which product media have an extension "jpg"

$criteria->addFilter(
    new EqualsFilter('category.products.media.fileExtension', 'jpg')
);

Example 3: Get customers with an order which has been delivered to "Denmark" and the payment method has been PayPal

$criteria->addFilter(
    new EqualsFilter('customer.orders.deliveries.shippingOrderAddress.country.name', 'Denmark'),
    new EqualsFilter('customer.orders.paymentMethod.name', 'PayPal')
);

Associations

By default your query will only fetch the main entity you are requesting. You can add associations to your criteria object in order to fetch data for that association.

Example 1: Simple Association

In the example below, this adds the line item entity to be included with the order entity.

$orderCriteria = new Criteria();
$orderCriteria->addFilter(new EqualsFilter('orderNumber', 'SW10001'));
$orderCriteria->addAssociation('lineItems');

When looping through the order entities of a collection, this will then return a OrderLineItemEntity object than null when you use the method $orderEntity->getLineItems()

Example 2: Nested Association

You can also add nested associations to your criteria object using the same approach. In the example below, it will add the payment method entity to the transaction entity.

$orderCriteria->addAssociation('transactions.paymentMethod');

When access the transaction entity within the order entity, it will return a PaymentMethodEntity object than null when you use the method $transactionEntity->getPaymentMethod()

Filter

Filters reduce your results to your needs and will be considered when aggregating data. You can filter on every property of an entity, both via code or API.

Class nameAPI nameDescription
EqualsFiltertermExact match for the given value
EqualsAnyFiltertermsAt least one exact match for a value of the given list
ContainsFiltermatchBefore and after wildcard search for the given value
RangeFilterrangeFor range compatible fields like numbers or dates
ScoreQueryscoreOnly usable for fields with a scoring. Filter on a minimum score

Combining filters

Class nameAPI nameDescription
MultiFilternestedGroup multiple filters into one filter and concat them using the AND or OR operator
NotFilternotA negated MultiFilter

Adding Filters

The base for filters is a Criteria object, which is a definition for your search request. This is your base to start with:

$criteria = new Criteria();

// ...filters...

$results = $this->repository->search($criteria, $context);

EqualsFilter

$criteria->addFilter(
    new EqualsFilter('product.name', 'Dagger')
);
  • The first parameter $field is the field selector to filter on.
  • The second parameter $value is the value for the exact match on the given field.

EqualsAnyFilter

$criteria->addFilter(
    new EqualsAnyFilter('product.name', ['Dagger', 'Sword', 'Axe'])
);
  • The first parameter $field is the field selector to filter on.
  • The second parameter $values is a list of values for a possible exact match on the given field.

ContainsFilter

$criteria->addFilter(
    new ContainsFilter('product.description', 'iPhone')
);
  • The first parameter $field is the field selector to filter on.
  • The second parameter $value is the value for the wildcard query. In this case, the filter matches all entries where "iPhone" is anywhere in the description.

RangeFilter

$criteria->addFilter(
    new RangeFilter('product.stock', ['gt' => 10]),
    new RangeFilter('product.stock', ['gte' => 20]),
    new RangeFilter('product.stock', ['lt' => 30]),
    new RangeFilter('product.stock', ['lte' => 40])
);
  • The first parameter $field is the field selector to filter on.
  • The second parameter $range is an array containing the comparison. There are four comparisons available:
    • gt - The value must be greater than the given value
    • gte - The value must be greater than or equals the given value
    • lt - The value must be lower than the given value
    • lte - The value must be lower than or equals the given value

You can even combine multiple comparisons to define a range between a given value. The example below matches products with a stock between 10 and 20:

$criteria->addFilter(
    new RangeFilter('product.stock', ['gt' => 10, 'lt' => 20]),
);

ScoreQuery

$criteria->addFilter(
    new ScoreQuery(new EqualsFilter('product.description', 'Blue'), 10),
    new ScoreQuery(new EqualsFilter('product.description', 'Red'), 100, 'product.stock'),
);
  • The first parameter $query defines the expression for the score query.
  • The second parameter $score defines the score which should be used if the expression matches. In case that "Blue" is found in product.description, it gets an additional score of 100.
  • The third parameter $scoreField allows defining a multiplier for the score. For example: In case that "Red" is found in product.description, the score of 100 is multiplied with the value of product.stock.

MultiFilter

$criteria->addFilter(new MultiFilter(
    MultiFilter::CONNECTION_OR,
    [
        new EqualsFilter('product.name', 'Dagger'),
        new RangeFilter('product.stock', ['gt' => 10, 'lt' => 20]),
    ]
));

The nested query groups multiple queries into one and concat them using the AND or OR operator.

  • The first parameter $operator defines the operator for the queries. You can choose between AND and OR.
  • The second parameter $queries is a list of additional queries to be grouped.

NotFilter

$criteria->addFilter(new NotFilter(
    NotFilter::CONNECTION_AND,
    [new EqualsAnyFilter('product.name', ['Sword', 'Axe'])]
));

The NotFilter is an equivalent to the MultiFilter with the only difference, that the result of the inner queries is negated.

  • The first parameter $operator defines the operator for the queries. You can choose between AND and OR.
  • The second parameter $queries is a list of additional queries to be grouped and negated.

Post-Filter

Post-Filters work the same way as filters, but they won't be considered when aggregating data.

When to use Post-Filters?

A common use-case for post filters is to get only active products, but the total of products should be without any filter active:

Given 20 products with 5 of them are active, your filter would be empty and your post-filter contains a EqualsFilter on product.active. You will get the 5 active products but the calculated total count of products will still be 20.

$criteria = new Criteria();
$criteria->addPostFilter(new EqualsFilter('product.active', true));

$results = $this->repository->search($criteria, $context);

echo $results->getTotal(); // 20
echo $results->getEntities()->count(); // 5

Sort

The Criteria object supports to sort entities. You can add multiple sorting rules to the criteria object to define the sorting order.

$criteria->addSorting(
    new FieldSorting('product.name')
);
  • The first parameter $field is the field selector to sort on.
  • The second parameter $direction is the direction to sort. Available options are:
    • FieldSorting::ASCENDING for A-Z sorting
    • FieldSorting::DESCENDING for Z-A sorting

Aggregate

Aggregations are a powerful tool which allows you to gather statistical data about your executed query.

Class nameAPI nameTypeReturn valuesDescription
AvgAggregationavgsinge-valueavgAverage of all numeric values for the specified field
CountAggregationcountsingle-valuecountNumber of records for the specified field
MaxAggregationmaxsingle-valuemaxMaximum value for the specified field
MinAggregationminsingle-valueminMinimal value for the specified field
StatsAggregationstatsmulti-valuecount, avg, sum, min, maxStats overall numeric values for the specified field
SumAggregationsumsingle-valuesumSum of all numeric values for the specified field
FilterAggregationfilternone-valueAllows to filter the aggregation result
EntityAggregationentitymulti-valueentitiesGroups the result for each value of the provided field and fetches the entities for this field
TermsAggregationtermsmulti-valuebuckets,countGroups the result for each value of the provided field and fetches the count of affected documents
DateHistogramAggregationhistogrammulti-valuebuckets,countGroups the result for each value of the provided field and fetches the count of affected documents. Although allows to provide date interval (day, month, ...)

AvgAggregation

$criteria->addAggregation(
    new AvgAggregation('avg-price', 'product.price')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var AvgResult $avg */
$avg = $result->get('avg-price');

$avg->getAvg();

CountAggregation

$criteria->addAggregation(
    new CountAggregation('count-manufacturer', 'product.manufacturerId')
);
$result = $this->repository->aggregate($criteria, $context);

/** @var CountResult $count */
$count = $result->get('count-manufacturer');

$count->getCount();

MaxAggregation

$criteria->addAggregation(
    new MaxAggregation('max-price', 'product.price')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var MaxResult $max */
$max = $result->get('max-price');

$max->getMax();

MinAggregation

$criteria->addAggregation(
    new MinAggregation('min-price', 'product.price')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var MinResult $min */
$min = $result->get('min-price');

$min->getMin();

StatsAggregation

$criteria->addAggregation(
    new StatsAggregation('stats-price', 'product.price')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var StatsResult $stats */
$stats = $result->get('stats-price');

$stats->getMin();
$stats->getMax();
$stats->getAvg();
$stats->getSum();

SumAggregation

$criteria->addAggregation(
    new SumAggregation('sum-price', 'product.price')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var SumResult $sum */
$sum = $result->get('sum-price');

$sum->getSum();

FilterAggregation

$criteria->addAggregation(
    new FilterAggregation(
        'filter',
        new AvgAggregation('avg-price', 'product.price'),
        [new EqualsAnyFilter('product.active', true)]
    )
);

$result = $this->repository->aggregate($criteria, $context);

$price = $result->get('avg-price');

$price->getAvg();

TermsAggregation

$criteria->addAggregation(
    new TermsAggregation('category-ids', 'product.categories.id')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var TermsResult $categoryAgg */
$categoryAgg = $result->get('category-ids');

foreach ($categoryAgg->getBuckets() as $bucket) {
    $categoryId = $bucket->getKey();
    $count = $bucket->getCount();
}

DateHistogramAggregation

$criteria->addAggregation(
    new DateHistogramAggregation('release-histogram', 'product.releaseDate', 'month')
);

$result = $this->repository->aggregate($criteria, $context);

/** @var DateHistogramResult $histogram */
$histogram = $result->get('release-histogram');

foreach ($histogram->getBuckets() as $bucket) {
    $releaseDate = $bucket->getKey();
    $count = $bucket->getCount();
}

Total Count Mode

The total count mode allows you to configure the value of the total property of the result. This gives you more control of expensive queries. There are three modes available:

ModePerformanceDescriptionUsage
Criteria::TOTAL_COUNT_MODE_EXACTslowFetches the exact total countIf an exact pagination is required
Criteria::TOTAL_COUNT_MODE_NEXT_PAGESfastFetches limit * 5 + 1 to evaluate if there are more itemsIf pagination is satisfied with the information that more than 5 pages exist
Criteria::TOTAL_COUNT_MODE_NONE (default)fastestDoes not fetch the total countIf no pagination required

The total count mode is set on the Criteria object directly.

$criteria->setTotalCountMode(Criteria::TOTAL_COUNT_MODE_NEXT_PAGES);