gocassa

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2015 License: MIT Imports: 14 Imported by: 61

README

gocassa

GoDoc Build Status

Gocassa is a high-level library on top of gocql.

Current version: v1.2.0

Compared to gocql it provides query building, adds data binding, and provides easy-to-use "recipe" tables for common query use-cases. Unlike cqlc, it does not use code generation.

For docs, see: https://godoc.org/github.com/hailocab/gocassa

Usage

Below is a basic example showing how to connect to a Cassandra cluster and setup a simple table. For more advanced examples see the "Table Types" section below.

package main

import(
    "fmt"
    "time"
    
    "github.com/hailocab/gocassa"
)

type Sale struct {
    Id          string
    CustomerId  string
    SellerId    string
    Price       int
    Created     time.Time
}

func main() {
    keySpace, err := gocassa.ConnectToKeySpace("test", []string{"127.0.0.1"}, "", "")
    if err != nil {
        panic(err)
    }
    salesTable := keySpace.Table("sale", &Sale{}, gocassa.Keys{
        PartitionKeys: []string{"Id"},
    })

    err = salesTable.Set(Sale{
        Id: "sale-1",
        CustomerId: "customer-1",
        SellerId: "seller-1",
        Price: 42,
        Created: time.Now(),
    }).Run()
    if err != nil {
        panic(err)
    }

    result := Sale{}
    if err := salesTable.Where(gocassa.Eq("Id", "sale-1")).ReadOne(&result).Run(); err != nil {
        panic(err)
    }
    fmt.Println(result)
}

You can pass additional options to a gocassa Op to further configure your queries, for example the following query orders the results by the field "Name" in descending order and limits the results to a total of 100.

err := salesTable.List("seller-1", nil, 0, &results).WithOptions(gocassa.Options{
    ClusteringOrder: []ClusteringOrderColumn{
        {DESC, "Name"},
    },
    Limit: 100,
}).Run()
Table Types

Gocassa provides multiple table types with their own unique interfaces:

  • a raw CQL table called simply Table - this lets you do pretty much any query imaginable
  • and a number of single purpose 'recipe' tables (Map, Multimap, TimeSeries, MultiTimeSeries, MultiMapMultiKey), which aims to help the user by having a simplified interface tailored to a given common query use case
Table
    salesTable := keySpace.Table("sale", &Sale{}, gocassa.Keys{
        PartitionKeys: []string{"Id"},
    })
    result := Sale{}
    err := salesTable.Where(gocassa.Eq("Id", "sale-1")).ReadOne(&result).Run()

link to this example

MapTable

MapTable provides only very simple CRUD functionality:

    // …
    salesTable := keySpace.MapTable("sale", "Id", &Sale{})
    result := Sale{}
    salesTable.Read("sale-1", &result).Run()
}

link to this example

Read, Set, Update, and Delete all happen by "Id".

MultimapTable

MultimapTable can list rows filtered by equality of a single field (eg. list sales based on their sellerId):

    salesTable := keySpace.MultimapTable("sale", "SellerId", "Id", &Sale{})
    // …
    results := []Sale{}
    err := salesTable.List("seller-1", nil, 0, &results).Run()

link to this example

For examples on how to do pagination or Update with this table, refer to the example (linked under code snippet).

TimeSeriesTable

TimeSeriesTable provides an interface to list rows within a time interval:

    salesTable := keySpace.TimeSeriesTable("sale", "Created", "Id", &Sale{}, 24 * time.Hour)
    //...
    results := []Sale{}
    err := salesTable.List(yesterdayTime, todayTime, &results).Run()
MultiTimeSeriesTable

MultiTimeSeriesTable is like a cross between MultimapTable and TimeSeriesTable. It can list rows within a time interval, and filtered by equality of a single field. The following lists sales in a time interval, by a certain seller:

    salesTable := keySpace.MultiTimeSeriesTable("sale", "SellerId", "Created", "Id", &Sale{}, 24 * time.Hour)
    //...
    results := []Sale{}
    err := salesTable.List("seller-1", yesterdayTime, todayTime, &results).Run()
MultiMapMultiKeyTable

