Skip to content

Commit 0283c9f

Browse files
authored
feat: add support for custom tag order (#363)
1 parent 2366504 commit 0283c9f

File tree

9 files changed

+361
-48
lines changed

9 files changed

+361
-48
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
### Features
44

55
1. [#360](https://github.com/InfluxCommunity/influxdb3-java/pull/360): Support passing interceptors to the Flight client.
6+
1. [#363](https://github.com/InfluxCommunity/influxdb3-java/pull/363): Support custom tag order via `tagOrder` write option.
7+
See [Sort tags by priority](https://docs.influxdata.com/influxdb3/enterprise/write-data/best-practices/schema-design/#sort-tags-by-query-priority) for more.
68

79
## 1.8.0 [2026-02-19]
810

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,13 @@ To start with the client, import the `com.influxdb.v3.client` package and create
6969
package com.influxdb.v3;
7070

7171
import java.time.Instant;
72+
import java.util.List;
7273
import java.util.stream.Stream;
7374

7475
import com.influxdb.v3.client.InfluxDBClient;
7576
import com.influxdb.v3.client.query.QueryOptions;
7677
import com.influxdb.v3.client.Point;
78+
import com.influxdb.v3.client.write.WriteOptions;
7779

7880
public class IOxExample {
7981
public static void main(String[] args) throws Exception {
@@ -100,6 +102,17 @@ Point point = Point.measurement("temperature")
100102
.setTimestamp(Instant.now().minusSeconds(-10));
101103
client.writePoint(point);
102104

105+
WriteOptions orderedTagWrite = new WriteOptions.Builder()
106+
.tagOrder(List.of("region", "host"))
107+
.build();
108+
client.writePoint(
109+
Point.measurement("temperature")
110+
.setTag("host", "server-1")
111+
.setTag("region", "eu-west")
112+
.setField("value", 60.25),
113+
orderedTagWrite
114+
);
115+
103116
//
104117
// Write by LineProtocol
105118
//

src/main/java/com/influxdb/v3/client/Point.java

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@
2525
import java.math.BigInteger;
2626
import java.text.NumberFormat;
2727
import java.time.Instant;
28+
import java.util.ArrayList;
29+
import java.util.List;
2830
import java.util.Locale;
2931
import java.util.Map;
32+
import java.util.Set;
33+
import java.util.TreeSet;
3034
import java.util.concurrent.TimeUnit;
3135
import javax.annotation.Nonnull;
3236
import javax.annotation.Nullable;
@@ -530,7 +534,7 @@ public Point copy() {
530534
*/
531535
@Nonnull
532536
public String toLineProtocol() {
533-
return toLineProtocol(null);
537+
return toLineProtocol(null, null, null);
534538
}
535539

536540
/**
@@ -541,11 +545,26 @@ public String toLineProtocol() {
541545
*/
542546
@Nonnull
543547
public String toLineProtocol(@Nullable final WritePrecision precision) {
548+
return toLineProtocol(precision, null, null);
549+
}
550+
551+
/**
552+
* Transform to Line Protocol.
553+
*
554+
* @param precision required precision
555+
* @param defaultTags default tags to include in point serialization
556+
* @param tagOrder preferred order of tags in point serialization
557+
* @return Line Protocol
558+
*/
559+
@Nonnull
560+
public String toLineProtocol(@Nullable final WritePrecision precision,
561+
@Nullable final Map<String, String> defaultTags,
562+
@Nullable final List<String> tagOrder) {
544563

545564
StringBuilder sb = new StringBuilder();
546565

547566
escapeKey(sb, getMeasurement(), false);
548-
appendTags(sb);
567+
appendTags(sb, defaultTags, tagOrder);
549568
boolean appendedFields = appendFields(sb);
550569
if (!appendedFields) {
551570
return "";
@@ -564,24 +583,62 @@ private Point putField(@Nonnull final String field, @Nullable final Object value
564583
return this;
565584
}
566585

567-
private void appendTags(@Nonnull final StringBuilder sb) {
568-
569-
for (String name : values.getTagNames()) {
586+
private void appendTags(@Nonnull final StringBuilder sb,
587+
@Nullable final Map<String, String> defaultTags,
588+
@Nullable final List<String> tagOrder) {
589+
if ((defaultTags == null || defaultTags.isEmpty()) && (tagOrder == null || tagOrder.isEmpty())) {
590+
values.forEachTag((name, value) -> appendTag(sb, name, value));
591+
sb.append(' ');
592+
return;
593+
}
570594

571-
String value = values.getTag(name);
595+
Set<String> remaining = new TreeSet<>();
596+
values.forEachTagName(pointTag -> {
597+
if (!pointTag.isEmpty()) {
598+
remaining.add(pointTag);
599+
}
600+
});
601+
if (defaultTags != null) {
602+
for (String defaultTag : defaultTags.keySet()) {
603+
if (defaultTag != null && !defaultTag.isEmpty()) {
604+
remaining.add(defaultTag);
605+
}
606+
}
607+
}
572608

573-
if (name.isEmpty() || value == null || value.isEmpty()) {
574-
continue;
609+
List<String> orderedTagNames = new ArrayList<>();
610+
if (tagOrder != null && !tagOrder.isEmpty()) {
611+
for (String preferredTag : tagOrder) {
612+
if (preferredTag == null || preferredTag.isEmpty()) {
613+
continue;
614+
}
615+
if (remaining.remove(preferredTag)) {
616+
orderedTagNames.add(preferredTag);
617+
}
575618
}
619+
}
620+
orderedTagNames.addAll(remaining);
576621

577-
sb.append(',');
578-
escapeKey(sb, name);
579-
sb.append('=');
580-
escapeKey(sb, value);
622+
for (String name : orderedTagNames) {
623+
String value = values.getTag(name);
624+
if (defaultTags != null && defaultTags.containsKey(name)) {
625+
value = defaultTags.get(name);
626+
}
627+
appendTag(sb, name, value);
581628
}
582629
sb.append(' ');
583630
}
584631

632+
private void appendTag(@Nonnull final StringBuilder sb, @Nullable final String name, @Nullable final String value) {
633+
if (name == null || name.isEmpty() || value == null || value.isEmpty()) {
634+
return;
635+
}
636+
sb.append(',');
637+
escapeKey(sb, name);
638+
sb.append('=');
639+
escapeKey(sb, value);
640+
}
641+
585642
private boolean appendFields(@Nonnull final StringBuilder sb) {
586643

587644
boolean appended = false;

src/main/java/com/influxdb/v3/client/PointValues.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.time.Instant;
2626
import java.util.Map;
2727
import java.util.TreeMap;
28+
import java.util.function.BiConsumer;
29+
import java.util.function.Consumer;
2830
import javax.annotation.Nonnull;
2931
import javax.annotation.Nullable;
3032
import javax.annotation.concurrent.NotThreadSafe;
@@ -226,6 +228,16 @@ public String[] getTagNames() {
226228
return tags.keySet().toArray(new String[0]);
227229
}
228230

231+
void forEachTag(@Nonnull final BiConsumer<String, String> consumer) {
232+
Arguments.checkNotNull(consumer, "consumer");
233+
tags.forEach(consumer);
234+
}
235+
236+
void forEachTagName(@Nonnull final Consumer<String> consumer) {
237+
Arguments.checkNotNull(consumer, "consumer");
238+
tags.keySet().forEach(consumer);
239+
}
240+
229241
/**
230242
* Gets the float field value associated with the specified name.
231243
* If the field is not present, returns null.

src/main/java/com/influxdb/v3/client/internal/InfluxDBClientImpl.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.concurrent.TimeUnit;
3333
import java.util.function.BiFunction;
3434
import java.util.logging.Logger;
35-
import java.util.stream.Collectors;
3635
import java.util.stream.IntStream;
3736
import java.util.stream.Stream;
3837
import java.util.zip.GZIPOutputStream;
@@ -330,21 +329,29 @@ private <T> void writeData(@Nonnull final List<T> data, @Nonnull final WriteOpti
330329
}
331330

332331
Map<String, String> defaultTags = options.defaultTagsSafe(config);
332+
List<String> tagOrder = options.tagOrderSafe();
333+
334+
StringBuilder lineProtocolBuilder = new StringBuilder();
335+
for (T item : data) {
336+
String line;
337+
if (item == null) {
338+
line = null;
339+
} else if (item instanceof Point) {
340+
line = ((Point) item).toLineProtocol(null, defaultTags, tagOrder);
341+
} else {
342+
line = item.toString();
343+
}
344+
345+
if (line == null || line.isEmpty()) {
346+
continue;
347+
}
333348

334-
String lineProtocol = data.stream().map(item -> {
335-
if (item == null) {
336-
return null;
337-
} else if (item instanceof Point) {
338-
for (String key : defaultTags.keySet()) {
339-
((Point) item).setTag(key, defaultTags.get(key));
340-
}
341-
return ((Point) item).toLineProtocol();
342-
} else {
343-
return item.toString();
344-
}
345-
})
346-
.filter(it -> it != null && !it.isEmpty())
347-
.collect(Collectors.joining("\n"));
349+
if (lineProtocolBuilder.length() > 0) {
350+
lineProtocolBuilder.append('\n');
351+
}
352+
lineProtocolBuilder.append(line);
353+
}
354+
String lineProtocol = lineProtocolBuilder.toString();
348355

349356
if (lineProtocol.isEmpty()) {
350357
LOG.warning("No data to write, please check your input data.");
@@ -353,9 +360,9 @@ private <T> void writeData(@Nonnull final List<T> data, @Nonnull final WriteOpti
353360

354361
Map<String, String> headers = new HashMap<>(Map.of("Content-Type", "text/plain; charset=utf-8"));
355362
byte[] body = lineProtocol.getBytes(StandardCharsets.UTF_8);
356-
if (lineProtocol.length() >= options.gzipThresholdSafe(config)) {
363+
if (body.length >= options.gzipThresholdSafe(config)) {
357364
try {
358-
body = gzipData(lineProtocol.getBytes(StandardCharsets.UTF_8));
365+
body = gzipData(body);
359366
headers.put("Content-Encoding", "gzip");
360367
} catch (IOException e) {
361368
throw new InfluxDBApiException(e);

0 commit comments

Comments
 (0)