0
\$\begingroup\$

description

  • I'm using influxDB, I need use Java to generate a query command.
  • The TestingTag struct maybe missing 0-3 fields.

code:

    public List<FluxTable> getAggregatedValueWithTag(String type, String fieldName, String func, TestingTag tag) {
        queryApi = influxDBClient.getQueryApi();
        String flux =
            String.format("from(bucket: \"%s\")\n",bucket) +
            "  |> range(start: 0)\n" +
            String.format("  |> filter(fn: (r) => r[\"_measurement\"] == \"%s\")\n",type) +
            (tag.getPackageName() == null ? "" : String.format("  |> filter(fn: (r) => r[\"packageName\"] == \"%s\")\n",tag.getPackageName())) +
            (tag.getDeviceID() == null ? "" : String.format("  |> filter(fn: (r) => r[\"deviceID\"] == \"%s\")\n",tag.getDeviceID())) +
            (tag.getRunTime() == null ? "" : String.format("  |> filter(fn: (r) => r[\"runTime\"] == \"%s\")\n",tag.getRunTime())) +
            String.format("  |> filter(fn: (r) => r[\"_field\"] == \"%s\")\n",fieldName) +
            String.format("  |> aggregateWindow(every: 1mo, fn: %s, createEmpty: false)\n",func) +
            String.format("  |> yield(name: \"%s\")",func);
        return queryApi.query(flux, org);
    }
  • I need to copy-paste the field command for 3 times. The fields may be much more, such as 10. When I have to copy-paste it for many times, may forget to change some parts of code to make it buggy.

my try:

  • Use a loop to deal with it. But I have to use reflection to do tag.get{fieldName}, which maybe not a proper way in Java?

question:

  • Any better way to format this string?
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Please don't post random slapped-together lines of code that couldn't possibly compile to CodeReview. We strongly prefer code that compiles, even if all the dependencies aren't available to us. This method will never compile.

In canonical Java, there is whitespace after a ,

String building is typically cleaner when done with a StringBuilder.

A simple helper method to generate a filter string would greatly clean up the numerous repetitions of string formatting.

More helper methods would make the whole method read more cleanly.

If you need a generally useful filter, instead of just this one method, make a QueryBuilder class to do the heavy lifting.

Helper methods would look something like:

public List<FluxTable> getAggregatedValueWithTag(String type, String fieldName, String func, TestingTag tag) {
    QueryApi queryApi = influxDBClient.getQueryApi();
    String flux = new StringBuilder()
            .append(from(bucket))
            .append(range(0))
            .append(filter("_measurement", type))
            .append(filter("packageName", tag.getPackageName()))
            .append(filter("deviceID", tag.getDeviceID()))
            .append(filter("runTime", tag.getRunTime()))
            .append(filter("_field", fieldName))
            .append(aggregateWindow("1m", func, false))
            .append(yield(func))
            .toString();
    return queryApi.query(flux, org);
}

private String from(String bucket) {
    return String.format("from(bucket: \"%s\")\n", bucket);
}

private String range(int start) {
    return String.format("  |> range(start: %d)\n", start);
}

private String filter(String name, String value) {
    if (value == null) {
        return "";
    }
    return String.format("  |> filter(fn: (r) => r[\"%s\"] == \"%s\")\n", name, value);
}

private String aggregateWindow(String frequency, String function, boolean createEmpty) {
    return String.format("  |> aggregateWindow(every: %s, fn: %s, createEmpty: %b)\n", frequency, function, createEmpty);
}

private String yield(String name) {
    return String.format("  |> yield(name: \"%s\")", name);
}

A QueryBuilder class would look something like:

public final class QueryBuilder {

    private final StringBuilder query = new StringBuilder();

    public static QueryBuilder fromBucket(String bucket) {
        return new QueryBuilder("bucket", bucket);
    }

    private QueryBuilder(String fromType, String from) {
        append("from(%s: \"%s\")\n", fromType, from);
    }

    public QueryBuilder range(int start) {
        append("  |> range(start: %d)\n", start);
        return this;
    }

    public QueryBuilder filter(String name, String value) {
        if (value != null) {
            append("  |> filter(fn: (r) => r[\"%s\"] == \"%s\")\n", name, value);
        }
        return this;
    }

    public QueryBuilder aggregateWindow(String frequency, String function, boolean createEmpty) {
        append("  |> aggregateWindow(every: %s, fn: %s, createEmpty: %b)\n", frequency, function, createEmpty);
        return this;
    }

    public QueryBuilder yield(String name) {
        append("  |> yield(name: \"%s\")", name);
        return this;
    }

    public String toString() {
        return query.toString();
    }

    private void append(String formatString, Object... values) {
        query.append(String.format(formatString, values));
    }
}

and the calling method would then look like:

public List<FluxTable> getAggregatedValueWithTag(String type, String fieldName, String func, TestingTag tag) {
    QueryApi queryApi = influxDBClient.getQueryApi();
    String flux = QueryBuilder.fromBucket(bucket)
            .range(0)
            .filter("_measurement", type)
            .filter("packageName", tag.getPackageName())
            .filter("deviceID", tag.getDeviceID())
            .filter("runTime", tag.getRunTime())
            .filter("_field", fieldName)
            .aggregateWindow("1m", func, false)
            .yield(func)
            .toString();
    return queryApi.query(flux, org);
}
\$\endgroup\$
3
  • \$\begingroup\$ Last time I post a MRE code in question, then my question got removed. They told me I need to provide real code not the example code. If I provide a real compilable code, it'll contain a lot irrevent code in it, then no one want to review such a long code. \$\endgroup\$ Commented Sep 8, 2021 at 6:37
  • \$\begingroup\$ codereview.meta.stackexchange.com/questions/3649/… \$\endgroup\$
    – Eric Stein
    Commented Sep 8, 2021 at 11:49
  • \$\begingroup\$ Also, you might want to give a little more time for other people to give answers before you accept an answer. Seeing an accepted answer discourages others from providing answers. \$\endgroup\$
    – Eric Stein
    Commented Sep 8, 2021 at 11:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.