MultiMapMultiKeyTable can perform CRUD operations on rows filtered by equality of multiple fields (eg. read a sale based on their city , sellerId and Id of the sale):

    salePartitionKeys := []Sale{"City"}
    saleClusteringKeys := []Sale{"SellerId","Id"}
    salesTable := keySpace.MultimapMultiKeyTable("sale", salePartitionKeys, saleClusteringKeys, Sale{})
    // …
    result := Sale{}
    saleFieldCity = salePartitionKeys[0]
    saleFieldSellerId = saleClusteringKeys[0]
    saleFieldSaleId = saleClusteringKeys[1]

    field := make(map[string]interface{})
    id := make(map[string]interface{})


    field[saleFieldCity] = "London"
    id[saleFieldSellerId] = "141-dasf1-124"
    id[saleFieldSaleId] = "512hha232"

    err := salesTable.Read(field, id , &result).Run()

Encoding/Decoding data structures

When setting structs in gocassa the library first converts your value to a map. Each exported field is added to the map unless

  • the field's tag is "-", or
  • the field is empty and its tag specifies the "omitempty" option

Each fields default name in the map is the field name but can be specified in the struct field's tag value. The "cql" key in the struct field's tag value is the key name, followed by an optional comma and options. Examples:

// Field is ignored by this package.
Field int `cql:"-"`
// Field appears as key "myName".
Field int `cql:"myName"`
// Field appears as key "myName" and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `cql:"myName,omitempty"`
// Field appears as key "Field" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `cql:",omitempty"`
// All fields in the EmbeddedType are squashed into the parent type.
EmbeddedType `cql:",squash"`

When encoding maps with non-string keys the key values are automatically converted to strings where possible, however it is recommended that you use strings where possible (for example map[string]T).

Troubleshooting

Too long table names

In case you get the following error:

Column family names shouldn't be more than 48 characters long (got "somelongishtablename_multitimeseries_start_id_24h0m0s")

You can use the TableName options to override the default internal ones:

tbl = tbl.WithOptions(Options{TableName: "somelongishtablename_mts_start_id_24h0m0s"})

Documentation

Overview

Package gocassa is a high-level library on top of gocql

Current version: v1.2.0 Compared to gocql it provides query building, adds data binding, and provides easy-to-use "recipe" tables for common query use-cases. Unlike cqlc, it does not use code generation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClusteringOrderColumn

type ClusteringOrderColumn struct {
	Direction ColumnDirection
	Column    string
}

ClusteringOrderColumn specifies a clustering column and whether its clustering order is ASC or DESC.

type ColumnDirection

type ColumnDirection bool
const (
	ASC  ColumnDirection = false
	DESC                 = true
)

func (ColumnDirection) String

func (d ColumnDirection) String() string

type Connection

type Connection interface {
	CreateKeySpace(name string) error
	DropKeySpace(name string) error
	KeySpace(name string) KeySpace
}

Connection exists because one can not connect to a keyspace if it does not exist, thus having a Create on KeySpace is not possible. Use ConnectToKeySpace to acquire an instance of KeySpace without getting a Connection.

func Connect

func Connect(nodeIps []string, username, password string) (Connection, error)

Connect to a cluster. If you are happy with default the options use this, if you need anything fancier, use `NewConnection`

func NewConnection

func NewConnection(q QueryExecutor) Connection

NewConnection creates a Connection with a custom query executor. Use `Connect` if you just want to talk to Cassandra with the default options. See `GoCQLSessionToQueryExecutor` if you want to use a gocql session with your own options as a `QueryExecutor`

type Counter

type Counter int

type Filter

type Filter interface {
	// Updates does a partial update. Use this if you don't want to overwrite your whole row, but you want to modify fields atomically.
	Update(m map[string]interface{}) Op // Probably this is danger zone (can't be implemented efficiently) on a selectuinb with more than 1 document
	// Delete all rows matching the filter.
	Delete() Op
	// Read the results. Make sure you pass in a pointer to a slice.
	Read(pointerToASlice interface{}) Op
	// Read one result. Make sure you pass in a pointer.
	ReadOne(pointer interface{}) Op
}

Filter is a subset of a Table, filtered by Relations. You can do writes or reads on a filter.

type KeySpace

