Skip to content

Commit

Permalink
adds string dimension support
Browse files Browse the repository at this point in the history
  • Loading branch information
antalakas committed Apr 14, 2020
1 parent 83eee25 commit b65efa3
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 0 deletions.
15 changes: 15 additions & 0 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ type NonEmptyDomain struct {
Bounds interface{}
}

// NonEmptyDomainVar contains the non empty dimension bounds and dimension name
// for a var-sized dimension
type NonEmptyDomainVar struct {
DimensionName string
Bounds []interface{}
}

// NewArray alloc a new array
func NewArray(ctx *Context, uri string) (*Array, error) {
curi := C.CString(uri)
Expand Down Expand Up @@ -276,6 +283,11 @@ func makeNonEmptyDomain(domain *Domain, domainSlice interface{}) ([]NonEmptyDoma
return nil, err
}

// dimCellValNum, err := dimension.CellValNum()
// if dimCellValNum == TILEDB_VAR_NUM {
// break
// }

switch domainType {
case TILEDB_INT8:
tmpDomain := domainSlice.([]int8)
Expand Down Expand Up @@ -307,6 +319,9 @@ func makeNonEmptyDomain(domain *Domain, domainSlice interface{}) ([]NonEmptyDoma
case TILEDB_FLOAT64:
tmpDomain := domainSlice.([]float64)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []float64{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
case TILEDB_STRING_ASCII:
tmpDomain := domainSlice.([]uint8)
nonEmptyDomains = append(nonEmptyDomains, NonEmptyDomain{DimensionName: name, Bounds: []uint8{tmpDomain[i*2], tmpDomain[(i*2)+1]}})
default:
return nil, fmt.Errorf("error creating non empty domain: unknown domain type")
}
Expand Down
28 changes: 28 additions & 0 deletions dimension.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,30 @@ func NewDimension(context *Context, name string, domain interface{}, extent inte
return &dimension, nil
}

// NewStringDimension alloc a new string dimension
func NewStringDimension(context *Context, name string) (*Dimension, error) {
dimension := Dimension{context: context}
var cname *C.char = C.CString(name)
defer C.free(unsafe.Pointer(cname))

var datatype Datatype
var ret C.int32_t

datatype = TILEDB_STRING_ASCII
ret = C.tiledb_dimension_alloc(context.tiledbContext, cname, C.tiledb_datatype_t(datatype), nil, nil, &dimension.tiledbDimension)

if ret != C.TILEDB_OK {
return nil, fmt.Errorf("Error creating tiledb dimension: %s", context.LastError())
}

// Set finalizer for free C pointer on gc
runtime.SetFinalizer(&dimension, func(dimension *Dimension) {
dimension.Free()
})

return &dimension, nil
}

// Free tiledb_dimension_t that was allocated on heap in c
func (d *Dimension) Free() {
if d.tiledbDimension != nil {
Expand Down Expand Up @@ -344,6 +368,8 @@ func (d *Dimension) Domain() (interface{}, error) {
tmpDomain[i] = float64(s)
}
domain = tmpDomain
case TILEDB_STRING_ASCII:
domain = nil
default:
return nil, fmt.Errorf("Unrecognized domain type: %d", datatype)
}
Expand Down Expand Up @@ -414,6 +440,8 @@ func (d *Dimension) Extent() (interface{}, error) {
defer C.free(cextent)
ret = C.tiledb_dimension_get_tile_extent(d.context.tiledbContext, d.tiledbDimension, &cextent)
extent = *(*float64)(unsafe.Pointer(cextent))
case TILEDB_STRING_ASCII:
extent = nil
default:
return nil, fmt.Errorf("Unrecognized extent type: %d", datatype)
}
Expand Down
12 changes: 12 additions & 0 deletions dimension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ func TestDimensionDomainTypes(t *testing.T) {
assert.NotNil(t, extent)
assert.EqualValues(t, float64(5), extent)

dimension, err = NewStringDimension(context, "test")
assert.Nil(t, err)
assert.NotNil(t, dimension)
domain, err = dimension.Domain()
assert.Nil(t, err)
// Test getting domain
assert.Nil(t, domain)
// Test getting extent
extent, err = dimension.Extent()
assert.Nil(t, err)
assert.Nil(t, extent)

// Temp path for testing dump
tmpPathDump := os.TempDir() + string(os.PathSeparator) + "tiledb_dimension_dump_test"
// Cleanup tmp file when test ends
Expand Down
186 changes: 186 additions & 0 deletions examples/string_dim_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/**
* @file string_dim_test.go
*
* @section LICENSE
*
* The MIT License
*
* @copyright Copyright (c) 2020 TileDB, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @section DESCRIPTION
*
* When run, this program will create a 2D sparse array, having string dim
* write some data to it, and read a ranges of data to prove usability
*
*/

package examples

import (
"fmt"
"os"

tiledb "github.com/TileDB-Inc/TileDB-Go"
)

// Name of array.
var stringDimArrayName = "string_dim"

func createStringDimArray() {
// Create a TileDB context.
ctx, err := tiledb.NewContext(nil)
checkError(err)

// The array will be 4x4 with dimensions "rows" and "cols",
// with domain [1,4].
domain, err := tiledb.NewDomain(ctx)
checkError(err)
d, err := tiledb.NewStringDimension(ctx, "d")
checkError(err)
err = domain.AddDimensions(d)
checkError(err)

// The array will be sparse.
schema, err := tiledb.NewArraySchema(ctx, tiledb.TILEDB_SPARSE)
checkError(err)
err = schema.SetDomain(domain)
checkError(err)

// Add a single attribute "a" so each cell can store an integer.
a, err := tiledb.NewAttribute(ctx, "a", tiledb.TILEDB_INT32)
checkError(err)
err = schema.AddAttributes(a)
checkError(err)

// Create the (empty) array on disk.
array, err := tiledb.NewArray(ctx, stringDimArrayName)
checkError(err)
err = array.Create(schema)
checkError(err)
}

func writeStringDimArray() {
ctx, err := tiledb.NewContext(nil)
checkError(err)

// Open the array for writing
array, err := tiledb.NewArray(ctx, stringDimArrayName)
checkError(err)
err = array.Open(tiledb.TILEDB_WRITE)
checkError(err)

// Prepare some data for the array
buffA := []int32{3, 2, 1, 4}
dData := []byte("ccbbddddaa")
dOff := []uint64{0, 2, 4, 8}

// Create the query
query, err := tiledb.NewQuery(ctx, array)
checkError(err)
_, _, err = query.SetBufferVar("d", dOff, dData)
checkError(err)
_, err = query.SetBuffer("a", buffA)
checkError(err)
err = query.SetLayout(tiledb.TILEDB_UNORDERED)
checkError(err)

// Perform the write and close the array.
err = query.Submit()
checkError(err)
err = array.Close()
checkError(err)
}

func readStringDimArray() {
ctx, err := tiledb.NewContext(nil)
checkError(err)

// Prepare the array for reading
array, err := tiledb.NewArray(ctx, stringDimArrayName)
checkError(err)
err = array.Open(tiledb.TILEDB_READ)
checkError(err)

nonEmptyDomainStart, nonEmptyDomainEnd, result, err := array.NonEmptyDomainVarFromName("d")

if !result {
fmt.Printf("NonEmptyDomain: %v\n", nonEmptyDomainStart)
fmt.Printf("NonEmptyDomain: %v\n", nonEmptyDomainEnd)
} else {
fmt.Println(err)
}

// Prepare the query
query, err := tiledb.NewQuery(ctx, array)
checkError(err)
s1 := []byte("a")
s2 := []byte("ee")
err = query.AddRangeVar(0, s1, s2)
checkError(err)

// Try using valid index
rangeNum, err := query.GetRangeNum(0)
checkError(err)

// Try using valid dimension index and range index
start, end, err := query.GetRange(0, 0)
checkError(err)

fmt.Printf("Range start for dimension 0, range 0 is: %d\n", start.(uint8))
fmt.Printf("Range end for dimension 0, range 0 is: %d\n", end.(uint8))

fmt.Printf("Number of ranges across dimension 0 is: %d\n", *rangeNum)

offsets := make([]uint64, 4)
data := make([]byte, 10)
_, _, err = query.SetBufferVar("d", offsets, data)
checkError(err)

// Submit the query and close the array.
err = query.Submit()
checkError(err)

fmt.Printf("offsets: %v\n", offsets)
fmt.Printf("data: %v\n", data)

err = array.Close()
checkError(err)
}

// ExampleStringDimArray shows an example of creation, writing and reading of a
// sparse array with string dim
func ExampleStringDimArray() {
err := os.RemoveAll(stringDimArrayName)
checkError(err)

createStringDimArray()
writeStringDimArray()
readStringDimArray()

// // Cleanup example so unit tests are clean
// if _, err := os.Stat(stringDimArrayName); err == nil {
// err = os.RemoveAll(stringDimArrayName)
// checkError(err)
// }

fmt.Println("ToDo")
// Output: ToDo
}
3 changes: 3 additions & 0 deletions query.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,9 @@ func (q *Query) GetRange(dimIdx uint32, rangeNum uint64) (interface{}, interface
case TILEDB_FLOAT64:
start = *(*float64)(unsafe.Pointer(pStart))
end = *(*float64)(unsafe.Pointer(pEnd))
case TILEDB_STRING_ASCII:
start = *(*uint8)(unsafe.Pointer(pStart))
end = *(*uint8)(unsafe.Pointer(pEnd))
default:
return nil, nil, fmt.Errorf("Unrecognized dimension type: %d", datatype)
}
Expand Down

0 comments on commit b65efa3

Please sign in to comment.