Copilot commented on code in PR #94:
URL: 
https://github.com/apache/skywalking-banyandb-java-client/pull/94#discussion_r2313227316


##########
src/main/java/org/apache/skywalking/banyandb/v1/client/Value.java:
##########
@@ -128,6 +129,24 @@ public BanyandbModel.TagValue serialize() {
         }
     }
 
+    /**
+     * The value of a timestamp type tag.
+     */
+    public static class TimestampTagValue extends Value<Long> implements 
Serializable<BanyandbModel.TagValue> {
+        private TimestampTagValue(Long value) {
+            super(value);
+        }
+
+        @Override
+        public BanyandbModel.TagValue serialize() {
+            Timestamp timestamp = Timestamp.newBuilder()
+                    .setSeconds(value / 1000)
+                    .setNanos((int) ((value % 1000) * 1000000))
+                    .build();
+            return 
BanyandbModel.TagValue.newBuilder().setTimestamp(timestamp).build();
+        }
+    }
+
     /**

Review Comment:
   The timestamp conversion logic is duplicated in TagAndValue.java lines 
200-203. Consider extracting this conversion to a utility method to avoid code 
duplication and ensure consistency.
   ```suggestion
               Timestamp timestamp = toProtobufTimestamp(value);
               return 
BanyandbModel.TagValue.newBuilder().setTimestamp(timestamp).build();
           }
       }
   
       /**
        * Utility method to convert milliseconds to protobuf Timestamp.
        */
       private static Timestamp toProtobufTimestamp(long millis) {
           return Timestamp.newBuilder()
                   .setSeconds(millis / 1000)
                   .setNanos((int) ((millis % 1000) * 1000000))
                   .build();
       }
   
       /**
   ```



##########
src/test/java/org/apache/skywalking/banyandb/v1/client/ITTraceTest.java:
##########
@@ -0,0 +1,271 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.banyandb.v1.client;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import org.apache.skywalking.banyandb.common.v1.BanyandbCommon;
+import org.apache.skywalking.banyandb.database.v1.BanyandbDatabase;
+import 
org.apache.skywalking.banyandb.v1.client.grpc.exception.BanyanDBException;
+import org.apache.skywalking.banyandb.v1.client.util.TimeUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static 
org.apache.skywalking.banyandb.v1.client.BanyanDBClient.DEFAULT_EXPIRE_AT;
+import static org.awaitility.Awaitility.await;
+
+/**
+ * Integration test for trace functionality.
+ * Note: This test demonstrates the trace API but requires a running BanyanDB 
instance.
+ */
+public class ITTraceTest extends BanyanDBClientTestCI {
+    private final String groupName = "sw_trace";
+    private final String traceName = "trace_data";
+    private TraceBulkWriteProcessor processor;
+
+    @Before
+    public void setUp() throws IOException, BanyanDBException, 
InterruptedException {
+        this.setUpConnection();
+        // Create trace group
+        BanyandbCommon.Group traceGroup = BanyandbCommon.Group.newBuilder()
+            
.setMetadata(BanyandbCommon.Metadata.newBuilder().setName(groupName))
+            .setCatalog(BanyandbCommon.Catalog.CATALOG_TRACE)
+            .setResourceOpts(BanyandbCommon.ResourceOpts.newBuilder()
+                .setShardNum(2)
+                .setSegmentInterval(BanyandbCommon.IntervalRule.newBuilder()
+                    .setUnit(BanyandbCommon.IntervalRule.Unit.UNIT_DAY)
+                    .setNum(1))
+                .setTtl(BanyandbCommon.IntervalRule.newBuilder()
+                    .setUnit(BanyandbCommon.IntervalRule.Unit.UNIT_DAY)
+                    .setNum(7)))
+            .build();
+        
+        this.client.define(traceGroup);
+        
+        // Create trace schema
+        BanyandbDatabase.Trace trace = BanyandbDatabase.Trace.newBuilder()
+            .setMetadata(BanyandbCommon.Metadata.newBuilder()
+                .setGroup(groupName)
+                .setName(traceName))
+            .addTags(BanyandbDatabase.TraceTagSpec.newBuilder()
+                .setName("trace_id")
+                .setType(BanyandbDatabase.TagType.TAG_TYPE_STRING))
+            .addTags(BanyandbDatabase.TraceTagSpec.newBuilder()
+                .setName("span_id")
+                .setType(BanyandbDatabase.TagType.TAG_TYPE_STRING))
+            .addTags(BanyandbDatabase.TraceTagSpec.newBuilder()
+                .setName("service_name")
+                .setType(BanyandbDatabase.TagType.TAG_TYPE_STRING))
+            .addTags(BanyandbDatabase.TraceTagSpec.newBuilder()
+                .setName("start_time")
+                .setType(BanyandbDatabase.TagType.TAG_TYPE_TIMESTAMP))
+            .setTraceIdTagName("trace_id")
+            .setTimestampTagName("start_time")
+            .build();
+            
+        this.client.define(trace);
+        this.client.define(buildIndexRule());
+        this.client.define(buildIndexRuleBinding());
+        
+        processor = client.buildTraceWriteProcessor(1000, 1, 1, 10);
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        if (processor != null) {
+            processor.close();
+        }
+        this.closeClient();
+    }
+
+    @Test
+    public void testTraceSchemaOperations() throws BanyanDBException {
+        // Test trace definition exists
+        BanyandbDatabase.Trace retrievedTrace = client.findTrace(groupName, 
traceName);
+        Assert.assertNotNull("Trace should exist", retrievedTrace);
+        Assert.assertEquals("Trace name should match", traceName, 
retrievedTrace.getMetadata().getName());
+        Assert.assertEquals("Trace group should match", groupName, 
retrievedTrace.getMetadata().getGroup());
+        
+        // Test trace exists
+        Assert.assertTrue("Trace should exist", client.existTrace(groupName, 
traceName).hasResource());
+    }
+
+    @Test
+    public void testTraceQueryByTraceId() throws BanyanDBException, 
ExecutionException, InterruptedException, TimeoutException {
+        // Test data
+        String traceId = "trace-query-test-12345";
+        String spanId = "span-query-test-67890";
+        String serviceName = "query-test-service";
+        Instant now = Instant.now();
+        byte[] spanData = "query-test-span-data".getBytes();
+        
+        // Create and write trace data
+        TraceWrite traceWrite = client.createTraceWrite(groupName, traceName, 
now.toEpochMilli())
+            .tag("trace_id", Value.stringTagValue(traceId))
+            .tag("span_id", Value.stringTagValue(spanId))
+            .tag("service_name", Value.stringTagValue(serviceName))
+            .tag("start_time", Value.timestampTagValue(now.toEpochMilli()))
+            .span(spanData)
+            .version(1L);
+            
+        // Write the trace via bulk processor
+        CompletableFuture<Void> writeFuture = processor.add(traceWrite);
+        writeFuture.exceptionally(exp -> {
+            Assert.fail("Write failed: " + exp.getMessage());
+            return null;
+        });
+        writeFuture.get(10, TimeUnit.SECONDS);
+        
+        // Create trace query with trace_id condition
+        TraceQuery query = new TraceQuery(
+            Lists.newArrayList(groupName), 
+            traceName,
+            Collections.emptySet()
+        );
+        query.and(PairQueryCondition.StringQueryCondition.eq("trace_id", 
traceId));
+
+        // Execute query with conditions
+        await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> {
+            TraceQueryResponse response = client.query(query);
+            Assert.assertNotNull("Query response should not be null", 
response);
+            Assert.assertFalse("Should have at least one result", 
response.isEmpty());
+            Assert.assertEquals("Should have exactly one span", 1, 
response.size());
+            
+            // Verify we can access span data
+            Assert.assertNotNull("Spans list should not be null", 
response.getSpans());
+            Assert.assertEquals("Should have one span in list", 1, 
response.getSpans().size());
+            
+            // Get the first span and verify its contents
+            org.apache.skywalking.banyandb.trace.v1.BanyandbTrace.Span span = 
response.getSpans().get(0);
+            Assert.assertNotNull("Span should not be null", span);
+            
+            // Verify span data (binary content) - this is the main content 
returned
+            Assert.assertNotNull("Span data should not be null", 
span.getSpan());
+            Assert.assertFalse("Span data should not be empty", 
span.getSpan().isEmpty());
+            Assert.assertArrayEquals("Span data should match", spanData, 
span.getSpan().toByteArray());
+        });
+    }
+
+    @Test
+    public void testTraceQueryOrderByStartTime() throws BanyanDBException, 
ExecutionException, InterruptedException, TimeoutException {
+        // Test data with different timestamps
+        String traceId = "trace-order-test-";
+        String serviceName = "order-test-service";
+        Instant baseTime = Instant.now().minusSeconds(60); // Start 1 minute 
ago
+        
+        // Create 3 traces with different timestamps (1 minute apart)
+        TraceWrite trace1 = client.createTraceWrite(groupName, traceName, 
baseTime.toEpochMilli())
+            .tag("trace_id", Value.stringTagValue(traceId + "1"))
+            .tag("span_id", Value.stringTagValue("span-1"))
+            .tag("service_name", Value.stringTagValue(serviceName))
+            .tag("start_time", 
Value.timestampTagValue(baseTime.toEpochMilli()))
+            .span("span-data-1".getBytes())
+            .version(1L);
+            
+        TraceWrite trace2 = client.createTraceWrite(groupName, traceName, 
baseTime.plusSeconds(60).toEpochMilli())
+            .tag("trace_id", Value.stringTagValue(traceId + "2"))
+            .tag("span_id", Value.stringTagValue("span-2"))
+            .tag("service_name", Value.stringTagValue(serviceName))
+            .tag("start_time", 
Value.timestampTagValue(baseTime.plusSeconds(60).toEpochMilli()))
+            .span("span-data-2".getBytes())
+            .version(1L);
+            
+        TraceWrite trace3 = client.createTraceWrite(groupName, traceName, 
baseTime.plusSeconds(120).toEpochMilli())
+            .tag("trace_id", Value.stringTagValue(traceId + "3"))
+            .tag("span_id", Value.stringTagValue("span-3"))
+            .tag("service_name", Value.stringTagValue(serviceName))
+            .tag("start_time", 
Value.timestampTagValue(baseTime.plusSeconds(120).toEpochMilli()))
+            .span("span-data-3".getBytes())
+            .version(1L);
+        
+        // Write the traces via bulk processor
+        CompletableFuture<Void> future1 = processor.add(trace1);
+        CompletableFuture<Void> future2 = processor.add(trace2);
+        CompletableFuture<Void> future3 = processor.add(trace3);
+        
+        CompletableFuture.allOf(future1, future2, future3).get(10, 
TimeUnit.SECONDS);
+        
+        // Create trace query with order by start_time (no trace_id condition 
as it interferes with ordering)
+        TraceQuery query = new TraceQuery(
+            Lists.newArrayList(groupName), 
+            traceName,
+            new TimestampRange(baseTime.toEpochMilli(), 
baseTime.plusSeconds(60).toEpochMilli()),
+            ImmutableSet.of("start_time")

Review Comment:
   The timestamp range only covers the first 60 seconds (baseTime to 
baseTime+60), but the test writes 3 traces at baseTime, baseTime+60, and 
baseTime+120. The third trace at baseTime+120 will be outside the query range 
and won't be returned, making the assertion on line 231 expecting exactly 2 
spans incorrect if the third trace were included.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscr...@skywalking.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to