@Override
  public final List<Product> findByFeatures(final Map<Integer, List<String>> featureValues) {
    CriteriaBuilder builder = manager.getCriteriaBuilder();
    CriteriaQuery<Product> criteriaQuery = builder.createQuery(Product.class);
    Root<Product> product = criteriaQuery.from(Product.class);
    Path<ProductFeature> feature = product.join("features");
    CriteriaQuery<Product> select = criteriaQuery.select(product);

    Predicate featurePredicate = builder.disjunction();
    for (final Map.Entry<Integer, List<String>> fValue : featureValues.entrySet()) {
      Predicate equalFeatureId = builder.equal(feature.get("featureId"), fValue.getKey());
      List<String> values = fValue.getValue();
      Predicate equalsValues = builder.disjunction();
      for (String value : values) {
        Predicate equalFeatureVal = builder.equal(feature.get("value"), value);
        equalsValues = builder.or(equalsValues, equalFeatureVal);
      }

      featurePredicate = builder.or(featurePredicate, builder.and(equalFeatureId, equalsValues));
    }
    select.where(featurePredicate);
    select.groupBy(product.get("id"));
    select.having(builder.equal(builder.count(product), featureValues.size()));

    TypedQuery<Product> query = manager.createQuery(criteriaQuery);
    return query.getResultList();
  }