type KeySpace interface {
	MapTable(tableName, id string, row interface{}) MapTable
	MultimapTable(tableName, fieldToIndexBy, uniqueKey string, row interface{}) MultimapTable
	MultimapMultiKeyTable(tableName string, fieldToIndexBy, uniqueKey []string, row interface{}) MultimapMkTable
	TimeSeriesTable(tableName, timeField, uniqueKey string, bucketSize time.Duration, row interface{}) TimeSeriesTable
	MultiTimeSeriesTable(tableName, fieldToIndexByField, timeField, uniqueKey string, bucketSize time.Duration, row interface{}) MultiTimeSeriesTable
	Table(tableName string, row interface{}, keys Keys) Table
	// DebugMode enables/disables debug mode depending on the value of the input boolean.
	// When DebugMode is enabled, all built CQL statements are printe to stdout.
	DebugMode(bool)
	// Name returns the keyspace name as in C*
	Name() string
	// Tables returns the name of all configured column families in this keyspace
	Tables() ([]string, error)
	// Exists returns whether the specified column family exists within the keyspace
	Exists(string) (bool, error)
}

KeySpace is used to obtain tables from.

func ConnectToKeySpace

func ConnectToKeySpace(keySpace string, nodeIps []string, username, password string) (KeySpace, error)

Connect to a certain keyspace directly. Same as using Connect().KeySpace(keySpaceName)

func NewMockKeySpace

func NewMockKeySpace() KeySpace

type Keys

type Keys struct {
	PartitionKeys     []string
	ClusteringColumns []string
	Compound          bool //indicates if the partitions keys are gereated as compound key when no clustering columns are set
}

Keys is used with the raw CQL Table type. It is implicit when using recipe tables.

type MapTable

type MapTable interface {
	Set(v interface{}) Op
	Update(id interface{}, m map[string]interface{}) Op
	Delete(id interface{}) Op
	Read(id, pointer interface{}) Op
	MultiRead(ids []interface{}, pointerToASlice interface{}) Op
	WithOptions(Options) MapTable
	TableChanger
}

MapTable gives you basic CRUD functionality. If you need fancier ways to query your data set have a look at the other tables.

type MockFilter

type MockFilter struct {
	// contains filtered or unexported fields
}

MockFilter implements the Filter interface and works with MockTable.

func (*MockFilter) Delete

func (f *MockFilter) Delete() Op

func (*MockFilter) Read

func (q *MockFilter) Read(out interface{}) Op

func (*MockFilter) ReadOne

func (q *MockFilter) ReadOne(out interface{}) Op

func (*MockFilter) Update

func (f *MockFilter) Update(m map[string]interface{}) Op

func (*MockFilter) UpdateWithOptions

func (f *MockFilter) UpdateWithOptions(m map[string]interface{}, options Options) Op

type MockTable

type MockTable struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

MockTable implements the Table interface and stores rows in-memory.

func (*MockTable) Create

func (t *MockTable) Create() error

func (*MockTable) CreateIfNotExist

func (t *MockTable) CreateIfNotExist() error

func (*MockTable) CreateIfNotExistStatement

func (t *MockTable) CreateIfNotExistStatement() (string, error)

func (*MockTable) CreateStatement

func (t *MockTable) CreateStatement() (string, error)

func (*MockTable) Name

func (t *MockTable) Name() string

func (*MockTable) Recreate

func (t *MockTable) Recreate() error

func (*MockTable) Set

func (t *MockTable) Set(i interface{}) Op

func (*MockTable) SetWithOptions

func (t *MockTable) SetWithOptions(i interface{}, options Options) Op

func (*MockTable) Where

func (t *MockTable) Where(relations ...Relation) Filter

func (*MockTable) WithOptions

func (t *MockTable) WithOptions(o Options) Table

type Modifier

type Modifier struct {
	// contains filtered or unexported fields
}

func CounterIncrement

func CounterIncrement(value int) Modifier

CounterIncrement increments the value of the counter with the given value. Negative value results in decrementing.

func ListAppend

func ListAppend(value interface{}) Modifier

ListAppend appends a value to the end of the list

func ListPrepend

func ListPrepend(value interface{}) Modifier

ListPrepend prepends a value to the front of the list

func ListRemove

func ListRemove(value interface{}) Modifier

ListRemove removes all elements from a list having a particular value

func ListSetAtIndex

func ListSetAtIndex(index int, value interface{}) Modifier

ListSetAtIndex sets the list element at a given index to a given value

func MapSetField

func MapSetField(key, value interface{}) Modifier

MapSetField updates the map with the given key and value

func MapSetFields

func MapSetFields(fields map[string]interface{}) Modifier

MapSetFields updates the map with keys and values in the given map

type MultiTimeSeriesTable

