From 3d726d4a4d0c8087612118567b4b0177dbb4f30d Mon Sep 17 00:00:00 2001 From: Young Xu Date: Sun, 18 Aug 2024 19:07:44 +0800 Subject: [PATCH] feat: refactor measurement manage api Signed-off-by: Young Xu --- opengemini/client.go | 28 ++- opengemini/error.go | 18 +- opengemini/measurement.go | 65 ++++++- opengemini/measurement_builder.go | 307 ++++++++++++++++++++++++++++++ opengemini/measurement_test.go | 122 ++++++++++-- opengemini/query_condition.go | 6 + 6 files changed, 514 insertions(+), 32 deletions(-) create mode 100644 opengemini/measurement_builder.go diff --git a/opengemini/client.go b/opengemini/client.go index bd23e81..e93865e 100644 --- a/opengemini/client.go +++ b/opengemini/client.go @@ -21,7 +21,7 @@ type Client interface { Query(query Query) (*QueryResult, error) // WritePoint write single point to assigned database. If you don't want to implement callbackFunc to receive error - // in writing, you cloud use opengemini.CallbackDummy. + // in writing, you could use opengemini.CallbackDummy. WritePoint(database string, point *Point, callbackFunc WriteCallback) error // WritePointWithRp write single point with retention policy. If you don't want to implement callbackFunc to // receive error in writing, you cloud use opengemini.CallbackDummy. @@ -46,8 +46,30 @@ type Client interface { ShowRetentionPolicies(database string) ([]RetentionPolicy, error) DropRetentionPolicy(database, retentionPolicy string) error - ShowMeasurements(database, retentionPolicy string) ([]string, error) - DropMeasurement(database, retentionPolicy, measurement string) error + // ShowMeasurements use command `SHOW MEASUREMENT` to view the measurements created in the database, calling + // NewMeasurementBuilder.Database("db0").RetentionPolicy("rp0").Show() is the best way to set up + // the builder, don't forget to set the database otherwise it will return an error + ShowMeasurements(builder ShowMeasurementBuilder) ([]string, error) + // DropMeasurement use command `DROP MEASUREMENT` to delete measurement, deleting a measurement + // will delete all indexes, series and data. if retentionPolicy is empty, use default retention policy, calling + // NewMeasurementBuilder.Database("db0").RetentionPolicy("rp0").Drop() is the best way to set up the builder, don't + // forget to set the database otherwise it will return an error + DropMeasurement(builder DropMeasurementBuilder) error + // CreateMeasurement use command `CREATE MEASUREMENT` to create measurement, openGemini supports + // automatic table creation when writing data, but in the following three situations, tables need + // to be created in advance. + // - specify a tag as partition key + // - text search + // - high series cardinality storage engine(HSCE) + // calling NewMeasurementBuilder().Database(databaseName).Measurement(measurement). + // Create().TagList([]string{"tag1", "tag2"}).FieldMap(map[string]fieldType{ + // "f_int64": FieldTypeInt64, + // "f_float": FieldTypeFloat64, + // "f_bool": FieldTypeBool, + // "f_string": FieldTypeString, + // }).ShardKeys([]string{"tag1"}) is the best way to set up the + // builder, don't forget to set the database otherwise it will return an error + CreateMeasurement(builder CreateMeasurementBuilder) error ShowTagKeys(database, command string) ([]ValuesResult, error) ShowTagValues(database, command string) ([]ValuesResult, error) diff --git a/opengemini/error.go b/opengemini/error.go index cc89182..0dd4b6e 100644 --- a/opengemini/error.go +++ b/opengemini/error.go @@ -7,6 +7,7 @@ var ( ErrRetentionPolicy = errors.New("empty retention policy") ErrEmptyMeasurement = errors.New("empty measurement") ErrEmptyCommand = errors.New("empty command") + ErrEmptyTagOrField = errors.New("empty tag or field") ) // checkDatabaseName checks if the database name is empty and returns an error if it is. @@ -17,23 +18,20 @@ func checkDatabaseName(database string) error { return nil } -func checkDatabaseAndPolicy(database, retentionPolicy string) error { - if len(database) == 0 { - return ErrEmptyDatabaseName - } - if len(retentionPolicy) == 0 { - return ErrRetentionPolicy +// checkMeasurementName checks if the measurement name is empty and returns an error if it is. +func checkMeasurementName(mst string) error { + if len(mst) == 0 { + return ErrEmptyMeasurement } return nil } -// checkDatabaseAndMeasurement checks if the database name, retention policy, or measurement is empty and returns the appropriate error. -func checkDatabaseAndMeasurement(database, measurement string) error { +func checkDatabaseAndPolicy(database, retentionPolicy string) error { if len(database) == 0 { return ErrEmptyDatabaseName } - if len(measurement) == 0 { - return ErrEmptyMeasurement + if len(retentionPolicy) == 0 { + return ErrRetentionPolicy } return nil } diff --git a/opengemini/measurement.go b/opengemini/measurement.go index 02e3b79..6afa1ac 100644 --- a/opengemini/measurement.go +++ b/opengemini/measurement.go @@ -13,13 +13,23 @@ type ValuesResult struct { Values []interface{} } -func (c *client) ShowMeasurements(database, retentionPolicy string) ([]string, error) { - err := checkDatabaseName(database) +func (c *client) ShowMeasurements(builder ShowMeasurementBuilder) ([]string, error) { + base := builder.getMeasurementBase() + err := checkDatabaseName(base.database) if err != nil { return nil, err } - queryResult, err := c.queryPost(Query{Database: database, RetentionPolicy: retentionPolicy, Command: "SHOW MEASUREMENTS"}) + command, err := builder.build() + if err != nil { + return nil, err + } + + queryResult, err := c.queryPost(Query{ + Database: base.database, + RetentionPolicy: base.retentionPolicy, + Command: command, + }) if err != nil { return nil, err @@ -33,17 +43,56 @@ func (c *client) ShowMeasurements(database, retentionPolicy string) ([]string, e return queryResult.convertMeasurements(), nil } -func (c *client) DropMeasurement(database, retentionPolicy, measurement string) error { - err := checkDatabaseAndMeasurement(database, measurement) +func (c *client) DropMeasurement(builder DropMeasurementBuilder) error { + base := builder.getMeasurementBase() + err := checkDatabaseName(base.database) + if err != nil { + return err + } + + command, err := builder.build() + if err != nil { + return err + } + + req := requestDetails{ + queryValues: make(url.Values), + } + req.queryValues.Add("db", base.database) + req.queryValues.Add("rp", base.retentionPolicy) + req.queryValues.Add("q", command) + resp, err := c.executeHttpPost("/query", req) + if err != nil { + return err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return errors.New("read resp failed, error: " + err.Error()) + } + if resp.StatusCode != http.StatusOK { + return errors.New("error resp, code: " + resp.Status + "body: " + string(body)) + } + return nil +} + +func (c *client) CreateMeasurement(builder CreateMeasurementBuilder) error { + base := builder.getMeasurementBase() + err := checkDatabaseName(base.database) + if err != nil { + return err + } + + command, err := builder.build() if err != nil { return err } req := requestDetails{ queryValues: make(url.Values), } - req.queryValues.Add("db", database) - req.queryValues.Add("rp", retentionPolicy) - req.queryValues.Add("q", "DROP MEASUREMENT \""+measurement+"\"") + req.queryValues.Add("db", base.database) + req.queryValues.Add("rp", base.retentionPolicy) + req.queryValues.Add("q", command) resp, err := c.executeHttpPost("/query", req) if err != nil { return err diff --git a/opengemini/measurement_builder.go b/opengemini/measurement_builder.go new file mode 100644 index 0000000..fe78845 --- /dev/null +++ b/opengemini/measurement_builder.go @@ -0,0 +1,307 @@ +package opengemini + +import ( + "errors" + "fmt" + "strings" +) + +type shardType string + +const ( + ShardTypeHash shardType = "HASH" + ShardTypeRange shardType = "RANGE" +) + +type fieldType string + +const ( + FieldTypeInt64 fieldType = "INT64" + FieldTypeFloat64 fieldType = "FLOAT64" + FieldTypeString fieldType = "STRING" + FieldTypeBool fieldType = "BOOL" +) + +type engineType string + +const ( + EngineTypeColumnStore engineType = "columnstore" +) + +type measurementCommand string + +const ( + MeasureCreate measurementCommand = "CREATE" + MeasureDrop measurementCommand = "DROP" + MeasureShow measurementCommand = "SHOW" +) + +type measurementBase struct { + database string + retentionPolicy string + measurement string +} + +type measurementBuilder struct { + // command specify the command type + command measurementCommand + // measurementBase is the base information of measurement + measurementBase + // filter use regexp to filter measurements + filter *ComparisonCondition + // tagList is the tags schema for measurement + tagList []string + // fields is the fields schema for measurement + fields []string + // shardType support ShardTypeHash and ShardTypeRange two ways to break up data + shardType shardType + // shardKeys specify tag as partition key + shardKeys []string + // indexType specify `text` full-text index on + indexType string + // indexList specify to create a full-text index on the fields + indexList []string + // engineType must be EngineTypeColumnStore + engineType engineType + // primaryKey storage engine will create indexes on these two fields + primaryKey []string + // sortKeys specify the data sorting method inside the storage engine + sortKeys []string +} + +func (m *measurementBuilder) TagList(tagList []string) CreateMeasurementBuilder { + for _, tag := range tagList { + m.tagList = append(m.tagList, tag+" TAG") + } + return m +} + +func (m *measurementBuilder) FieldMap(fields map[string]fieldType) CreateMeasurementBuilder { + for key, value := range fields { + m.fields = append(m.fields, key+" "+string(value)+" FIELD") + } + return m +} + +func (m *measurementBuilder) ShardKeys(shardKeys []string) CreateMeasurementBuilder { + m.shardKeys = shardKeys + return m +} + +func (m *measurementBuilder) ShardType(shardType shardType) CreateMeasurementBuilder { + m.shardType = shardType + return m +} + +func (m *measurementBuilder) FullTextIndex() CreateMeasurementBuilder { + m.indexType = "text" + return m +} + +func (m *measurementBuilder) IndexList(indexList []string) CreateMeasurementBuilder { + m.indexList = indexList + return m +} + +func (m *measurementBuilder) EngineType(engineType engineType) CreateMeasurementBuilder { + m.engineType = engineType + return m +} + +func (m *measurementBuilder) PrimaryKey(primaryKey []string) CreateMeasurementBuilder { + m.primaryKey = primaryKey + return m +} + +func (m *measurementBuilder) SortKeys(sortKeys []string) CreateMeasurementBuilder { + m.sortKeys = sortKeys + return m +} + +func (m *measurementBuilder) Filter(operator ComparisonOperator, regex string) ShowMeasurementBuilder { + m.filter = NewComparisonCondition("MEASUREMENT", operator, regex) + return m +} + +func (m *measurementBuilder) build() (string, error) { + err := checkDatabaseName(m.database) + if err != nil { + return "", err + } + switch m.command { + case MeasureCreate: + if len(m.tagList) == 0 && len(m.fields) == 0 { + return "", ErrEmptyTagOrField + } + var buffer strings.Builder + buffer.WriteString(`CREATE MEASUREMENT ` + m.measurement + " (") + if len(m.tagList) != 0 { + buffer.WriteString(strings.Join(m.tagList, ",")) + } + if len(m.tagList) != 0 && len(m.fields) != 0 { + buffer.WriteString(",") + } + if len(m.fields) != 0 { + buffer.WriteString(strings.Join(m.fields, ",")) + } + buffer.WriteString(")") + var withIdentifier bool + if m.indexType != "" && len(m.indexList) == 0 { + return "", errors.New("empty index list") + } + if m.indexType != "" { + withIdentifier = true + buffer.WriteString(" WITH ") + buffer.WriteString(" INDEXTYPE " + m.indexType) + buffer.WriteString(" INDEXLIST " + strings.Join(m.indexList, ",")) + } + if m.engineType != "" { + if !withIdentifier { + withIdentifier = true + buffer.WriteString(" WITH ") + } + buffer.WriteString(" ENGINETYPE = " + string(m.engineType)) + } + if len(m.shardKeys) != 0 { + if !withIdentifier { + withIdentifier = true + buffer.WriteString(" WITH ") + } + buffer.WriteString(" SHARDKEY " + strings.Join(m.shardKeys, ",")) + } + if m.shardType != "" { + if !withIdentifier { + withIdentifier = true + buffer.WriteString(" WITH ") + } + buffer.WriteString(" TYPE " + string(m.shardType)) + } + if len(m.primaryKey) != 0 { + if !withIdentifier { + withIdentifier = true + buffer.WriteString(" WITH ") + } + buffer.WriteString(" PRIMARYKEY " + strings.Join(m.primaryKey, ",")) + } + if len(m.sortKeys) != 0 { + if !withIdentifier { + buffer.WriteString(" WITH ") + } + buffer.WriteString(" SORTKEY " + strings.Join(m.sortKeys, ",")) + } + return buffer.String(), nil + case MeasureDrop: + err := checkMeasurementName(m.measurement) + if err != nil { + return "", err + } + return fmt.Sprintf(`DROP MEASUREMENT "%s"`, m.measurement), nil + case MeasureShow: + var buf strings.Builder + buf.WriteString(`SHOW MEASUREMENTS`) + if m.filter != nil { + buf.WriteString(" WITH " + m.filter.string()) + } + return buf.String(), nil + default: + return "", fmt.Errorf("invalid command: %s", m.command) + } +} + +func (m *measurementBuilder) Show() ShowMeasurementBuilder { + m.command = MeasureShow + return m +} + +func (m *measurementBuilder) Drop() DropMeasurementBuilder { + m.command = MeasureDrop + return m +} + +func (m *measurementBuilder) Create() CreateMeasurementBuilder { + m.command = MeasureCreate + return m +} + +// Database specify measurement in database +func (m *measurementBuilder) Database(database string) MeasurementBuilder { + m.database = database + return m +} + +// Measurement specify measurement name +func (m *measurementBuilder) Measurement(measurement string) MeasurementBuilder { + m.measurement = measurement + return m +} + +// RetentionPolicy specify retention policy +func (m *measurementBuilder) RetentionPolicy(rp string) MeasurementBuilder { + m.retentionPolicy = rp + return m +} + +// getMeasurementBase get measurement info base +func (m *measurementBuilder) getMeasurementBase() measurementBase { + return m.measurementBase +} + +type MeasurementBuilder interface { + // Database specify measurement in database + Database(database string) MeasurementBuilder + // Measurement specify measurement name + Measurement(measurement string) MeasurementBuilder + // RetentionPolicy specify retention policy + RetentionPolicy(rp string) MeasurementBuilder + // Show use command `SHOW MEASUREMENT` to show measurements + Show() ShowMeasurementBuilder + // Drop use command `DROP MEASUREMENT` to drop measurement + Drop() DropMeasurementBuilder + // Create use command `CREATE MEASUREMENT` to create measurement + Create() CreateMeasurementBuilder +} + +// DropMeasurementBuilder drop measurement, if measurement not exist, return error +type DropMeasurementBuilder interface { + build() (string, error) + getMeasurementBase() measurementBase +} + +type ShowMeasurementBuilder interface { + // Filter use regular statements to filter measurements, operator support Match, NotMatch, Equals, NotEquals + Filter(operator ComparisonOperator, regex string) ShowMeasurementBuilder + build() (string, error) + getMeasurementBase() measurementBase +} + +type CreateMeasurementBuilder interface { + // TagList specify tag list to create measurement + TagList(tagList []string) CreateMeasurementBuilder + // FieldMap specify field map to create measurement + FieldMap(fields map[string]fieldType) CreateMeasurementBuilder + // ShardKeys specify shard keys(tag as partition key) to create measurement, required when use + // high series cardinality storage engine(HSCE) + ShardKeys(shardKeys []string) CreateMeasurementBuilder + // ShardType specify shard type to create measurement, support ShardTypeHash and ShardTypeRange two ways to + // break up data, required when use high series cardinality storage engine(HSCE) + ShardType(shardType shardType) CreateMeasurementBuilder + // FullTextIndex required when want measurement support full-text index + FullTextIndex() CreateMeasurementBuilder + // IndexList required when specify which Field fields to create a full-text index on, + // these fields must be 'string' data type + IndexList(indexList []string) CreateMeasurementBuilder + // EngineType required when want measurement support HSCE, set EngineTypeColumnStore + EngineType(engineType engineType) CreateMeasurementBuilder + // PrimaryKey required when use HSCE, such as the primary key is `location` and `direction`, which means that the + // storage engine will create indexes on these two fields + PrimaryKey(primaryKey []string) CreateMeasurementBuilder + // SortKeys required when use HSCE, specify the data sorting method inside the storage engine, time means sorting + // by time, and can also be changed to rtt or direction, or even other fields in the table + SortKeys(sortKeys []string) CreateMeasurementBuilder + build() (string, error) + getMeasurementBase() measurementBase +} + +func NewMeasurementBuilder() MeasurementBuilder { + return &measurementBuilder{} +} diff --git a/opengemini/measurement_test.go b/opengemini/measurement_test.go index 8f0942e..002c76f 100644 --- a/opengemini/measurement_test.go +++ b/opengemini/measurement_test.go @@ -7,6 +7,34 @@ import ( "time" ) +func TestClient_ShowMeasurements(t *testing.T) { + c := testDefaultClient(t) + databaseName := randomDatabaseName() + measurement := "prefix_" + randomMeasurement() + err := c.CreateDatabase(databaseName) + require.Nil(t, err) + err = c.CreateMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement(measurement). + Create().TagList([]string{"tag1", "tag2"}).FieldMap(map[string]fieldType{ + "f_int64": FieldTypeInt64, + "f_float": FieldTypeFloat64, + "f_bool": FieldTypeBool, + "f_string": FieldTypeString, + })) + require.Nil(t, err) + time.Sleep(time.Second * 5) + measurements, err := c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName). + Show().Filter(Match, "/prefix.*/")) + require.Nil(t, err) + require.Contains(t, measurements, measurement) + measurements, err = c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName). + Show().Filter(Match, "/suffix.*/")) + require.Nil(t, err) + require.Equal(t, 0, len(measurements)) + + err = c.DropDatabase(databaseName) + require.Nil(t, err) +} + func TestClientDropMeasurementExistSpecifyRp(t *testing.T) { c := testDefaultClient(t) databaseName := randomDatabaseName() @@ -29,15 +57,18 @@ func TestClientDropMeasurementExistSpecifyRp(t *testing.T) { }) require.Nil(t, err) time.Sleep(time.Second * 5) - measurements, err := c.ShowMeasurements(databaseName, retentionPolicy) + measurements, err := c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName). + RetentionPolicy(retentionPolicy).Show()) require.Nil(t, err) require.Contains(t, measurements, measurement) - err = c.DropMeasurement(databaseName, retentionPolicy, measurement) + err = c.DropMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement(measurement). + RetentionPolicy(retentionPolicy).Drop()) require.Nil(t, err) - measurements, err = c.ShowMeasurements(databaseName, retentionPolicy) + measurements, err = c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName). + RetentionPolicy(retentionPolicy).Show()) require.Nil(t, err) require.NotContains(t, measurements, measurement) - err = c.DropRetentionPolicy(databaseName, retentionPolicy) + err = c.DropDatabase(databaseName) require.Nil(t, err) } @@ -49,7 +80,8 @@ func TestClientDropMeasurementNonExistent(t *testing.T) { require.Nil(t, err) err = c.CreateRetentionPolicy(databaseName, RpConfig{Name: retentionPolicy, Duration: "3d"}, false) require.Nil(t, err) - err = c.DropMeasurement(databaseName, retentionPolicy, "non_existent_measurement") + err = c.DropMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement("non_existent_measurement"). + RetentionPolicy(retentionPolicy).Drop()) require.Nil(t, err) err = c.DropRetentionPolicy(databaseName, retentionPolicy) require.Nil(t, err) @@ -65,7 +97,7 @@ func TestClientDropMeasurementEmptyMeasurementName(t *testing.T) { require.Nil(t, err) err = c.CreateRetentionPolicy(databaseName, RpConfig{Name: retentionPolicy, Duration: "3d"}, false) require.Nil(t, err) - err = c.DropMeasurement(databaseName, retentionPolicy, "") + err = c.DropMeasurement(NewMeasurementBuilder().Database(databaseName).RetentionPolicy(retentionPolicy).Drop()) require.NotNil(t, err) err = c.DropRetentionPolicy(databaseName, retentionPolicy) require.Nil(t, err) @@ -86,18 +118,18 @@ func TestClientDropMeasurementEmptyRetentionPolicy(t *testing.T) { Time: time.Time{}, Tags: nil, Fields: map[string]interface{}{ - "value": 1, + "string": 1, }, }, }) require.Nil(t, err) time.Sleep(time.Second * 5) - measurements, err := c.ShowMeasurements(databaseName, "") + measurements, err := c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName).Show()) require.Nil(t, err) require.Contains(t, measurements, measurement) - err = c.DropMeasurement(databaseName, "", measurement) + err = c.DropMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement(measurement).Drop()) require.Nil(t, err) - measurements, err = c.ShowMeasurements(databaseName, "") + measurements, err = c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName).Show()) require.Nil(t, err) require.NotContains(t, measurements, measurement) err = c.DropDatabase(databaseName) @@ -108,6 +140,74 @@ func TestClientDropMeasurementEmptyDatabaseName(t *testing.T) { c := testDefaultClient(t) retentionPolicy := randomRetentionPolicy() measurement := randomMeasurement() - err := c.DropMeasurement("", retentionPolicy, measurement) + err := c.DropMeasurement(NewMeasurementBuilder().Database("").Measurement(measurement). + RetentionPolicy(retentionPolicy).Drop()) require.NotNil(t, err) } + +func TestClient_CreateMeasurement(t *testing.T) { + c := testDefaultClient(t) + databaseName := randomDatabaseName() + measurement := randomMeasurement() + err := c.CreateDatabase(databaseName) + require.Nil(t, err) + err = c.CreateMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement(measurement). + Create().TagList([]string{"tag1", "tag2"}).FieldMap(map[string]fieldType{ + "f_int64": FieldTypeInt64, + "f_float": FieldTypeFloat64, + "f_bool": FieldTypeBool, + "f_string": FieldTypeString, + }).ShardKeys([]string{"tag1"})) + require.Nil(t, err) + time.Sleep(time.Second * 5) + measurements, err := c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName).Show()) + require.Nil(t, err) + require.Contains(t, measurements, measurement) + err = c.DropDatabase(databaseName) + require.Nil(t, err) +} + +func TestClient_CreateMeasurementWithHSCE(t *testing.T) { + c := testDefaultClient(t) + databaseName := randomDatabaseName() + measurement := randomMeasurement() + err := c.CreateDatabase(databaseName) + require.Nil(t, err) + err = c.CreateMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement(measurement). + Create().TagList([]string{"tag1", "tag2", "tag3", "tag4"}).FieldMap(map[string]fieldType{ + "f_int64": FieldTypeInt64, + "f_float": FieldTypeFloat64, + "f_bool": FieldTypeBool, + "f_string": FieldTypeString, + }).ShardKeys([]string{"tag1"}).EngineType(EngineTypeColumnStore).IndexList([]string{"f_int64", "f_string"}). + PrimaryKey([]string{"f_string"}).SortKeys([]string{"f_string"})) + require.Nil(t, err) + time.Sleep(time.Second * 5) + measurements, err := c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName).Show()) + require.Nil(t, err) + require.Contains(t, measurements, measurement) + err = c.DropDatabase(databaseName) + require.Nil(t, err) +} + +func TestClient_CreateMeasurementWithFullTextIndex(t *testing.T) { + c := testDefaultClient(t) + databaseName := randomDatabaseName() + measurement := randomMeasurement() + err := c.CreateDatabase(databaseName) + require.Nil(t, err) + err = c.CreateMeasurement(NewMeasurementBuilder().Database(databaseName).Measurement(measurement). + Create().TagList([]string{"tag1", "tag2", "tag3", "tag4"}).FieldMap(map[string]fieldType{ + "f_int64": FieldTypeInt64, + "f_float": FieldTypeFloat64, + "f_bool": FieldTypeBool, + "f_string": FieldTypeString, + }).ShardKeys([]string{"tag1"}).FullTextIndex().IndexList([]string{"f_int64", "f_string"})) + require.Nil(t, err) + time.Sleep(time.Second * 5) + measurements, err := c.ShowMeasurements(NewMeasurementBuilder().Database(databaseName).Show()) + require.Nil(t, err) + require.Contains(t, measurements, measurement) + err = c.DropDatabase(databaseName) + require.Nil(t, err) +} diff --git a/opengemini/query_condition.go b/opengemini/query_condition.go index 99e3742..21e4759 100644 --- a/opengemini/query_condition.go +++ b/opengemini/query_condition.go @@ -1,5 +1,7 @@ package opengemini +import "fmt" + type Condition interface{} type ComparisonCondition struct { @@ -8,6 +10,10 @@ type ComparisonCondition struct { Value interface{} } +func (cc *ComparisonCondition) string() string { + return fmt.Sprintf("%s %s %v", cc.Column, cc.Operator, cc.Value) +} + func NewComparisonCondition(column string, operator ComparisonOperator, value interface{}) *ComparisonCondition { return &ComparisonCondition{ Column: column,