Oql generate(Query query) {
    if (query.count() && (query.hasAggregations() || !query.groupBy().isEmpty())) {
      throw new UnsupportedOperationException("Aggregations / Group By and count(*) are not yet supported");
    }

    // wherever to rewrite query as "select count(*) from (select [distinct] ... from ... where ... limit ?)"
    boolean addOuterCountQuery = query.count() && (query.distinct() || query.hasProjections() || query.limit().isPresent());

    final StringBuilder oql = new StringBuilder("SELECT");
    if (query.distinct()) {
      oql.append(" DISTINCT");
    }

    if (query.hasProjections()) {
      // explicitly add list of projections
      List<String> paths = query.projections().stream()
              .map(this::toProjection)
              .collect(Collectors.toList());
      String projections = query.count() && !addOuterCountQuery ? " COUNT(*) " : String.join(", ", paths);
      oql.append(" ");
      oql.append(projections);
    } else {
      // no projections
      oql.append(query.count() && !addOuterCountQuery ? " COUNT(*)" : " *");
    }

    oql.append(" FROM ").append(regionName());
    final List<Object> variables = new ArrayList<>();
    if (query.filter().isPresent()) {
      Oql withVars = Geodes.converter(useBindVariables(), pathNaming()).convert(query.filter().get());
      oql.append(" WHERE ").append(withVars.oql());
      variables.addAll(withVars.variables());
    }

    if (!query.groupBy().isEmpty()) {
      oql.append(" GROUP BY ");
      oql.append(query.groupBy().stream().map(this::toProjection).collect(Collectors.joining(", ")));
    }

    if (!query.collations().isEmpty()) {
      oql.append(" ORDER BY ");

      final String ascending = isMultiDirectionCollation(query) ? " ASC" : "";
      final String orderBy = query.collations().stream()
              .map(c -> pathNaming().name(c.path()) + (c.direction().isAscending() ? ascending : " DESC"))
              .collect(Collectors.joining(", "));

      oql.append(orderBy);
    }

    query.limit().ifPresent(limit -> oql.append(" LIMIT ").append(limit));
    query.offset().ifPresent(offset -> oql.append(" OFFSET ").append(offset));

    if (addOuterCountQuery) {
      // rewrite query as "SELECT COUNT(*) FROM ($oql)"
      // Example: select count(*) from (select distinct a, b, c from /region)
      oql.insert(0, "SELECT COUNT(*) FROM (");
      oql.append(")");
    }

    return new Oql(variables, oql.toString());
  }
Don't generate queries with bind variables, use literals instead (careful with OQL injection)
criteria/geode/src/org/immutables/criteria/geode/OqlGenerator.java