type MultiTimeSeriesTable interface {
	// timeField and idField must be present
	Set(v interface{}) Op
	Update(v interface{}, timeStamp time.Time, id interface{}, m map[string]interface{}) Op
	Delete(v interface{}, timeStamp time.Time, id interface{}) Op
	Read(v interface{}, timeStamp time.Time, id, pointer interface{}) Op
	List(v interface{}, start, end time.Time, pointerToASlice interface{}) Op
	WithOptions(Options) MultiTimeSeriesTable
	TableChanger
}

MultiTimeSeriesTable is a cross between TimeSeries and Multimap tables.

type MultimapMkTable

type MultimapMkTable interface {
	Set(v interface{}) Op
	Update(v, id map[string]interface{}, m map[string]interface{}) Op
	Delete(v, id map[string]interface{}) Op
	DeleteAll(v map[string]interface{}) Op
	List(v, startId map[string]interface{}, limit int, pointerToASlice interface{}) Op
	Read(v, id map[string]interface{}, pointer interface{}) Op
	MultiRead(v, id map[string]interface{}, pointerToASlice interface{}) Op
	WithOptions(Options) MultimapMkTable
	TableChanger
}

MultimapMkTable lets you list rows based on several fields equality, eg. 'list all sales where seller id = v and name = 'john'.

type MultimapTable

type MultimapTable interface {
	Set(v interface{}) Op
	Update(v, id interface{}, m map[string]interface{}) Op
	Delete(v, id interface{}) Op
	DeleteAll(v interface{}) Op
	List(v, startId interface{}, limit int, pointerToASlice interface{}) Op
	Read(v, id, pointer interface{}) Op
	MultiRead(v interface{}, ids []interface{}, pointerToASlice interface{}) Op
	WithOptions(Options) MultimapTable
	TableChanger
}

MultimapTable lets you list rows based on a field equality, eg. 'list all sales where seller id = v'.

type Op

type Op interface {
	// Run the operation.
	Run() error
	// You do not need this in 95% of the use cases, use Run!
	// Using atomic batched writes (logged batches in Cassandra terminology) comes at a high performance cost!
	RunAtomically() error
	// Add an other Op to this one.
	Add(...Op) Op
	// WithOptions lets you specify `Op` level `Options`.
	// The `Op` level Options and the `Table` level `Options` will be merged in a way that Op level takes precedence.
	// All queries in an `Op` will have the specified `Options`.
	// When using Add(), the existing options are preserved.
	// For example:
	//
	//    op1.WithOptions(Options{Limit:3}).Add(op2.WithOptions(Options{Limit:2})) // op1 has a limit of 3, op2 has a limit of 2
	//    op1.WithOptions(Options{Limit:3}).Add(op2).WithOptions(Options{Limit:2}) // op1 and op2 both have a limit of 2
	//
	WithOptions(Options) Op
	// Preflight performs any pre-execution validation that confirms the op considers itself "valid".
	// NOTE: Run() and RunAtomically() should call this method before execution, and abort if any errors are returned.
	Preflight() error
	// GenerateStatement generates the statment and params to perform the operation
	GenerateStatement() (string, []interface{})
	// QueryExecutor returns the QueryExecutor
	QueryExecutor() QueryExecutor
}

Op is returned by both read and write methods, you have to run them explicitly to take effect. It represents one or more operations.

func Noop

func Noop() Op

type Options

type Options struct {
	// TTL specifies a duration over which data is valid. It will be truncated to second precision upon statement
	// execution.
	TTL time.Duration
	// Limit query result set
	Limit int
	// TableName overrides the default internal table name. When naming a table 'users' the internal table name becomes 'users_someTableSpecificMetaInformation'.
	TableName string
	// ClusteringOrder specifies the clustering order during table creation. If empty, it is omitted and the defaults are used.
	ClusteringOrder []ClusteringOrderColumn
	// Indicates if allow filtering should be appended at the end of the query
	AllowFiltering bool
	// Select allows you to do partial reads, ie. retrieve only a subset of fields
	Select []string
	// Consistency specifies the consistency level. If nil, it is considered not set
	Consistency *gocql.Consistency
	// Setting CompactStorage to true enables table creation with compact storage
	CompactStorage bool
	// Compressor specifies the compressor (if any) to use on a newly created table
	Compressor string
}

Options can contain table or statement specific options. The reason for this is because statement specific (TTL, Limit) options make sense as table level options (eg. have default TTL for every Update without specifying it all the time)

