children;
+
+ public long getDimensionValue() {
+ return dimensionValue;
+ }
+
+ public byte getNodeType() {
+ return nodeType;
+ }
}
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTree.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTree.java
new file mode 100644
index 0000000000000..f2d4ce2fef83a
--- /dev/null
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTree.java
@@ -0,0 +1,66 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+package org.opensearch.index.compositeindex.datacube.startree.node;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.RandomAccessInput;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.meta.StarTreeMetadata;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.writer.StarTreeDataWriter;
+
+import java.io.IOException;
+
+import static org.opensearch.index.compositeindex.CompositeIndexConstants.COMPOSITE_FIELD_MARKER;
+import static org.opensearch.index.compositeindex.CompositeIndexConstants.VERSION;
+
+/**
+ * Off heap implementation of the star-tree.
+ *
+ * @opensearch.experimental
+ */
+public class StarTree implements Tree {
+ private static final Logger logger = LogManager.getLogger(StarTree.class);
+ private final FixedLengthStarTreeNode root;
+ private final Integer numNodes;
+
+ public StarTree(IndexInput data, StarTreeMetadata starTreeMetadata) throws IOException {
+ long magicMarker = data.readLong();
+ if (COMPOSITE_FIELD_MARKER != magicMarker) {
+ logger.error("Invalid magic marker");
+ throw new IOException("Invalid magic marker");
+ }
+ int version = data.readInt();
+ if (VERSION != version) {
+ logger.error("Invalid star tree version");
+ throw new IOException("Invalid version");
+ }
+ numNodes = data.readInt(); // num nodes
+
+ RandomAccessInput in = data.randomAccessSlice(
+ StarTreeDataWriter.computeStarTreeDataHeaderByteSize(),
+ starTreeMetadata.getDataLength() - StarTreeDataWriter.computeStarTreeDataHeaderByteSize()
+ );
+ root = new FixedLengthStarTreeNode(in, 0);
+ }
+
+ @Override
+ public StarTreeNode getRoot() {
+ return root;
+ }
+
+ /**
+ * Returns the number of nodes in star-tree
+ *
+ * @return number of nodes in te star-tree
+ */
+ public Integer getNumNodes() {
+ return numNodes;
+ }
+
+}
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNode.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNode.java
index 59522ffa4be89..3c2acadff6a96 100644
--- a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNode.java
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNode.java
@@ -20,7 +20,6 @@
*/
@ExperimentalApi
public interface StarTreeNode {
- long ALL = -1l;
/**
* Returns the dimension ID of the current star-tree node.
@@ -86,12 +85,12 @@ public interface StarTreeNode {
boolean isLeaf();
/**
- * Checks if the current node is a star node.
+ * Checks if the current node is a star node, null node or a node with actual dimension value.
*
- * @return true if the node is a star node, false otherwise
- * @throws IOException if an I/O error occurs while reading the star node status
+ * @return the node type value based on the star-tree node type
+ * @throws IOException if an I/O error occurs while reading the node type
*/
- boolean isStarNode() throws IOException;
+ byte getStarTreeNodeType() throws IOException;
/**
* Returns the child star-tree node for the given dimension value.
@@ -100,7 +99,7 @@ public interface StarTreeNode {
* @return the child node for the given dimension value or null if child is not present
* @throws IOException if an I/O error occurs while retrieving the child node
*/
- StarTreeNode getChildForDimensionValue(long dimensionValue) throws IOException;
+ StarTreeNode getChildForDimensionValue(long dimensionValue, boolean isStar) throws IOException;
/**
* Returns an iterator over the children of the current star-tree node.
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNodeType.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNodeType.java
new file mode 100644
index 0000000000000..856fee072cd9e
--- /dev/null
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/StarTreeNodeType.java
@@ -0,0 +1,90 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.index.compositeindex.datacube.startree.node;
+
+/**
+ * Represents the different types of nodes in a StarTree data structure.
+ *
+ * In order to handle different node types, we use a byte value to represent the node type.
+ * This enum provides a convenient way to map byte values to their corresponding node types.
+ *
+ * Star and Null Nodes are represented as special cases. Default is the general case.
+ * Star and Null nodes are represented with negative ordinals so that first node is Star, second node is Null Node
+ * and the rest of the default nodes are sorted based on dimension values.
+ *
+ * By default, we want to consider nodes as default node.
+ *
+ * @opensearch.experimental
+ * @see StarTreeNode
+ */
+public enum StarTreeNodeType {
+
+ /**
+ * Represents a star node type.
+ *
+ */
+ STAR("star", (byte) -2),
+
+ /**
+ * Represents a null node type.
+ */
+ NULL("null", (byte) -1),
+
+ /**
+ * Represents a default node type.
+ */
+ DEFAULT("default", (byte) 0);
+
+ private final String name;
+ private final byte value;
+
+ /**
+ * Constructs a new StarTreeNodeType with the given name and value.
+ *
+ * @param name the name of the node type
+ * @param value the value associated with the node type
+ */
+ StarTreeNodeType(String name, byte value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ /**
+ * Returns the name of the node type.
+ *
+ * @return the name of the node type
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the value associated with the node type.
+ *
+ * @return the value associated with the node type
+ */
+ public byte getValue() {
+ return value;
+ }
+
+ /**
+ * Returns the StarTreeNodeType enum constant with the specified value.
+ *
+ * @param value the value of the enum constant to return
+ * @return the enum constant with the specified value, or null if no such constant exists
+ */
+ public static StarTreeNodeType fromValue(byte value) {
+ for (StarTreeNodeType nodeType : StarTreeNodeType.values()) {
+ if (nodeType.getValue() == value) {
+ return nodeType;
+ }
+ }
+ return null;
+ }
+}
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/Tree.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/Tree.java
new file mode 100644
index 0000000000000..811150da64bec
--- /dev/null
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/Tree.java
@@ -0,0 +1,23 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+package org.opensearch.index.compositeindex.datacube.startree.node;
+
+/**
+ * Interface for star-tree.
+ *
+ * @opensearch.experimental
+ */
+public interface Tree {
+
+ /**
+ * Fetches the root node of the star-tree.
+ * @return the root of the star-tree
+ */
+ StarTreeNode getRoot();
+
+}
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/package-info.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/package-info.java
index 516d5b5a012ab..19d12bc6318d7 100644
--- a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/package-info.java
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/node/package-info.java
@@ -8,5 +8,7 @@
/**
* Holds classes associated with star tree node
+ *
+ * @opensearch.experimental
*/
package org.opensearch.index.compositeindex.datacube.startree.node;
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/SequentialDocValuesIterator.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/SequentialDocValuesIterator.java
index 400d7a1c00104..bf1f1d52b4eee 100644
--- a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/SequentialDocValuesIterator.java
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/SequentialDocValuesIterator.java
@@ -9,6 +9,7 @@
package org.opensearch.index.compositeindex.datacube.startree.utils;
+import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.search.DocIdSetIterator;
import org.opensearch.common.annotation.ExperimentalApi;
@@ -28,6 +29,11 @@ public class SequentialDocValuesIterator {
*/
private final DocIdSetIterator docIdSetIterator;
+ /**
+ * The value associated with the latest document.
+ */
+ private Long docValue;
+
/**
* The id of the latest document.
*/
@@ -42,15 +48,50 @@ public SequentialDocValuesIterator(DocIdSetIterator docIdSetIterator) {
this.docIdSetIterator = docIdSetIterator;
}
+ /**
+ * Constructs a new SequentialDocValuesIterator instance with an empty sorted numeric.
+ *
+ */
+ public SequentialDocValuesIterator() {
+ this.docIdSetIterator = DocValues.emptySortedNumeric();
+ }
+
+ /**
+ * Returns the value associated with the latest document.
+ *
+ * @return the value associated with the latest document
+ */
+ public Long getDocValue() {
+ return docValue;
+ }
+
+ /**
+ * Sets the value associated with the latest document.
+ *
+ * @param docValue the value to be associated with the latest document
+ */
+ public void setDocValue(Long docValue) {
+ this.docValue = docValue;
+ }
+
/**
* Returns the id of the latest document.
*
* @return the id of the latest document
*/
- int getDocId() {
+ public int getDocId() {
return docId;
}
+ /**
+ * Sets the id of the latest document.
+ *
+ * @param docId the ID of the latest document
+ */
+ public void setDocId(int docId) {
+ this.docId = docId;
+ }
+
/**
* Returns the DocIdSetIterator associated with this instance.
*
@@ -65,7 +106,7 @@ public int nextDoc(int currentDocId) throws IOException {
if (docId >= currentDocId) {
return docId;
}
- docId = this.docIdSetIterator.nextDoc();
+ setDocId(this.docIdSetIterator.nextDoc());
return docId;
}
@@ -81,7 +122,12 @@ public Long value(int currentDocId) throws IOException {
if (docId == DocIdSetIterator.NO_MORE_DOCS || docId != currentDocId) {
return null;
}
- return sortedNumericDocValues.nextValue();
+ if (docValue == null) {
+ docValue = sortedNumericDocValues.nextValue();
+ }
+ Long nextValue = docValue;
+ docValue = null;
+ return nextValue;
} else {
throw new IllegalStateException("Unsupported Iterator requested for SequentialDocValuesIterator");
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/StarTreeUtils.java b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/StarTreeUtils.java
new file mode 100644
index 0000000000000..dc155df4eafca
--- /dev/null
+++ b/server/src/main/java/org/opensearch/index/compositeindex/datacube/startree/utils/StarTreeUtils.java
@@ -0,0 +1,111 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+package org.opensearch.index.compositeindex.datacube.startree.utils;
+
+import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.index.VectorEncoding;
+import org.apache.lucene.index.VectorSimilarityFunction;
+import org.opensearch.index.compositeindex.datacube.startree.aggregators.MetricAggregatorInfo;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Util class for building star tree
+ *
+ * @opensearch.experimental
+ */
+public class StarTreeUtils {
+
+ private StarTreeUtils() {}
+
+ public static final int ALL = -1;
+
+ /**
+ * The suffix appended to dimension field names in the Star Tree index.
+ */
+ public static final String DIMENSION_SUFFIX = "dim";
+
+ /**
+ * The suffix appended to metric field names in the Star Tree index.
+ */
+ public static final String METRIC_SUFFIX = "metric";
+
+ /**
+ * Returns the full field name for a dimension in the star-tree index.
+ *
+ * @param starTreeFieldName star-tree field name
+ * @param dimensionName name of the dimension
+ * @return full field name for the dimension in the star-tree index
+ */
+ public static String fullyQualifiedFieldNameForStarTreeDimensionsDocValues(String starTreeFieldName, String dimensionName) {
+ return starTreeFieldName + "_" + dimensionName + "_" + DIMENSION_SUFFIX;
+ }
+
+ /**
+ * Returns the full field name for a metric in the star-tree index.
+ *
+ * @param starTreeFieldName star-tree field name
+ * @param fieldName name of the metric field
+ * @param metricName name of the metric
+ * @return full field name for the metric in the star-tree index
+ */
+ public static String fullyQualifiedFieldNameForStarTreeMetricsDocValues(String starTreeFieldName, String fieldName, String metricName) {
+ return MetricAggregatorInfo.toFieldName(starTreeFieldName, fieldName, metricName) + "_" + METRIC_SUFFIX;
+ }
+
+ /**
+ * Get field infos from field names
+ *
+ * @param fields field names
+ * @return field infos
+ */
+ public static FieldInfo[] getFieldInfoList(List fields) {
+ FieldInfo[] fieldInfoList = new FieldInfo[fields.size()];
+
+ // field number is not really used. We depend on unique field names to get the desired iterator
+ int fieldNumber = 0;
+
+ for (String fieldName : fields) {
+ fieldInfoList[fieldNumber] = getFieldInfo(fieldName, fieldNumber);
+ fieldNumber++;
+ }
+ return fieldInfoList;
+ }
+
+ /**
+ * Get new field info instance for a given field name and field number
+ * @param fieldName name of the field
+ * @param fieldNumber number of the field
+ * @return new field info instance
+ */
+ public static FieldInfo getFieldInfo(String fieldName, int fieldNumber) {
+ return new FieldInfo(
+ fieldName,
+ fieldNumber,
+ false,
+ false,
+ true,
+ IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS,
+ DocValuesType.SORTED_NUMERIC,
+ -1,
+ Collections.emptyMap(),
+ 0,
+ 0,
+ 0,
+ 0,
+ VectorEncoding.FLOAT32,
+ VectorSimilarityFunction.EUCLIDEAN,
+ false,
+ false
+ );
+ }
+
+}
diff --git a/server/src/main/java/org/opensearch/index/compositeindex/package-info.java b/server/src/main/java/org/opensearch/index/compositeindex/package-info.java
index 59f18efec26b1..9a88f88d9850a 100644
--- a/server/src/main/java/org/opensearch/index/compositeindex/package-info.java
+++ b/server/src/main/java/org/opensearch/index/compositeindex/package-info.java
@@ -8,6 +8,7 @@
/**
* Core classes for handling composite indices.
- * @opensearch.experimental
+ *
+ * @opensearch.experimental
*/
package org.opensearch.index.compositeindex;
diff --git a/server/src/test/java/org/opensearch/index/codec/composite/datacube/startree/StarTreeDocValuesFormatTests.java b/server/src/test/java/org/opensearch/index/codec/composite/datacube/startree/StarTreeDocValuesFormatTests.java
index 049d91bc42d9c..b01d239a99671 100644
--- a/server/src/test/java/org/opensearch/index/codec/composite/datacube/startree/StarTreeDocValuesFormatTests.java
+++ b/server/src/test/java/org/opensearch/index/codec/composite/datacube/startree/StarTreeDocValuesFormatTests.java
@@ -23,6 +23,7 @@
import org.opensearch.cluster.ClusterModule;
import org.opensearch.cluster.metadata.IndexMetadata;
import org.opensearch.common.CheckedConsumer;
+import org.opensearch.common.Rounding;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.FeatureFlags;
import org.opensearch.common.xcontent.XContentFactory;
@@ -30,16 +31,27 @@
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.index.MapperTestUtils;
import org.opensearch.index.codec.composite.Composite99Codec;
+import org.opensearch.index.compositeindex.datacube.DateDimension;
+import org.opensearch.index.compositeindex.datacube.Dimension;
+import org.opensearch.index.compositeindex.datacube.Metric;
+import org.opensearch.index.compositeindex.datacube.MetricStat;
+import org.opensearch.index.compositeindex.datacube.NumericDimension;
+import org.opensearch.index.compositeindex.datacube.startree.StarTreeField;
+import org.opensearch.index.compositeindex.datacube.startree.StarTreeFieldConfiguration;
import org.opensearch.index.mapper.MapperService;
+import org.opensearch.index.mapper.StarTreeMapper;
import org.opensearch.indices.IndicesModule;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import static org.opensearch.common.util.FeatureFlags.STAR_TREE_INDEX;
+import static org.opensearch.test.OpenSearchTestCase.randomFrom;
/**
* Star tree doc values Lucene tests
@@ -68,7 +80,7 @@ protected Codec getCodec() {
final Logger testLogger = LogManager.getLogger(StarTreeDocValuesFormatTests.class);
try {
- createMapperService(getExpandedMapping("status", "size"));
+ createMapperService(getExpandedMapping());
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -76,6 +88,32 @@ protected Codec getCodec() {
return codec;
}
+ private StarTreeMapper.StarTreeFieldType getStarTreeFieldType() {
+ List m1 = new ArrayList<>();
+ m1.add(MetricStat.MAX);
+ Metric metric = new Metric("sndv", m1);
+ List d1CalendarIntervals = new ArrayList<>();
+ d1CalendarIntervals.add(Rounding.DateTimeUnit.HOUR_OF_DAY);
+ StarTreeField starTreeField = getStarTreeField(d1CalendarIntervals, metric);
+
+ return new StarTreeMapper.StarTreeFieldType("star_tree", starTreeField);
+ }
+
+ private static StarTreeField getStarTreeField(List d1CalendarIntervals, Metric metric1) {
+ DateDimension d1 = new DateDimension("field", d1CalendarIntervals);
+ NumericDimension d2 = new NumericDimension("dv");
+
+ List metrics = List.of(metric1);
+ List dims = List.of(d1, d2);
+ StarTreeFieldConfiguration config = new StarTreeFieldConfiguration(
+ 100,
+ Collections.emptySet(),
+ randomFrom(StarTreeFieldConfiguration.StarTreeBuildMode.ON_HEAP) // TODO : change it
+ );
+
+ return new StarTreeField("starTree", dims, metrics, config);
+ }
+
public void testStarTreeDocValues() throws IOException {
Directory directory = newDirectory();
IndexWriterConfig conf = newIndexWriterConfig(null);
@@ -106,7 +144,7 @@ public void testStarTreeDocValues() throws IOException {
directory.close();
}
- private XContentBuilder getExpandedMapping(String dim, String metric) throws IOException {
+ private XContentBuilder getExpandedMapping() throws IOException {
return topMapping(b -> {
b.startObject("composite");
b.startObject("startree");
diff --git a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/AbstractStarTreeBuilderTests.java b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/AbstractStarTreeBuilderTests.java
index 76a7875919a8b..7cc0f578c3fab 100644
--- a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/AbstractStarTreeBuilderTests.java
+++ b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/AbstractStarTreeBuilderTests.java
@@ -8,6 +8,7 @@
package org.opensearch.index.compositeindex.datacube.startree.builder;
+import org.apache.lucene.codecs.DocValuesConsumer;
import org.apache.lucene.codecs.DocValuesProducer;
import org.apache.lucene.codecs.lucene99.Lucene99Codec;
import org.apache.lucene.index.DocValues;
@@ -15,6 +16,7 @@
import org.apache.lucene.index.EmptyDocValuesProducer;
import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.FieldInfos;
+import org.apache.lucene.index.IndexFileNames;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.SegmentInfo;
import org.apache.lucene.index.SegmentWriteState;
@@ -24,11 +26,14 @@
import org.apache.lucene.sandbox.document.HalfFloatPoint;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.InfoStream;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.util.Version;
import org.opensearch.common.settings.Settings;
+import org.opensearch.index.codec.composite.Composite99DocValuesFormat;
import org.opensearch.index.codec.composite.datacube.startree.StarTreeValues;
+import org.opensearch.index.compositeindex.CompositeIndexConstants;
import org.opensearch.index.compositeindex.datacube.Dimension;
import org.opensearch.index.compositeindex.datacube.Metric;
import org.opensearch.index.compositeindex.datacube.MetricStat;
@@ -36,8 +41,9 @@
import org.opensearch.index.compositeindex.datacube.startree.StarTreeDocument;
import org.opensearch.index.compositeindex.datacube.startree.StarTreeField;
import org.opensearch.index.compositeindex.datacube.startree.StarTreeFieldConfiguration;
+import org.opensearch.index.compositeindex.datacube.startree.node.InMemoryTreeNode;
+import org.opensearch.index.compositeindex.datacube.startree.node.StarTreeNodeType;
import org.opensearch.index.compositeindex.datacube.startree.utils.SequentialDocValuesIterator;
-import org.opensearch.index.compositeindex.datacube.startree.utils.TreeNode;
import org.opensearch.index.mapper.ContentPath;
import org.opensearch.index.mapper.DocumentMapper;
import org.opensearch.index.mapper.Mapper;
@@ -61,8 +67,8 @@
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
-import static org.opensearch.index.compositeindex.datacube.startree.builder.BaseStarTreeBuilder.NUM_SEGMENT_DOCS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -76,11 +82,15 @@ public abstract class AbstractStarTreeBuilderTests extends OpenSearchTestCase {
protected StarTreeField compositeField;
protected Map fieldProducerMap;
protected SegmentWriteState writeState;
- private BaseStarTreeBuilder builder;
+ protected BaseStarTreeBuilder builder;
+ protected IndexOutput dataOut;
+ protected IndexOutput metaOut;
+ protected DocValuesConsumer docValuesConsumer;
@Before
public void setup() throws IOException {
fields = List.of("field1", "field2", "field3", "field4", "field5", "field6", "field7", "field8", "field9", "field10");
+ docValuesConsumer = mock(DocValuesConsumer.class);
dimensionsOrder = List.of(
new NumericDimension("field1"),
@@ -130,6 +140,20 @@ public void setup() throws IOException {
}
writeState = getWriteState(5);
+ String dataFileName = IndexFileNames.segmentFileName(
+ writeState.segmentInfo.name,
+ writeState.segmentSuffix,
+ Composite99DocValuesFormat.DATA_EXTENSION
+ );
+ dataOut = writeState.directory.createOutput(dataFileName, writeState.context);
+
+ String metaFileName = IndexFileNames.segmentFileName(
+ writeState.segmentInfo.name,
+ writeState.segmentSuffix,
+ Composite99DocValuesFormat.META_EXTENSION
+ );
+ metaOut = writeState.directory.createOutput(metaFileName, writeState.context);
+
mapperService = mock(MapperService.class);
DocumentMapper documentMapper = mock(DocumentMapper.class);
when(mapperService.documentMapper()).thenReturn(documentMapper);
@@ -665,7 +689,7 @@ public void test_build_halfFloatMetrics() throws IOException {
dimsIterators,
metricsIterators
);
- builder.build(segmentStarTreeDocumentIterator);
+ builder.build(segmentStarTreeDocumentIterator, new AtomicInteger(), docValuesConsumer);
List resultStarTreeDocuments = builder.getStarTreeDocuments();
assertEquals(7, resultStarTreeDocuments.size());
@@ -718,7 +742,7 @@ public void test_build_floatMetrics() throws IOException {
dimsIterators,
metricsIterators
);
- builder.build(segmentStarTreeDocumentIterator);
+ builder.build(segmentStarTreeDocumentIterator, new AtomicInteger(), docValuesConsumer);
List resultStarTreeDocuments = builder.getStarTreeDocuments();
assertEquals(7, resultStarTreeDocuments.size());
@@ -772,7 +796,7 @@ public void test_build_longMetrics() throws IOException {
dimsIterators,
metricsIterators
);
- builder.build(segmentStarTreeDocumentIterator);
+ builder.build(segmentStarTreeDocumentIterator, new AtomicInteger(), docValuesConsumer);
List resultStarTreeDocuments = builder.getStarTreeDocuments();
assertEquals(7, resultStarTreeDocuments.size());
@@ -821,7 +845,7 @@ public void test_build() throws IOException {
dimsIterators,
metricsIterators
);
- builder.build(segmentStarTreeDocumentIterator);
+ builder.build(segmentStarTreeDocumentIterator, new AtomicInteger(), docValuesConsumer);
List resultStarTreeDocuments = builder.getStarTreeDocuments();
assertEquals(7, resultStarTreeDocuments.size());
@@ -944,13 +968,13 @@ public void test_build_starTreeDataset() throws IOException {
dimsIterators,
metricsIterators
);
- builder.build(segmentStarTreeDocumentIterator);
+ builder.build(segmentStarTreeDocumentIterator, new AtomicInteger(), docValuesConsumer);
List resultStarTreeDocuments = builder.getStarTreeDocuments();
Iterator expectedStarTreeDocumentIterator = expectedStarTreeDocuments();
Iterator resultStarTreeDocumentIterator = resultStarTreeDocuments.iterator();
Map> dimValueToDocIdMap = new HashMap<>();
- builder.rootNode.isStarNode = true;
+ builder.rootNode.nodeType = StarTreeNodeType.STAR.getValue();
traverseStarTree(builder.rootNode, dimValueToDocIdMap, true);
Map> expectedDimToValueMap = getExpectedDimToValueMap();
@@ -1055,7 +1079,7 @@ public void testFlushFlow() throws IOException {
SortedNumericDocValues m1sndv = getSortedNumericMock(metricsList, metricsWithField);
SortedNumericDocValues m2sndv = getSortedNumericMock(metricsList, metricsWithField);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(6), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(6), mapperService);
SequentialDocValuesIterator[] dimDvs = { new SequentialDocValuesIterator(d1sndv), new SequentialDocValuesIterator(d2sndv) };
Iterator starTreeDocumentIterator = builder.sortAndAggregateSegmentDocuments(
dimDvs,
@@ -1126,7 +1150,7 @@ public void testFlushFlowBuild() throws IOException {
DocValuesProducer d2vp = getDocValuesProducer(d2sndv);
DocValuesProducer m1vp = getDocValuesProducer(m1sndv);
Map fieldProducerMap = Map.of("field1", d1vp, "field3", d2vp, "field2", m1vp);
- builder.build(fieldProducerMap);
+ builder.build(fieldProducerMap, new AtomicInteger(), docValuesConsumer);
/**
* Asserting following dim / metrics [ dim1, dim2 / Sum [ metric] ]
[0, 0] | [0.0]
@@ -1209,7 +1233,7 @@ public void testMergeFlowWithSum() throws IOException {
sf,
"6"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(6), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(6), mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Sum [ metric] ]
@@ -1259,7 +1283,7 @@ public void testMergeFlowWithCount() throws IOException {
sf,
"6"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(6), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(6), mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Count [ metric] ]
@@ -1298,7 +1322,7 @@ private StarTreeValues getStarTreeValues(
null,
dimDocIdSetIterators,
metricDocIdSetIterators,
- Map.of("numSegmentDocs", number)
+ Map.of(CompositeIndexConstants.SEGMENT_DOCS_COUNT, number)
);
return starTreeValues;
}
@@ -1336,7 +1360,7 @@ public void testMergeFlowWithDifferentDocsFromSegments() throws IOException {
sf,
"4"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(4), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(4), mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Count [ metric] ]
@@ -1396,7 +1420,7 @@ public void testMergeFlowWithMissingDocs() throws IOException {
sf,
"4"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(4), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(4), mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Count [ metric] ]
@@ -1456,7 +1480,7 @@ public void testMergeFlowWithMissingDocsInSecondDim() throws IOException {
sf,
"4"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(4), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(4), mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Count [ metric] ]
@@ -1517,7 +1541,7 @@ public void testMergeFlowWithDocsMissingAtTheEnd() throws IOException {
sf,
"4"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, writeState, mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, writeState, mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Count [ metric] ]
@@ -1569,7 +1593,7 @@ public void testMergeFlowWithEmptyFieldsInOneSegment() throws IOException {
sf,
"0"
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, getWriteState(0), mapperService);
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, getWriteState(0), mapperService);
Iterator starTreeDocumentIterator = builder.mergeStarTrees(List.of(starTreeValues, starTreeValues2));
/**
* Asserting following dim / metrics [ dim1, dim2 / Count [ metric] ]
@@ -1664,8 +1688,8 @@ public void testMergeFlowWithDuplicateDimensionValues() throws IOException {
metricsWithField,
sf
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, writeState, mapperService);
- builder.build(List.of(starTreeValues, starTreeValues2));
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, writeState, mapperService);
+ builder.build(List.of(starTreeValues, starTreeValues2), new AtomicInteger(), docValuesConsumer);
List starTreeDocuments = builder.getStarTreeDocuments();
assertEquals(401, starTreeDocuments.size());
int count = 0;
@@ -1774,8 +1798,8 @@ public void testMergeFlowWithMaxLeafDocs() throws IOException {
sf
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, writeState, mapperService);
- builder.build(List.of(starTreeValues, starTreeValues2));
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, writeState, mapperService);
+ builder.build(List.of(starTreeValues, starTreeValues2), new AtomicInteger(), docValuesConsumer);
List starTreeDocuments = builder.getStarTreeDocuments();
/**
635 docs get generated
@@ -1892,8 +1916,8 @@ public void testMergeFlowWithDuplicateDimensionValueWithMaxLeafDocs() throws IOE
metricsWithField,
sf
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, writeState, mapperService);
- builder.build(List.of(starTreeValues, starTreeValues2));
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, writeState, mapperService);
+ builder.build(List.of(starTreeValues, starTreeValues2), new AtomicInteger(), docValuesConsumer);
List starTreeDocuments = builder.getStarTreeDocuments();
assertEquals(401, starTreeDocuments.size());
builder.close();
@@ -1991,8 +2015,8 @@ public void testMergeFlowWithMaxLeafDocsAndStarTreeNodesAssertion() throws IOExc
metricsWithField,
sf
);
- OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(sf, writeState, mapperService);
- builder.build(List.of(starTreeValues, starTreeValues2));
+ OnHeapStarTreeBuilder builder = new OnHeapStarTreeBuilder(metaOut, dataOut, sf, writeState, mapperService);
+ builder.build(List.of(starTreeValues, starTreeValues2), new AtomicInteger(), docValuesConsumer);
List starTreeDocuments = builder.getStarTreeDocuments();
Map> dimValueToDocIdMap = new HashMap<>();
traverseStarTree(builder.rootNode, dimValueToDocIdMap, true);
@@ -2027,13 +2051,13 @@ private static StarTreeField getStarTreeField(int maxLeafDocs) {
return sf;
}
- private void traverseStarTree(TreeNode root, Map> dimValueToDocIdMap, boolean traverStarNodes) {
- TreeNode starTree = root;
+ private void traverseStarTree(InMemoryTreeNode root, Map> dimValueToDocIdMap, boolean traverStarNodes) {
+ InMemoryTreeNode starTree = root;
// Use BFS to traverse the star tree
- Queue queue = new ArrayDeque<>();
+ Queue queue = new ArrayDeque<>();
queue.add(starTree);
int currentDimensionId = -1;
- TreeNode starTreeNode;
+ InMemoryTreeNode starTreeNode;
List docIds = new ArrayList<>();
while ((starTreeNode = queue.poll()) != null) {
int dimensionId = starTreeNode.dimensionId;
@@ -2044,17 +2068,17 @@ private void traverseStarTree(TreeNode root, Map> di
// store aggregated document of the node
int docId = starTreeNode.aggregatedDocId;
Map map = dimValueToDocIdMap.getOrDefault(dimensionId, new HashMap<>());
- if (starTreeNode.isStarNode) {
+ if (starTreeNode.nodeType == StarTreeNodeType.STAR.getValue()) {
map.put(Long.MAX_VALUE, docId);
} else {
map.put(starTreeNode.dimensionValue, docId);
}
dimValueToDocIdMap.put(dimensionId, map);
- if (starTreeNode.children != null && (!traverStarNodes || starTreeNode.isStarNode)) {
- Iterator childrenIterator = starTreeNode.children.values().iterator();
+ if (starTreeNode.children != null && (!traverStarNodes || (starTreeNode.nodeType == StarTreeNodeType.STAR.getValue()))) {
+ Iterator childrenIterator = starTreeNode.children.values().iterator();
while (childrenIterator.hasNext()) {
- TreeNode childNode = childrenIterator.next();
+ InMemoryTreeNode childNode = childrenIterator.next();
queue.add(childNode);
}
}
@@ -2171,7 +2195,7 @@ public void testMergeFlow() throws IOException {
}
Map getAttributes(int numSegmentDocs) {
- return Map.of(String.valueOf(NUM_SEGMENT_DOCS), String.valueOf(numSegmentDocs));
+ return Map.of(CompositeIndexConstants.SEGMENT_DOCS_COUNT, String.valueOf(numSegmentDocs));
}
private static StarTreeField getStarTreeField(MetricStat count) {
@@ -2246,6 +2270,8 @@ public void tearDown() throws Exception {
if (builder != null) {
builder.close();
}
+ metaOut.close();
+ dataOut.close();
directory.close();
}
}
diff --git a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/BaseStarTreeBuilderTests.java b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/BaseStarTreeBuilderTests.java
index 51ebc02ea8243..42f195f41b1af 100644
--- a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/BaseStarTreeBuilderTests.java
+++ b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/BaseStarTreeBuilderTests.java
@@ -8,20 +8,24 @@
package org.opensearch.index.compositeindex.datacube.startree.builder;
+import org.apache.lucene.codecs.DocValuesConsumer;
import org.apache.lucene.codecs.DocValuesProducer;
import org.apache.lucene.codecs.lucene99.Lucene99Codec;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.FieldInfos;
+import org.apache.lucene.index.IndexFileNames;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.SegmentInfo;
import org.apache.lucene.index.SegmentWriteState;
import org.apache.lucene.index.VectorEncoding;
import org.apache.lucene.index.VectorSimilarityFunction;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.InfoStream;
import org.apache.lucene.util.Version;
import org.opensearch.common.settings.Settings;
+import org.opensearch.index.codec.composite.Composite99DocValuesFormat;
import org.opensearch.index.codec.composite.datacube.startree.StarTreeValues;
import org.opensearch.index.compositeindex.datacube.Dimension;
import org.opensearch.index.compositeindex.datacube.Metric;
@@ -51,6 +55,7 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -75,9 +80,12 @@ public class BaseStarTreeBuilderTests extends OpenSearchTestCase {
private static List metrics;
private static Directory directory;
private static FieldInfo[] fieldsInfo;
- private static SegmentWriteState state;
+ private static SegmentWriteState writeState;
private static StarTreeField starTreeField;
+ private static IndexOutput dataOut;
+ private static IndexOutput metaOut;
+
@BeforeClass
public static void setup() throws IOException {
@@ -138,7 +146,21 @@ public static void setup() throws IOException {
fieldProducerMap.put(fields.get(i), docValuesProducer);
}
FieldInfos fieldInfos = new FieldInfos(fieldsInfo);
- state = new SegmentWriteState(InfoStream.getDefault(), segmentInfo.dir, segmentInfo, fieldInfos, null, newIOContext(random()));
+ writeState = new SegmentWriteState(InfoStream.getDefault(), segmentInfo.dir, segmentInfo, fieldInfos, null, newIOContext(random()));
+
+ String dataFileName = IndexFileNames.segmentFileName(
+ writeState.segmentInfo.name,
+ writeState.segmentSuffix,
+ Composite99DocValuesFormat.DATA_EXTENSION
+ );
+ dataOut = writeState.directory.createOutput(dataFileName, writeState.context);
+
+ String metaFileName = IndexFileNames.segmentFileName(
+ writeState.segmentInfo.name,
+ writeState.segmentSuffix,
+ Composite99DocValuesFormat.META_EXTENSION
+ );
+ metaOut = writeState.directory.createOutput(metaFileName, writeState.context);
mapperService = mock(MapperService.class);
DocumentMapper documentMapper = mock(DocumentMapper.class);
@@ -157,9 +179,13 @@ public static void setup() throws IOException {
);
when(documentMapper.mappers()).thenReturn(fieldMappers);
- builder = new BaseStarTreeBuilder(starTreeField, state, mapperService) {
+ builder = new BaseStarTreeBuilder(metaOut, dataOut, starTreeField, writeState, mapperService) {
@Override
- public void build(List starTreeValuesSubs) throws IOException {}
+ public void build(
+ List starTreeValuesSubs,
+ AtomicInteger fieldNumberAcrossStarTrees,
+ DocValuesConsumer starTreeDocValuesConsumer
+ ) throws IOException {}
@Override
public void appendStarTreeDocument(StarTreeDocument starTreeDocument) throws IOException {}
@@ -169,6 +195,11 @@ public StarTreeDocument getStarTreeDocument(int docId) throws IOException {
return null;
}
+ @Override
+ public StarTreeDocument getStarTreeDocumentForCreatingDocValues(int docId) throws IOException {
+ return null;
+ }
+
@Override
public List getStarTreeDocuments() {
return List.of();
@@ -224,6 +255,8 @@ public void test_reduceStarTreeDocuments() {
@Override
public void tearDown() throws Exception {
super.tearDown();
+ dataOut.close();
+ metaOut.close();
directory.close();
}
}
diff --git a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/OnHeapStarTreeBuilderTests.java b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/OnHeapStarTreeBuilderTests.java
index aed08b7727be7..315f8cb94b6e5 100644
--- a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/OnHeapStarTreeBuilderTests.java
+++ b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/OnHeapStarTreeBuilderTests.java
@@ -12,13 +12,16 @@
import org.opensearch.index.compositeindex.datacube.startree.StarTreeField;
import org.opensearch.index.mapper.MapperService;
+import java.io.IOException;
+
public class OnHeapStarTreeBuilderTests extends AbstractStarTreeBuilderTests {
+
@Override
public BaseStarTreeBuilder getStarTreeBuilder(
StarTreeField starTreeField,
SegmentWriteState segmentWriteState,
MapperService mapperService
- ) {
- return new OnHeapStarTreeBuilder(starTreeField, segmentWriteState, mapperService);
+ ) throws IOException {
+ return new OnHeapStarTreeBuilder(metaOut, dataOut, starTreeField, segmentWriteState, mapperService);
}
}
diff --git a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/StarTreesBuilderTests.java b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/StarTreesBuilderTests.java
index 564ab110fa7a5..455cd84bb44f8 100644
--- a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/StarTreesBuilderTests.java
+++ b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/builder/StarTreesBuilderTests.java
@@ -8,6 +8,7 @@
package org.opensearch.index.compositeindex.datacube.startree.builder;
+import org.apache.lucene.codecs.DocValuesConsumer;
import org.apache.lucene.codecs.DocValuesProducer;
import org.apache.lucene.codecs.lucene99.Lucene99Codec;
import org.apache.lucene.index.FieldInfo;
@@ -15,6 +16,7 @@
import org.apache.lucene.index.SegmentInfo;
import org.apache.lucene.index.SegmentWriteState;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.util.InfoStream;
import org.apache.lucene.util.Version;
import org.opensearch.index.compositeindex.datacube.startree.StarTreeField;
@@ -45,9 +47,13 @@ public class StarTreesBuilderTests extends OpenSearchTestCase {
private StarTreeField starTreeField;
private Map fieldProducerMap;
private Directory directory;
+ private IndexOutput dataOut;
+ private IndexOutput metaOut;
public void setUp() throws Exception {
super.setUp();
+ metaOut = mock(IndexOutput.class);
+ dataOut = mock(IndexOutput.class);
mapperService = mock(MapperService.class);
directory = newFSDirectory(createTempDir());
SegmentInfo segmentInfo = new SegmentInfo(
@@ -89,7 +95,7 @@ public void test_buildWithNoStarTreeFields() throws IOException {
when(mapperService.getCompositeFieldTypes()).thenReturn(new HashSet<>());
StarTreesBuilder starTreesBuilder = new StarTreesBuilder(segmentWriteState, mapperService);
- starTreesBuilder.build(fieldProducerMap);
+ starTreesBuilder.build(metaOut, dataOut, fieldProducerMap, mock(DocValuesConsumer.class));
verifyNoInteractions(docValuesProducer);
}
@@ -97,7 +103,7 @@ public void test_buildWithNoStarTreeFields() throws IOException {
public void test_getStarTreeBuilder() throws IOException {
when(mapperService.getCompositeFieldTypes()).thenReturn(Set.of(starTreeFieldType));
StarTreesBuilder starTreesBuilder = new StarTreesBuilder(segmentWriteState, mapperService);
- StarTreeBuilder starTreeBuilder = starTreesBuilder.getSingleTreeBuilder(starTreeField, segmentWriteState, mapperService);
+ StarTreeBuilder starTreeBuilder = starTreesBuilder.getSingleTreeBuilder(metaOut, dataOut, starTreeField, segmentWriteState, mapperService);
assertTrue(starTreeBuilder instanceof OnHeapStarTreeBuilder);
}
@@ -106,7 +112,7 @@ public void test_getStarTreeBuilder_illegalArgument() {
StarTreeFieldConfiguration starTreeFieldConfiguration = new StarTreeFieldConfiguration(1, new HashSet<>(), StarTreeFieldConfiguration.StarTreeBuildMode.OFF_HEAP);
StarTreeField starTreeField = new StarTreeField("star_tree", new ArrayList<>(), new ArrayList<>(), starTreeFieldConfiguration);
StarTreesBuilder starTreesBuilder = new StarTreesBuilder(segmentWriteState, mapperService);
- assertThrows(IllegalArgumentException.class, () -> starTreesBuilder.getSingleTreeBuilder(starTreeField, segmentWriteState, mapperService));
+ assertThrows(IllegalArgumentException.class, () -> starTreesBuilder.getSingleTreeBuilder(metaOut, dataOut, starTreeField, segmentWriteState, mapperService));
}
public void test_closeWithNoStarTreeFields() throws IOException {
diff --git a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/meta/StarTreeMetaTests.java b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/meta/StarTreeMetaTests.java
new file mode 100644
index 0000000000000..36e37e452b3f1
--- /dev/null
+++ b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/meta/StarTreeMetaTests.java
@@ -0,0 +1,210 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.index.compositeindex.datacube.startree.meta;
+
+import org.apache.lucene.codecs.lucene99.Lucene99Codec;
+import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.FieldInfos;
+import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.index.SegmentInfo;
+import org.apache.lucene.index.SegmentWriteState;
+import org.apache.lucene.index.VectorEncoding;
+import org.apache.lucene.index.VectorSimilarityFunction;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.util.InfoStream;
+import org.apache.lucene.util.Version;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.meta.MetricEntry;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.meta.StarTreeMetadata;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.writer.StarTreeWriter;
+import org.opensearch.index.compositeindex.datacube.Dimension;
+import org.opensearch.index.compositeindex.datacube.Metric;
+import org.opensearch.index.compositeindex.datacube.MetricStat;
+import org.opensearch.index.compositeindex.datacube.NumericDimension;
+import org.opensearch.index.compositeindex.datacube.startree.StarTreeField;
+import org.opensearch.index.compositeindex.datacube.startree.StarTreeFieldConfiguration;
+import org.opensearch.index.compositeindex.datacube.startree.aggregators.MetricAggregatorInfo;
+import org.opensearch.index.fielddata.IndexNumericFieldData;
+import org.opensearch.index.mapper.CompositeMappedFieldType;
+import org.opensearch.test.OpenSearchTestCase;
+import org.junit.Before;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+
+import static org.opensearch.index.compositeindex.CompositeIndexConstants.COMPOSITE_FIELD_MARKER;
+import static org.opensearch.index.compositeindex.CompositeIndexConstants.VERSION;
+import static org.opensearch.index.mapper.CompositeMappedFieldType.CompositeFieldType.STAR_TREE;
+
+public class StarTreeMetaTests extends OpenSearchTestCase {
+
+ private IndexOutput metaOut;
+ private IndexInput metaIn;
+ private StarTreeField starTreeField;
+ private SegmentWriteState writeState;
+ private Directory directory;
+ private FieldInfo[] fieldsInfo;
+ private List dimensionsOrder;
+ private List fields = List.of();
+ private List metrics;
+ private List metricAggregatorInfos = new ArrayList<>();
+ private int segmentDocumentCount;
+ private long dataFilePointer;
+ private long dataFileLength;
+
+ @Before
+ public void setup() throws IOException {
+ fields = List.of("field1", "field2", "field3", "field4", "field5", "field6", "field7", "field8", "field9", "field10");
+ directory = newFSDirectory(createTempDir());
+ SegmentInfo segmentInfo = new SegmentInfo(
+ directory,
+ Version.LATEST,
+ Version.LUCENE_9_11_0,
+ "test_segment",
+ 6,
+ false,
+ false,
+ new Lucene99Codec(),
+ new HashMap<>(),
+ UUID.randomUUID().toString().substring(0, 16).getBytes(StandardCharsets.UTF_8),
+ new HashMap<>(),
+ null
+ );
+
+ fieldsInfo = new FieldInfo[fields.size()];
+ for (int i = 0; i < fieldsInfo.length; i++) {
+ fieldsInfo[i] = new FieldInfo(
+ fields.get(i),
+ i,
+ false,
+ false,
+ true,
+ IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS,
+ DocValuesType.SORTED_NUMERIC,
+ -1,
+ Collections.emptyMap(),
+ 0,
+ 0,
+ 0,
+ 0,
+ VectorEncoding.FLOAT32,
+ VectorSimilarityFunction.EUCLIDEAN,
+ false,
+ false
+ );
+ }
+ FieldInfos fieldInfos = new FieldInfos(fieldsInfo);
+ writeState = new SegmentWriteState(InfoStream.getDefault(), segmentInfo.dir, segmentInfo, fieldInfos, null, newIOContext(random()));
+ }
+
+ public void test_starTreeMetadata() throws IOException {
+ dimensionsOrder = List.of(
+ new NumericDimension("field1"),
+ new NumericDimension("field3"),
+ new NumericDimension("field5"),
+ new NumericDimension("field8")
+ );
+ metrics = List.of(
+ new Metric("field2", List.of(MetricStat.SUM)),
+ new Metric("field4", List.of(MetricStat.SUM)),
+ new Metric("field6", List.of(MetricStat.COUNT))
+ );
+ int maxLeafDocs = randomNonNegativeInt();
+ StarTreeFieldConfiguration starTreeFieldConfiguration = new StarTreeFieldConfiguration(
+ maxLeafDocs,
+ new HashSet<>(),
+ StarTreeFieldConfiguration.StarTreeBuildMode.ON_HEAP
+ );
+ starTreeField = new StarTreeField("star_tree", dimensionsOrder, metrics, starTreeFieldConfiguration);
+
+ for (Metric metric : metrics) {
+ for (MetricStat metricType : metric.getMetrics()) {
+ MetricAggregatorInfo metricAggregatorInfo = new MetricAggregatorInfo(
+ metricType,
+ metric.getField(),
+ starTreeField.getName(),
+ IndexNumericFieldData.NumericType.DOUBLE
+ );
+ metricAggregatorInfos.add(metricAggregatorInfo);
+ }
+ }
+
+ dataFileLength = randomNonNegativeLong();
+ dataFilePointer = randomNonNegativeLong();
+ segmentDocumentCount = randomNonNegativeInt();
+ metaOut = directory.createOutput("star-tree-metadata", IOContext.DEFAULT);
+ StarTreeWriter.writeStarTreeMetadata(
+ metaOut,
+ starTreeField,
+ writeState,
+ metricAggregatorInfos,
+ segmentDocumentCount,
+ dataFilePointer,
+ dataFileLength
+ );
+ metaOut.close();
+ metaIn = directory.openInput("star-tree-metadata", IOContext.READONCE);
+ assertEquals(COMPOSITE_FIELD_MARKER, metaIn.readLong());
+ assertEquals(VERSION, metaIn.readVInt());
+
+ String compositeFieldName = metaIn.readString();
+ CompositeMappedFieldType.CompositeFieldType compositeFieldType = CompositeMappedFieldType.CompositeFieldType.fromName(
+ metaIn.readString()
+ );
+
+ StarTreeMetadata starTreeMetadata = new StarTreeMetadata(metaIn, compositeFieldName, compositeFieldType);
+ assertEquals(starTreeField.getName(), starTreeMetadata.getCompositeFieldName());
+ assertEquals(STAR_TREE, starTreeMetadata.getCompositeFieldType());
+
+ assertNotNull(starTreeMetadata);
+
+ for (int i = 0; i < dimensionsOrder.size(); i++) {
+ assertEquals(dimensionsOrder.get(i).getField(), starTreeMetadata.getDimensionFields().get(i));
+ }
+
+ for (int i = 0; i < metricAggregatorInfos.size(); i++) {
+ MetricEntry metricEntry = starTreeMetadata.getMetricEntries().get(i);
+ assertEquals(metricAggregatorInfos.get(i).getField(), metricEntry.getMetricFieldName());
+ assertEquals(metricAggregatorInfos.get(i).getMetricStat(), metricEntry.getMetricStat());
+ }
+ assertEquals(segmentDocumentCount, starTreeMetadata.getSegmentAggregatedDocCount(), 0);
+ assertEquals(maxLeafDocs, starTreeMetadata.getMaxLeafDocs(), 0);
+ assertEquals(
+ starTreeFieldConfiguration.getSkipStarNodeCreationInDims().size(),
+ starTreeMetadata.getSkipStarNodeCreationInDims().size()
+ );
+ for (String skipStarNodeCreationInDims : starTreeField.getStarTreeConfig().getSkipStarNodeCreationInDims()) {
+ assertTrue(starTreeMetadata.getSkipStarNodeCreationInDims().contains(skipStarNodeCreationInDims));
+ }
+ assertEquals(starTreeFieldConfiguration.getBuildMode(), starTreeMetadata.getStarTreeBuildMode());
+ assertEquals(dataFileLength, starTreeMetadata.getDataLength());
+ assertEquals(dataFilePointer, starTreeMetadata.getDataStartFilePointer());
+
+ metaIn.close();
+
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ metaOut.close();
+ metaIn.close();
+ directory.close();
+ }
+
+}
diff --git a/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/node/FixedLengthStarTreeNodeTests.java b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/node/FixedLengthStarTreeNodeTests.java
new file mode 100644
index 0000000000000..5d5f59b62a3b7
--- /dev/null
+++ b/server/src/test/java/org/opensearch/index/compositeindex/datacube/startree/node/FixedLengthStarTreeNodeTests.java
@@ -0,0 +1,207 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.index.compositeindex.datacube.startree.node;
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.meta.StarTreeMetadata;
+import org.opensearch.index.codec.composite.datacube.startree.fileformats.writer.StarTreeWriter;
+import org.opensearch.test.OpenSearchTestCase;
+import org.junit.Before;
+
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Queue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FixedLengthStarTreeNodeTests extends OpenSearchTestCase {
+
+ private IndexOutput dataOut;
+ private IndexInput dataIn;
+ private Directory directory;
+
+ @Before
+ public void setup() throws IOException {
+ directory = newFSDirectory(createTempDir());
+ }
+
+ public void test_StarTreeNode() throws IOException {
+
+ dataOut = directory.createOutput("star-tree-data", IOContext.DEFAULT);
+ Map levelOrderStarTreeNodeMap = new LinkedHashMap<>();
+ InMemoryTreeNode root = generateSampleTree(levelOrderStarTreeNodeMap);
+ long starTreeDataLength = StarTreeWriter.writeStarTree(dataOut, root, 7, "star-tree");
+
+ // asserting on the actual length of the star tree data file
+ assertEquals(starTreeDataLength, 247);
+ dataOut.close();
+
+ dataIn = directory.openInput("star-tree-data", IOContext.READONCE);
+
+ StarTreeMetadata starTreeMetadata = mock(StarTreeMetadata.class);
+ when(starTreeMetadata.getDataLength()).thenReturn(starTreeDataLength);
+ when(starTreeMetadata.getDataStartFilePointer()).thenReturn(0L);
+ StarTree starTree = new StarTree(dataIn, starTreeMetadata);
+
+ StarTreeNode starTreeNode = starTree.getRoot();
+ Queue queue = new ArrayDeque<>();
+ queue.add(starTreeNode);
+
+ while ((starTreeNode = queue.poll()) != null) {
+
+ // verify the star node
+ assertStarTreeNode(starTreeNode, levelOrderStarTreeNodeMap.get(starTreeNode.getDimensionValue()));
+
+ Iterator extends StarTreeNode> childrenIterator = starTreeNode.getChildrenIterator();
+
+ if (starTreeNode.getChildDimensionId() != -1) {
+ while (childrenIterator.hasNext()) {
+ StarTreeNode child = childrenIterator.next();
+ assertStarTreeNode(
+ starTreeNode.getChildForDimensionValue(child.getDimensionValue(), false),
+ levelOrderStarTreeNodeMap.get(child.getDimensionValue())
+ );
+ queue.add(child);
+ }
+ }
+ }
+
+ dataIn.close();
+
+ }
+
+ private void assertStarTreeNode(StarTreeNode starTreeNode, InMemoryTreeNode treeNode) throws IOException {
+ assertEquals(starTreeNode.getDimensionId(), treeNode.dimensionId);
+ assertEquals(starTreeNode.getDimensionValue(), treeNode.dimensionValue);
+ assertEquals(starTreeNode.getStartDocId(), treeNode.startDocId);
+ assertEquals(starTreeNode.getEndDocId(), treeNode.endDocId);
+ assertEquals(starTreeNode.getChildDimensionId(), treeNode.childDimensionId);
+ assertEquals(starTreeNode.getAggregatedDocId(), treeNode.aggregatedDocId);
+
+ if (starTreeNode.getChildDimensionId() != -1) {
+ assertFalse(starTreeNode.isLeaf());
+ if (treeNode.children != null) {
+ assertEquals(starTreeNode.getNumChildren(), treeNode.children.values().size());
+ }
+ } else {
+ assertTrue(starTreeNode.isLeaf());
+ }
+
+ }
+
+ private InMemoryTreeNode generateSampleTree(Map levelOrderStarTreeNode) {
+ // Create the root node
+ InMemoryTreeNode root = new InMemoryTreeNode();
+ root.dimensionId = 0;
+ root.startDocId = 0;
+ root.endDocId = 100;
+ root.childDimensionId = 1;
+ root.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ root.children = new HashMap<>();
+
+ levelOrderStarTreeNode.put(root.dimensionValue, root);
+
+ // Create child nodes for dimension 1
+ InMemoryTreeNode dim1Node1 = new InMemoryTreeNode();
+ dim1Node1.dimensionId = 1;
+ dim1Node1.dimensionValue = 1;
+ dim1Node1.startDocId = 0;
+ dim1Node1.endDocId = 50;
+ dim1Node1.childDimensionId = 2;
+ dim1Node1.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ dim1Node1.children = new HashMap<>();
+
+ InMemoryTreeNode dim1Node2 = new InMemoryTreeNode();
+ dim1Node2.dimensionId = 1;
+ dim1Node2.dimensionValue = 2;
+ dim1Node2.startDocId = 50;
+ dim1Node2.endDocId = 100;
+ dim1Node2.childDimensionId = 2;
+ dim1Node2.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ dim1Node2.children = new HashMap<>();
+
+ root.children.put(1L, dim1Node1);
+ root.children.put(2L, dim1Node2);
+
+ levelOrderStarTreeNode.put(dim1Node1.dimensionValue, dim1Node1);
+ levelOrderStarTreeNode.put(dim1Node2.dimensionValue, dim1Node2);
+
+ // Create child nodes for dimension 2
+ InMemoryTreeNode dim2Node1 = new InMemoryTreeNode();
+ dim2Node1.dimensionId = 2;
+ dim2Node1.dimensionValue = 3;
+ dim2Node1.startDocId = 0;
+ dim2Node1.endDocId = 25;
+ dim2Node1.childDimensionId = -1;
+ dim2Node1.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ dim2Node1.children = null;
+
+ InMemoryTreeNode dim2Node2 = new InMemoryTreeNode();
+ dim2Node2.dimensionId = 2;
+ dim2Node2.dimensionValue = 4;
+ dim2Node2.startDocId = 25;
+ dim2Node2.endDocId = 50;
+ dim2Node2.childDimensionId = -1;
+ dim2Node2.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ dim2Node2.children = null;
+
+ InMemoryTreeNode dim2Node3 = new InMemoryTreeNode();
+ dim2Node3.dimensionId = 2;
+ dim2Node3.dimensionValue = 5;
+ dim2Node3.startDocId = 50;
+ dim2Node3.endDocId = 75;
+ dim2Node3.childDimensionId = -1;
+ dim2Node3.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ dim2Node3.children = null;
+
+ InMemoryTreeNode dim2Node4 = new InMemoryTreeNode();
+ dim2Node4.dimensionId = 2;
+ dim2Node4.dimensionValue = 6;
+ dim2Node4.startDocId = 75;
+ dim2Node4.endDocId = 100;
+ dim2Node4.childDimensionId = -1;
+ dim2Node4.aggregatedDocId = randomInt();
+ root.nodeType = (byte) 0;
+ dim2Node4.children = null;
+
+ dim1Node1.children.put(3L, dim2Node1);
+ dim1Node1.children.put(4L, dim2Node2);
+ dim1Node2.children.put(5L, dim2Node3);
+ dim1Node2.children.put(6L, dim2Node4);
+
+ levelOrderStarTreeNode.put(dim2Node1.dimensionValue, dim2Node1);
+ levelOrderStarTreeNode.put(dim2Node2.dimensionValue, dim2Node2);
+ levelOrderStarTreeNode.put(dim2Node3.dimensionValue, dim2Node3);
+ levelOrderStarTreeNode.put(dim2Node4.dimensionValue, dim2Node4);
+
+ return root;
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ dataIn.close();
+ dataOut.close();
+ directory.close();
+ }
+
+}
diff --git a/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java b/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java
index 6afc7c23d9e66..e07d7ef3c5a7b 100644
--- a/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java
+++ b/test/framework/src/main/java/org/opensearch/test/OpenSearchTestCase.java
@@ -815,6 +815,14 @@ public static long randomNonNegativeLong() {
return randomLong == Long.MIN_VALUE ? 0 : Math.abs(randomLong);
}
+ /**
+ * @return a int
between 0
and Integer.MAX_VALUE
(inclusive) chosen uniformly at random.
+ */
+ public static int randomNonNegativeInt() {
+ int randomInt = randomInt();
+ return randomInt == Integer.MIN_VALUE ? 0 : Math.abs(randomInt);
+ }
+
public static float randomFloat() {
return random().nextFloat();
}