Susumu Yata
null+****@clear*****
Mon Jun 19 11:40:35 JST 2017
Susumu Yata 2017-06-19 11:40:35 +0900 (Mon, 19 Jun 2017) New Revision: 41e250309336a9d2eb630965c49fb2b320122959 https://github.com/groonga/grnci/commit/41e250309336a9d2eb630965c49fb2b320122959 Message: Support more commands in v2. Modified files: v2/command.go v2/db.go v2/db_test.go v2/info.go Modified: v2/command.go (+2 -0) =================================================================== --- v2/command.go 2017-06-16 22:46:33 +0900 (2b5089b) +++ v2/command.go 2017-06-19 11:40:35 +0900 (ad68a19) @@ -42,6 +42,8 @@ var formatParamValueDefault = func(value interface{}) (string, error) { return strconv.FormatFloat(v, 'f', -1, 64), nil case string: return v, nil + // TODO: case time.Time: + // TODO: case Geo: default: return "", NewError(InvalidCommand, map[string]interface{}{ "value": value, Modified: v2/db.go (+594 -23) =================================================================== --- v2/db.go 2017-06-16 22:46:33 +0900 (67be82c) +++ v2/db.go 2017-06-19 11:40:35 +0900 (0709b5b) @@ -7,6 +7,7 @@ import ( "io" "io/ioutil" "reflect" + "strconv" "strings" "time" ) @@ -449,31 +450,95 @@ func (db *DB) Load(tbl string, values io.Reader, options *DBLoadOptions) (int, R return db.recvInt(resp) } +// encodeValue encodes a value. +func (db *DB) encodeValue(body []byte, v reflect.Value) []byte { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return append(body, "null"...) + } + v = v.Elem() + } + switch v := v.Interface().(type) { + case bool: + return strconv.AppendBool(body, v) + case int: + return strconv.AppendInt(body, int64(v), 10) + case int8: + return strconv.AppendInt(body, int64(v), 10) + case int16: + return strconv.AppendInt(body, int64(v), 10) + case int32: + return strconv.AppendInt(body, int64(v), 10) + case int64: + return strconv.AppendInt(body, v, 10) + case uint: + return strconv.AppendUint(body, uint64(v), 10) + case uint8: + return strconv.AppendUint(body, uint64(v), 10) + case uint16: + return strconv.AppendUint(body, uint64(v), 10) + case uint32: + return strconv.AppendUint(body, uint64(v), 10) + case uint64: + return strconv.AppendUint(body, v, 10) + case float32: + return strconv.AppendFloat(body, float64(v), 'e', -1, 32) + case float64: + return strconv.AppendFloat(body, v, 'e', -1, 64) + case string: + b, _ := json.Marshal(v) + return append(body, b...) + case time.Time: + return strconv.AppendFloat(body, float64(v.UnixNano())/1000000000.0, 'f', -1, 64) + default: + return body + } +} + // encodeRow encodes a row. func (db *DB) encodeRow(body []byte, row reflect.Value, fis []*StructFieldInfo) []byte { - // TODO + body = append(body, '[') + for i, fi := range fis { + if i != 0 { + body = append(body, ',') + } + v := row.Field(fi.Index) + if v.Kind() == reflect.Ptr { + if v.IsNil() { + body = append(body, "null"...) + continue + } + v = v.Elem() + } + switch v.Kind() { + case reflect.Array, reflect.Slice: + body = append(body, '[') + for i := 0; i < v.Len(); i++ { + if i != 0 { + body = append(body, ',') + } + body = db.encodeValue(body, v.Index(i)) + } + body = append(body, ']') + default: + body = db.encodeValue(body, v) + } + } + body = append(body, ']') return body } // encodeRows encodes rows. -func (db *DB) encodeRows(rows reflect.Value, fis []*StructFieldInfo) ([]byte, error) { - body := []byte("[") - for rows.Kind() == reflect.Ptr { - rows = rows.Elem() - } - switch rows.Kind() { - case reflect.Array, reflect.Slice: - for i := 0; i < rows.Len(); i++ { - row := rows.Index(i) - for row.Kind() == reflect.Ptr { - row = row.Elem() - } - body = db.encodeRow(body, row, fis) +func (db *DB) encodeRows(body []byte, rows reflect.Value, fis []*StructFieldInfo) []byte { + n := rows.Len() + for i := 0; i < n; i++ { + if i != 0 { + body = append(body, ',') } - case reflect.Struct: + row := rows.Index(i) + body = db.encodeRow(body, row, fis) } - body = append(body, ']') - return body, nil + return body } // LoadRows executes load. @@ -496,16 +561,43 @@ func (db *DB) LoadRows(tbl string, rows interface{}, options *DBLoadOptions) (in fi, ok := si.FieldsByColumnName[col] if !ok { return 0, nil, NewError(InvalidCommand, map[string]interface{}{ - "error": "", + "column": col, + "error": "The column has no assciated field.", }) } fis = append(fis, fi) } } - body, err := db.encodeRows(reflect.ValueOf(rows), fis) - if err != nil { - return 0, nil, err + + body := []byte("[") + v := reflect.ValueOf(rows) + switch v.Kind() { + case reflect.Ptr: + if v.IsNil() { + return 0, nil, NewError(InvalidCommand, map[string]interface{}{ + "rows": nil, + "error": "The rows must not be nil.", + }) + } + v = v.Elem() + if v.Kind() != reflect.Struct { + return 0, nil, NewError(InvalidCommand, map[string]interface{}{ + "type": reflect.TypeOf(rows).Name(), + "error": "The type is not supported.", + }) + } + body = db.encodeRow(body, v, fis) + case reflect.Array, reflect.Slice: + body = db.encodeRows(body, v, fis) + case reflect.Struct: + body = db.encodeRow(body, v, fis) + default: + return 0, nil, NewError(InvalidCommand, map[string]interface{}{ + "type": reflect.TypeOf(rows).Name(), + "error": "The type is not supported.", + }) } + body = append(body, ']') return db.Load(tbl, bytes.NewReader(body), options) } @@ -586,6 +678,41 @@ func (db *DB) LogReopen() (bool, Response, error) { return db.recvBool(resp) } +// DBNormalizedText is a result of normalize. +type DBNormalizedText struct { + Normalized string `json:"normalized"` + Types []string `json:"types"` + Checks []int `json:"checks"` +} + +// Normalize executes normalize. +func (db *DB) Normalize(normalizer, str string, flags []string) (*DBNormalizedText, Response, error) { + params := map[string]interface{}{ + "normalizer": normalizer, + "string": str, + } + if flags != nil { + params["flags"] = flags + } + resp, err := db.Invoke("normalize", params, nil) + if err != nil { + return nil, nil, err + } + defer resp.Close() + jsonData, err := ioutil.ReadAll(resp) + if err != nil { + return nil, resp, err + } + var result DBNormalizedText + if err := json.Unmarshal(jsonData, &result); err != nil { + return nil, resp, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + return &result, resp, nil +} + // DBNormalizer is a result of tokenizer_list. type DBNormalizer struct { Name string `json:"name"` @@ -904,6 +1031,9 @@ func (db *DB) Select(tbl string, options *DBSelectOptions) (Response, error) { params["sort_keys"] = options.SortKeys } if options.OutputColumns != nil { + params["output_columns"] = options.OutputColumns + } + if options.OutputColumns != nil { params["query"] = options.Query } if options.Offset != 0 { @@ -957,10 +1087,342 @@ func (db *DB) Select(tbl string, options *DBSelectOptions) (Response, error) { return db.Invoke("select", params, nil) } +// parseRows parses rows. +func (db *DB) parseRows(rows interface{}, data []byte, fis []*StructFieldInfo) (int, error) { + var raw [][][]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + + var nHits int + if err := json.Unmarshal(raw[0][0][0], &nHits); err != nil { + return 0, err + } + + rawCols := raw[0][1] + nCols := len(rawCols) + if nCols != len(fis) { + // Remove _score from fields if _score does not exist in the response. + for i, field := range fis { + if field.ColumnName == "_score" { + hasScore := false + for _, rawCol := range rawCols { + var nameType []string + if err := json.Unmarshal(rawCol, &nameType); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + if nameType[0] == "_score" { + hasScore = true + break + } + } + if !hasScore { + for j := i + 1; j < len(fis); j++ { + fis[j-1] = fis[j] + } + fis = fis[:len(fis)-1] + } + break + } + } + if nCols != len(fis) { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "nFields": len(fis), + "nCols": nCols, + "error": "nFields and nColumns must be same.", + }) + } + } + // FIXME: the following check disallows functions. + // for i, rawCol := range rawCols { + // var nameType []string + // if err := json.Unmarshal(rawCol, &nameType); err != nil { + // return 0, err + // } + // if nameType[0] != fields[i].ColumnName() { + // return 0, fmt.Errorf("column %#v expected but column %#v actual", + // fields[i].ColumnName(), nameType[0]) + // } + // } + + rawRecs := raw[0][2:] + nRecs := len(rawRecs) + + recs := reflect.ValueOf(rows).Elem() + recs.Set(reflect.MakeSlice(recs.Type(), nRecs, nRecs)) + for i := 0; i < nRecs; i++ { + rec := recs.Index(i) + for j, field := range fis { + ptr := rec.Field(field.Index).Addr() + switch v := ptr.Interface().(type) { + case *bool: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *int: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *int8: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *int16: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *int32: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *int64: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *uint: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *uint8: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *uint16: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *uint32: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *uint64: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *float32: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *float64: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *string: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *time.Time: + var f float64 + if err := json.Unmarshal(rawRecs[i][j], &f); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + *v = time.Unix(int64(f), int64(f*1000000)%1000000) + case *[]bool: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]int: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]int8: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]int16: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]int32: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]int64: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]uint: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]uint8: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]uint16: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]uint32: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]uint64: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]float32: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]float64: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]string: + if err := json.Unmarshal(rawRecs[i][j], v); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + case *[]time.Time: + var f []float64 + if err := json.Unmarshal(rawRecs[i][j], &f); err != nil { + return 0, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + *v = make([]time.Time, len(f)) + for i := range f { + (*v)[i] = time.Unix(int64(f[i]), int64(f[i]*1000000)%1000000) + } + } + } + } + return nHits, nil + +} + // SelectRows executes select. func (db *DB) SelectRows(tbl string, rows interface{}, options *DBSelectOptions) (int, Response, error) { - // TODO - return 0, nil, nil + if options == nil { + options = NewDBSelectOptions() + } + si, err := GetStructInfo(rows) + if err != nil { + return 0, nil, err + } + var fis []*StructFieldInfo + if options.OutputColumns == nil { + fis = si.Fields + for _, fi := range fis { + options.OutputColumns = append(options.OutputColumns, fi.ColumnName) + } + } else { + for _, col := range options.OutputColumns { + fi, ok := si.FieldsByColumnName[col] + if !ok { + return 0, nil, NewError(InvalidCommand, map[string]interface{}{ + "column": col, + "error": "The column has no assciated field.", + }) + } + fis = append(fis, fi) + } + } + resp, err := db.Select(tbl, options) + if err != nil { + return 0, nil, err + } + defer resp.Close() + data, err := ioutil.ReadAll(resp) + if err != nil { + return 0, nil, err + } + n, err := db.parseRows(rows, data, fis) + return n, resp, err } // DBStatus is a response of status. @@ -1261,6 +1723,62 @@ func (db *DB) TableRename(name, newName string) (bool, Response, error) { return db.recvBool(resp) } +// DBTableTokenizeOptions is options of DB.TableTokenize. +type DBTableTokenizeOptions struct { + Flags []string + Mode string + IndexColumn string +} + +// NewDBTableTokenizeOptions returns the default DBTableTokenizeOptions. +func NewDBTableTokenizeOptions() *DBTableTokenizeOptions { + return &DBTableTokenizeOptions{} +} + +// DBToken is a result of table_tokenize and tokenize. +type DBToken struct { + Position int `json:"position"` + ForcePrefix bool `json:"force_prefix"` + Value string `json:"value"` +} + +// TableTokenize executes tokenize. +func (db *DB) TableTokenize(tbl, str string, options *DBTableTokenizeOptions) ([]DBToken, Response, error) { + if options == nil { + options = NewDBTableTokenizeOptions() + } + params := map[string]interface{}{ + "table": tbl, + "string": str, + } + if options.Flags != nil { + params["flags"] = options.Flags + } + if options.Mode != "" { + params["mode"] = options.Mode + } + if options.IndexColumn != "" { + params["index_column"] = options.IndexColumn + } + resp, err := db.Invoke("table_tokenize", params, nil) + if err != nil { + return nil, nil, err + } + defer resp.Close() + jsonData, err := ioutil.ReadAll(resp) + if err != nil { + return nil, resp, err + } + var result []DBToken + if err := json.Unmarshal(jsonData, &result); err != nil { + return nil, resp, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + return result, resp, nil +} + // ThreadLimit executes thread_limit. // If max < 0, max is not passed to thread_limit. func (db *DB) ThreadLimit(max int) (int, Response, error) { @@ -1277,6 +1795,59 @@ func (db *DB) ThreadLimit(max int) (int, Response, error) { return db.recvInt(resp) } +// DBTokenizeOptions is options of DB.Tokenize. +type DBTokenizeOptions struct { + Normalizer string + Flags []string + Mode string + TokenFilters []string +} + +// NewDBTokenizeOptions returns the default DBTokenizeOptions. +func NewDBTokenizeOptions() *DBTokenizeOptions { + return &DBTokenizeOptions{} +} + +// Tokenize executes tokenize. +func (db *DB) Tokenize(tokenizer, str string, options *DBTokenizeOptions) ([]DBToken, Response, error) { + if options == nil { + options = NewDBTokenizeOptions() + } + params := map[string]interface{}{ + "tokenizer": tokenizer, + "string": str, + } + if options.Normalizer != "" { + params["normalizer"] = options.Normalizer + } + if options.Flags != nil { + params["flags"] = options.Flags + } + if options.Mode != "" { + params["mode"] = options.Mode + } + if options.TokenFilters != nil { + params["token_filters"] = options.TokenFilters + } + resp, err := db.Invoke("tokenize", params, nil) + if err != nil { + return nil, nil, err + } + defer resp.Close() + jsonData, err := ioutil.ReadAll(resp) + if err != nil { + return nil, resp, err + } + var result []DBToken + if err := json.Unmarshal(jsonData, &result); err != nil { + return nil, resp, NewError(InvalidResponse, map[string]interface{}{ + "method": "json.Unmarshal", + "error": err.Error(), + }) + } + return result, resp, nil +} + // DBTokenizer is a result of tokenizer_list. type DBTokenizer struct { Name string `json:"name"` Modified: v2/db_test.go (+165 -1) =================================================================== --- v2/db_test.go 2017-06-16 22:46:33 +0900 (25fd77f) +++ v2/db_test.go 2017-06-19 11:40:35 +0900 (7033029) @@ -5,6 +5,7 @@ import ( "log" "strings" "testing" + "time" ) func TestDBColumnList(t *testing.T) { @@ -116,7 +117,71 @@ func TestDBLoad(t *testing.T) { result, resp, err := db.Load("Tbl", strings.NewReader("[]"), nil) if err != nil { - t.Fatalf("db.Dump failed: %v", err) + t.Fatalf("db.Load failed: %v", err) + } + log.Printf("result = %d", result) + log.Printf("resp = %#v", resp) + if err := resp.Err(); err != nil { + log.Printf("error = %#v", err) + } +} + +func TestDBLoadRows(t *testing.T) { + client, err := NewHTTPClient("", nil) + if err != nil { + t.Skipf("NewHTTPClient failed: %v", err) + } + db := NewDB(client) + defer db.Close() + + type Row struct { + Key string `grnci:"_key"` + Bool bool `grnci:"bool"` + Int int `grnci:"int"` + Int8 int8 `grnci:"int8"` + Int16 int16 `grnci:"int16"` + Int32 int32 `grnci:"int32"` + Int64 int64 `grnci:"int64"` + UInt uint `grnci:"uint"` + UInt8 uint8 `grnci:"uint8"` + UInt16 uint16 `grnci:"uint16"` + UInt32 uint32 `grnci:"uint32"` + UInt64 uint64 `grnci:"uint64"` + Float32 float32 `grnci:"float32"` + Float64 float64 `grnci:"float64"` + String string `grnci:"string"` + Time time.Time `grnci:"time"` + BoolSlice []bool `grnci:"bool_slice"` + IntSlice []int `grnci:"int_slice"` + Int8Slice []int8 `grnci:"int8_slice"` + Int16Slice []int16 `grnci:"int16_slice"` + Int32Slice []int32 `grnci:"int32_slice"` + Int64Slice []int64 `grnci:"int64_slice"` + UIntSlice []uint `grnci:"uint_slice"` + UInt8Slice []uint8 `grnci:"uint8_slice"` + UInt16Slice []uint16 `grnci:"uint16_slice"` + UInt32Slice []uint32 `grnci:"uint32_slice"` + UInt64Slice []uint64 `grnci:"uint64_slice"` + Float32Slice []float32 `grnci:"float32_slice"` + Float64Slice []float64 `grnci:"float64_slice"` + StringSlice []string `grnci:"string_slice"` + TimeSlice []time.Time `grnci:"time_slice"` + } + rows := []Row{ + Row{ + Key: "Appke", + Time: time.Now(), + StringSlice: []string{"iOS", "Safari"}, + }, + Row{ + Key: "Microsoft", + Time: time.Now(), + StringSlice: []string{"Windows", "Edge"}, + }, + } + result, resp, err := db.LoadRows("Tbl", rows, nil) + if err != nil { + t.Fatalf("db.LoadRows failed: %v", err) } log.Printf("result = %d", result) log.Printf("resp = %#v", resp) @@ -125,6 +190,27 @@ func TestDBLoad(t *testing.T) { } } +func TestDBNormalize(t *testing.T) { + client, err := NewHTTPClient("", nil) + if err != nil { + t.Skipf("NewHTTPClient failed: %v", err) + } + db := NewDB(client) + defer db.Close() + + result, resp, err := db.Normalize("NormalizerAuto", "LaTeX", []string{ + "WITH_TYPES", "WITH_CHECKS", + }) + if err != nil { + t.Fatalf("db.Normalize failed: %v", err) + } + log.Printf("result = %#v", result) + log.Printf("resp = %#v", resp) + if err := resp.Err(); err != nil { + log.Printf("error = %#v", err) + } +} + func TestDBNormalizerList(t *testing.T) { client, err := NewHTTPClient("", nil) if err != nil { @@ -186,6 +272,44 @@ func TestDBSelect(t *testing.T) { } } +func TestDBSelectRows(t *testing.T) { + client, err := NewHTTPClient("", nil) + if err != nil { + t.Skipf("NewHTTPClient failed: %v", err) + } + db := NewDB(client) + defer db.Close() + + type Row struct { + Key string `grnci:"_key"` + Bool bool `grnci:"bool"` + Int8 int8 `grnci:"int8"` + Int16 int16 `grnci:"int16"` + Int32 int32 `grnci:"int32"` + Int64 int64 `grnci:"int64"` + UInt8 int8 `grnci:"uint8"` + UInt16 int16 `grnci:"uint16"` + UInt32 int32 `grnci:"uint32"` + UInt64 int64 `grnci:"uint64"` + Float float64 `grnci:"float"` + String string `grnci:"string"` + Time time.Time `grnci:"time"` + TimeSlice []time.Time `grnci:"time_slice"` + } + var rows []Row + n, resp, err := db.SelectRows("Tbl", &rows, nil) + if err != nil { + t.Fatalf("db.SelectRows failed: %v", err) + } + log.Printf("n = %d", n) + log.Printf("rows = %#v", rows) + log.Printf("time = %s", rows[0].Time) + log.Printf("resp = %#v", resp) + if err := resp.Err(); err != nil { + log.Printf("error = %#v", err) + } +} + func TestDBStatus(t *testing.T) { client, err := NewHTTPClient("", nil) if err != nil { @@ -224,6 +348,46 @@ func TestDBTableList(t *testing.T) { } } +func TestDBTableTokenize(t *testing.T) { + client, err := NewHTTPClient("", nil) + if err != nil { + t.Skipf("NewHTTPClient failed: %v", err) + } + db := NewDB(client) + defer db.Close() + + options := NewDBTableTokenizeOptions() + options.Mode = "ADD" + result, resp, err := db.TableTokenize("Tbl", "あいうえお", options) + if err != nil { + t.Fatalf("db.TableTokenize failed: %v", err) + } + log.Printf("result = %#v", result) + log.Printf("resp = %#v", resp) + if err := resp.Err(); err != nil { + log.Printf("error = %#v", err) + } +} + +func TestDBTokenize(t *testing.T) { + client, err := NewHTTPClient("", nil) + if err != nil { + t.Skipf("NewHTTPClient failed: %v", err) + } + db := NewDB(client) + defer db.Close() + + result, resp, err := db.Tokenize("TokenBigram", "あいうえお", nil) + if err != nil { + t.Fatalf("db.Tokenize failed: %v", err) + } + log.Printf("result = %#v", result) + log.Printf("resp = %#v", resp) + if err := resp.Err(); err != nil { + log.Printf("error = %#v", err) + } +} + func TestDBTruncate(t *testing.T) { client, err := NewHTTPClient("", nil) if err != nil { Modified: v2/info.go (+6 -8) =================================================================== --- v2/info.go 2017-06-16 22:46:33 +0900 (8b8434f) +++ v2/info.go 2017-06-19 11:40:35 +0900 (8efeff8) @@ -11,7 +11,6 @@ import ( const ( // tagKey is the tag key for struct fields associated with Groonga columns. tagKey = "grnci" - // tagSep is the separator in a struct field tag value. tagSep = ';' ) @@ -62,20 +61,19 @@ func newStructFieldInfo(index int, field *reflect.StructField) (*StructFieldInfo case float32, float64: case string: case time.Time: - case Geo: default: return nil, NewError(InvalidType, map[string]interface{}{ "type": typ.Name(), "error": "The type is not supported.", }) } - return &StructFieldInfo{ - Index: index, - Field: field, - Tags: tags, - Type: field.Type, - Dimension: dim, + Index: index, + Field: field, + Tags: tags, + Type: field.Type, + ColumnName: tags[0], + Dimension: dim, }, nil } -------------- next part -------------- HTML����������������������������...Download