func (Options) AppendClusteringOrder

func (o Options) AppendClusteringOrder(column string, direction ColumnDirection) Options

AppendClusteringOrder adds a clustering order. If there already clustering orders, the new one is added to the end.

func (Options) Merge

func (o Options) Merge(neu Options) Options

Returns a new Options which is a right biased merge of the two initial Options.

type QueryExecutor

type QueryExecutor interface {
	// Query executes a query and returns the results.  It also takes Options to do things like set consistency
	QueryWithOptions(opts Options, stmt string, params ...interface{}) ([]map[string]interface{}, error)
	// Query executes a query and returns the results
	Query(stmt string, params ...interface{}) ([]map[string]interface{}, error)
	// Execute executes a DML query. It also takes Options to do things like set consistency
	ExecuteWithOptions(opts Options, stmt string, params ...interface{}) error
	// Execute executes a DML query
	Execute(stmt string, params ...interface{}) error
	// ExecuteAtomically executs multiple DML queries with a logged batch
	ExecuteAtomically(stmt []string, params [][]interface{}) error
}

QueryExecutor actually executes the queries - this is mostly useful for testing/mocking purposes, ignore this otherwise. This library is using github.com/gocql/gocql as the query executor by default.

func GoCQLSessionToQueryExecutor

func GoCQLSessionToQueryExecutor(sess *gocql.Session) QueryExecutor

GoCQLSessionToQueryExecutor enables you to supply your own gocql session with your custom options Then you can use NewConnection to mint your own thing See #90 for more details

type Relation

type Relation struct {
	// contains filtered or unexported fields
}

func Eq

func Eq(key string, term interface{}) Relation

func GT

func GT(key string, term interface{}) Relation

func GTE

func GTE(key string, term interface{}) Relation

func In

func In(key string, terms ...interface{}) Relation

func LT

func LT(key string, term interface{}) Relation

func LTE

func LTE(key string, term interface{}) Relation

type RowNotFoundError

type RowNotFoundError struct {
	// contains filtered or unexported fields
}

RowNotFoundError is returned by Reads if the Row is not found.

func (RowNotFoundError) Error

func (r RowNotFoundError) Error() string

type Table

type Table interface {
	// Set Inserts, or Replaces your row with the supplied struct. Be aware that what is not in your struct
	// will be deleted. To only overwrite some of the fields, use Query.Update.
	Set(v interface{}) Op
	// Where accepts a bunch of realtions and returns a filter. See the documentation for Relation and Filter to understand what that means.
	Where(relations ...Relation) Filter // Because we provide selections
	// Name returns the underlying table name, as stored in C*
	WithOptions(Options) Table
	TableChanger
}

Table is the only non-recipe table, it is the "raw CQL table", it lets you do pretty much whatever you want with the downside that you have to know what you are doing - eg. you have to know what queries can you make on a certain partition key - clustering column combination.

type TableChanger

type TableChanger interface {
	// Create creates the table in the keySpace, but only if it does not exist already.
	// If the table already exists, it returns an error.
	Create() error
	// CreateStatement returns you the CQL query which can be used to create the table manually in cqlsh
	CreateStatement() (string, error)
	// Create creates the table in the keySpace, but only if it does not exist already.
	// If the table already exists, then nothing is created.
	CreateIfNotExist() error
	// CreateStatement returns you the CQL query which can be used to create the table manually in cqlsh
	CreateIfNotExistStatement() (string, error)
	// Recreate drops the table if exists and creates it again.
	// This is useful for test purposes only.
	Recreate() error
	// Name returns the name of the table, as in C*
	Name() string
}

Danger zone! Do not use this interface unless you really know what you are doing

type TimeSeriesTable

type TimeSeriesTable interface {
	// timeField and idField must be present
	Set(v interface{}) Op
	Update(timeStamp time.Time, id interface{}, m map[string]interface{}) Op
	Delete(timeStamp time.Time, id interface{}) Op
	Read(timeStamp time.Time, id, pointer interface{}) Op
	List(start, end time.Time, pointerToASlice interface{}) Op
	WithOptions(Options) TimeSeriesTable
	TableChanger
}

TimeSeriesTable lets you list rows which have a field value between two date ranges.

Directories

Path Synopsis
examples
This package provides some punk-rock reflection which is not in the stdlib.
This package provides some punk-rock reflection which is not in the stdlib.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL