zoom

package module
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2014 License: MIT Imports: 16 Imported by: 12

README

Zoom

Version: 0.6.1

A blazing-fast, lightweight ORM for Go built on Redis.

Requires redis version >= 2.8.9 and Go version >= 1.0.

Full documentation is available on godoc.org.

WARNING: this isn't done yet and may change significantly before the official release. There is no promise of backwards compatibility until version 1.0. I do not advise using Zoom for production or mission-critical applications. Feedback and pull requests are welcome :)

Table of Contents

Philosophy

If you want to build a high-performing, low latency application, but still want some of the ease of an ORM, Zoom is for you!

Zoom allows you to:

  • Persistently save structs of any type
  • Retrieve structs from the database
  • Preserve relationships between structs
  • Preform limited queries

Zoom consciously makes the trade off of using more memory in order to increase performance. However, you can configure it to use less memory if you want. Zoom does not do sharding (but might in the future), so be aware that memory could be a hard constraint for larger applications.

Zoom is a high-level library and abstracts away more complicated aspects of the Redis API. For example, it manages its own connection pool, performs transactions when possible, and automatically converts structs to and from a format suitable for the database. If needed, you can still execute redis commands directly.

If you want to use advanced or complicated SQL queries, Zoom is not for you.

Installation

First, you must install Redis on your system. See installation instructions. By default, Zoom will use a tcp/http connection on localhost:6379 (same as the Redis default). The latest version of Redis is recommended.

To install Zoom itself:

go get github.com/albrow/zoom

This will pull the current master branch, which is (most likely) working but is quickly changing.

Getting Started

First, add github.com/albrow/zoom to your import statement:

import (
    ...
    github.com/albrow/zoom
)

Then, call zoom.Init somewhere in your app initialization code, e.g. in the main function. You must also call zoom.Close when your application exits, so it's a good idea to use defer.

func main() {
    // ...
    if err := zoom.Init(nil); err != nil {
        // handle err
    }
    defer zoom.Close()
    // ...
}

The Init function takes a *zoom.Configuration struct as an argument. Here's a list of options and their defaults:

type Configuration struct {
	Address       string // Address to connect to. Default: "localhost:6379"
	Network       string // Network to use. Default: "tcp"
	Database      int    // Database id to use (using SELECT). Default: 0
}

If possible, it is strongly recommended that you use a unix socket connection instead of tcp. Redis is roughly 50% faster this way. To connect with a unix socket, you must first configure redis to accept socket connections (typically on /tmp/redis.sock). If you are unsure how to do this, refer to the official redis docs for help. You might also find the redis quickstart guide helpful, especially the bottom sections.

To use unix sockets with Zoom, simply pass in "unix" as the Network and "/tmp/unix.sock" as the Address:

config := &zoom.Configuration {
	Address: "/tmp/redis.sock",
	Network: "unix",
}
zoom.Init(config)

Working with Models

Creating Models

In order to save a struct using Zoom, you need to embed an anonymous DefaultData field. DefaultData gives you an Id and getters and setters for that id, which are required in order to save the model to the database. Here's an example of a Person model:

type Person struct {
    Name string
    zoom.DefaultData
}

Because of the way zoom uses reflection, all the fields you want to save need to be public.

You must also call zoom.Register so that Zoom can spec out the different model types and the relations between them. You only need to do this once per type. For example, somewhere in your initialization sequence (e.g. in the main function) put the following:

// register the *Person type under the default name "Person"
if err := zoom.Register(&Person{}); err != nil {
    // handle error
}

Now the *Person type will be associated with the string name "Person." You can also use the RegisterName funcion to specify a custom name for the model type.

Saving Models

To persistently save a Person model to the databse, simply call zoom.Save.

p := &Person{Name: "Alice"}
if err := zoom.Save(p); err != nil {
    // handle error
}
Finding a Single Model

Zoom will automatically assign a random, unique id to each saved model. To retrieve a model by id, use the FindById function, which also requires the name associated with the model type. The return type is interface{} so you may need to type assert.

result, err := zoom.FindById("Person", "a_valid_person_id")
if err != nil {
    // handle error
}

// type assert
person, ok := result.(*Person)
if !ok {
    // handle !ok
}

Alternatively, you can use the ScanById function to avoid type assertion. It expects a pointer to a struct as an argument(some registered model type).

p := &Person{}
if err := zoom.ScanById("a_valid_person_id", p); err != nil {
    // handle error
}
Deleting Models

To delete a model you can just use the Delete function:

if err := zoom.Delete(person); err != nil {
	// handle err
}

Or if you know the model's id, use the DeleteById function:

if err := zoom.DeleteById("Person", "some_person_id"); err != nil {
	// handle err
}

Running Queries

The Query Object

Zoom provides a useful abstraction for querying the database. You create queries by using the NewQuery constuctor, where you must pass in the name corresponding to the type of model you want to query. For now, Zoom only supports queries on a single type of model at a time.

You can add one or more query modifiers to the query, such as Order, Limit, and Filter. These methods return the query itself, so you can chain them together. Any errors due to invalid arguments in the query modifiers will be remembered and returned when you attempt to run the query.

Finally, you run the query using a query finisher method, such as Run or Scan.

Finding all Models of a Given Type

To retrieve a list of all persons, create a query and don't apply any modifiers. The return type of Run is interface{}, but the underlying type is a slice of Model, i.e. a slice of pointers to structs. You may need a type assertion.

results, err := zoom.NewQuery("Person").Run()
if err != nil {
    // handle error
}

// type assert each element. something like:
persons := make([]*Person, len(results))
for i, result := range results {
    if person, ok := result.(*Person); !ok {
        // handle !ok
    }
    persons[i] = person
}

You can use the Scan method if you want to avoid a type assertion. Scan expects a pointer to a slice of pointers to some model type.

persons := make([]*Person, 0)
if _, err := zoom.NewQuery("Person").Scan(persons); err != nil {
	// handle err
}
Using Query Modifiers

You can chain a query object together with one or more different modifiers. Here's a list of all the available modifiers:

  • Order
  • Limit
  • Offset
  • Include
  • Exclude
  • Filter

You can run a query with one of the following query finishers:

  • Run
  • Scan
  • IdsOnly
  • Count
  • RunOne
  • ScanOne

Here's an example of a more complicated query using several modifiers:

q := zoom.NewQuery("Person").Order("-Name").Filter("Age >=", 25).Limit(10)
result, err := q.Run()

You might be able to guess what each of these methods do, but if anything is not obvious, full documentation on the different modifiers and finishers is available on godoc.org.

Relationships

Relationships in Zoom are simple. There are no special return types or functions for using relationships. What you put in is what you get out.

One-to-One Relationships

For these examples we're going to introduce two new struct types:

// The PetOwner struct
type PetOwner struct {
	Name  string
	Pet   *Pet    // *Pet should also be a registered type
	zoom.DefaultData
}

// The Pet struct
type Pet struct {
	Name   string
	zoom.DefaultData
}

Assuming you've registered both the *PetOwner and *Pet types, Zoom will automatically set up a relationship when you save a PetOwner with a valid Pet. (The Pet must have an id)

// create a new PetOwner and a Pet
owner := &PetOwner{Name: "Bob"}
pet := &Pet{Name: "Spot"}

// save the pet
if err := zoom.Save(pet); err != nil {
	// handle err
}

// set the owner's pet
owner.Pet = pet

// save the owner
if err := zoom.Save(owner); err != nil {
	// handle err
}

Now if you retrieve the pet owner by it's id, the pet attribute will persist as well.

For now, Zoom does not support reflexivity of one-to-one relationships. So if you want pet ownership to be bidirectional (i.e. if you want an owner to know about its pet and a pet to know about its owner), you would have to manually set up both relationships.

ownerCopy := &PetOwner{}
if err := zoom.ScanById("the_id_of_above_pet_owner", ownerCopy); err != nil {
	// handle err
}

// the Pet attribute is still set
fmt.Println(ownerCopy.Pet.Name)

// Output:
//	Spot
One-to-Many Relationships

One-to-many relationships work similarly. This time we're going to use two new struct types in the examples.

// The Parent struct
type Parent struct {
	Name     string
	Children []*Child  // *Child should also be a registered type
	zoom.DefaultData
}

// The Child struct
type Child struct {
	Name   string
	zoom.DefaultData
}

Assuming you register both the *Parent and *Child types, Zoom will automatically set up a relationship when you save a parent with some children (as long as each child has an id). Here's an example:

// create a parent and two children
parent := &Parent{Name: "Christine"}
child1 := &Child{Name: "Derick"}
child2 := &Child{Name: "Elise"}

// save the children
if err := zoom.Save(child1, child2); err != nil {
	// handle err
}

// assign the children to the parent
parent.Children = append(parent.Children, child1, child2)

// save the parent
if err := zoom.Save(parent); err != nil {
	// handle err
}

Again, Zoom does not support reflexivity. So if you wanted a child to know about its parent, you would have to set up and manage the relationship manually. This might change in the future.

Now when you retrieve a parent by id, it's children field will automatically be populated. So getting the children again is straight forward.

parentCopy := &Parent{}
if err := zoom.ScanById("the_id_of_above_parent", parentCopy); err != nil {
	// handle error
}

// now you can access the children normally
for _, child := range parentCopy.Children {
	fmt.Println(child.Name)
}

// Output:
//	Derick
//	Elise

For a Parent with a lot of children, it may take a long time to get each Child from the database. If this is the case, it's a good idea to use a query with the Exclude modifier when you don't intend to use the children.

parents := make([]*Parent, 0)
q := zoom.NewQuery("Parent").Filter("Id =", "the_id_of_above_parent").Exclude("Children")
if err := q.Scan(parents); err != nil {
	// handle error
}

// Since it was excluded, Children is empty.
fmt.Println(parents[0].Children)

// Output:
//	[]
Many-to-Many Relationships

There is nothing special about many-to-many relationships. They are simply made up of multiple one-to-many relationships.

Testing & Benchmarking

Running the Tests:

To run the tests, make sure you're in the root directory for Zoom and run:

go test .

If everything passes, you should see something like:

ok  	github.com/albrow/zoom	0.355s

If any of the tests fail, please open an issue and describe what happened.

By default, tests and benchmarks will run on localhost:6379 and use database #9. You can change the address, network, and database used with flags. So to run on a unix socket at /tmp/redis.sock and use database #3, you could use:

go test . -network unix -address /tmp/redis.sock -database 3
Running the Benchmarks:

To run the benchmarks, again make sure you're in the root directory and run:

go test . -bench .

You can use the same flags as above to change the network, address, and database used.

You should see some runtimes for various operations. If you see an error or if the build fails, please open an issue.

Here are the results from my laptop (2.3GHz intel i7, 8GB ram) using a socket connection with Redis set to append-only mode:

BenchmarkConnection		20000000	      95.4 ns/op
BenchmarkPing	   		   50000	     48033 ns/op
BenchmarkSet	   		   50000	     57117 ns/op
BenchmarkGet	   		   50000	     48612 ns/op
BenchmarkSave	   		   20000	     96096 ns/op
BenchmarkMSave100	        2000	    837420 ns/op
BenchmarkFindById	   	   20000	     87540 ns/op
BenchmarkMFindById100	    5000	    618841 ns/op
BenchmarkScanById	   	   20000	     88400 ns/op
BenchmarkMScanById100	    2000	    624335 ns/op
BenchmarkRepeatDeleteById	   20000	     90814 ns/op
BenchmarkRandomDeleteById	   20000	     90636 ns/op
BenchmarkFindAllQuery1	   	   10000	    229207 ns/op
BenchmarkFindAllQuery1000	     500	   5878403 ns/op
BenchmarkFindAllQuery100000	       2	 660701316 ns/op
BenchmarkCountAllQuery1	   	   50000	     52983 ns/op
BenchmarkCountAllQuery1000	   50000	     53110 ns/op
BenchmarkCountAllQuery100000   50000	     54126 ns/op
BenchmarkMDeleteById	    	2000	    603538 ns/op

Currently, there are not many benchmarks for queries; I'm working on adding more.

The results of the benchmark can vary widely from system to system. You should run your own benchmarks that are closer to your use case to get a real sense of how Zoom will perform for you. The speeds above are already pretty fast, but improving them is one of the top priorities for this project.

Example Usage

I have built an example json/rest application which uses the latest version of zoom. It is a simple example that doesn't use all of zoom's features, but should be good enough for understanding how zoom can work in a real application.

TODO

Ordered generally by priority, here's what I'm working on:

  • Improve performance and get as close as possible to raw redis
  • Add more benchmarks
  • Add godoc compatible examples in the test files
  • Support AND and OR operators on Filters
  • Support combining queries into a single transaction
  • Use scripting to reduce round-trip latencies in queries
  • Implement high-level watching for record changes
  • Support callbacks (BeforeSave, AfterSave, BeforeDelete, AfterDelete, etc.)
  • Add option to make relationships reflexive (inverseOf struct tag?)
  • Add a dependent:delete struct tag
  • Support automatic sharding

License

Zoom is licensed under the MIT License. See the LICENSE file for more information.

Documentation

Overview

Package zoom is a fast and lightweight ORM powered by Redis. It supports models of any arbitrary struct type, supports relationships between models, and provides basic querying functionality. It also supports running Redis commands directly.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Close

func Close()

Close closes the connection pool and shuts down the Zoom library. It should be run when application exits, e.g. using defer.

func Delete

func Delete(model Model) error

Delete removes a model from the database. It will throw an error if the type of the model has not yet been registered, if the Id field of the model is empty, or if there is a problem connecting to the database. If the model does not exist in the database, Delete will not return an error; it will simply have no effect.

func DeleteById

func DeleteById(modelName string, id string) error

DeleteById removes a model from the database by its registered name and id. By default modelName should be the string version of the type of model (without the asterisk, ampersand, or package prefix). If you used RegisterName instead of Register, modelName should be the custom name you used. DeleteById will throw an error if modelName is invalid or if there is a problem connecting to the database. If the model does not exist, DeleteById will not return an error; it will simply have no effect.

func GetConn

func GetConn() redis.Conn

GetConn gets a connection from the connection pool and returns it. It can be used for directly interacting with the database. Check out http://godoc.org/github.com/garyburd/redigo/redis for full documentation on the redis.Conn type.

func Init

func Init(passedConfig *Configuration)

Init starts the Zoom library and creates a connection pool. It accepts a Configuration struct as an argument. Any zero values in the configuration will fallback to their default values. Init should be called once during application startup.

func Interfaces added in v0.4.0

func Interfaces(in interface{}) []interface{}

Interfaces convert interface{} to []interface{} Will panic if the type is invalid.

func KeyExists

func KeyExists(key string, conn redis.Conn) (bool, error)

KeyExists returns true iff a given key exists in redis. If conn is nil, a new connection will be created and closed before the end of the function.

func MDelete added in v0.4.0

func MDelete(models []Model) error

MDelete is like Delete but accepts a slice of models and deletes them all in a single transaction. See http://redis.io/topics/transactions. If an error is encountered in the middle of the transaction, the function will halt and return the error. In that case, any models which were deleted before the error was encountered will still be deleted. Usually this is fine because calling Delete on a model a second time will have no adverse effects.

func MDeleteById added in v0.4.0

func MDeleteById(modelNames []string, ids []string) error

MDeleteById is like DeleteById but accepts a slice of modelNames and ids and deletes them all in a single transaction. See http://redis.io/topics/transactions. The slice of modelNames and ids should be properly aligned so that, e.g., modelNames[0] corresponds to ids[0]. If there is an error in the middle of the transaction, the function will halt and return the error. In that case, any models which were deleted before the error was encountered will still be deleted. Usually this is fine because calling Delete on a model a second time will have no adverse effects.

func MSave added in v0.4.0

func MSave(models []Model) error

MSave is like Save but accepts a slice of models and saves them all in a single transaction. See http://redis.io/topics/transactions. If there is an error in the middle of the transaction, any models that were saved before the error was encountered will still be saved. Usually this is fine because saving a model a second time will have no adverse effects.

func MScanById added in v0.4.0

func MScanById(ids []string, models interface{}) error

MScanById is like ScanById but accepts a slice of ids and a pointer to a slice of models. It executes the commands needed to retrieve the models in a single transaction. See http://redis.io/topics/transactions. The slice of ids and models should be properly aligned so that, e.g., ids[0] corresponds to models[0]. If there is an error in the middle of the transaction, the function will halt and return the error. Any models that were scanned before the error will still be valid. If any of the models in the models slice are nil, MScanById will use reflection to allocate memory for them.

func Register

func Register(model Model) error

Register adds a model type to the list of registered types. Any struct you wish to save must be registered first. The type of model must be unique, i.e. not already registered. Each registered model gets a name, a unique string identifier, which by default is just the string version of the type (the asterisk and any package prefixes are stripped). See RegisterName if you would prefer to specify a custom name.

func RegisterName added in v0.4.0

func RegisterName(name string, model Model) error

RegisterName is like Register but allows you to specify a custom name to use for the model type. The custom name will be used as a prefix for all models of this type stored in redis. The custom name will also be used in functions which require a model name, such as queries.

func Save

func Save(model Model) error

Save writes a model (a struct which satisfies the Model interface) to the redis database. Save throws an error if the type of the struct has not yet been registered or if there is a problem connecting to the database. If the Id field of the struct is empty, Save will mutate the struct by setting the Id. To make a struct satisfy the Model interface, you can embed zoom.DefaultData.

func ScanById added in v0.1.1

func ScanById(id string, model Model) error

ScanById retrieves a model from redis and scans it into model. model should be a pointer to a struct of a registered type. ScanById will mutate the struct, filling in its fields. It returns an error if a model with that id does not exist or if there was a problem connecting to the database.

func SetContains

func SetContains(key, member string, conn redis.Conn) (bool, error)

SetContains returns true iff the redis set identified by key contains member. If conn is nil, a new connection will be created and closed before the end of the function.

func Unregister added in v0.4.0

func Unregister(model Model) error

Unregister removes a model type from the list of registered types. You only need to call UnregisterName or UnregisterType, not both.

func UnregisterName

func UnregisterName(name string) error

UnregisterName removes a model type (identified by modelName) from the list of registered model types. You only need to call UnregisterName or UnregisterType, not both.

Types

type ById added in v0.5.0

type ById []*indexedPrimativesModel

utility type for quickly sorting by id

func (ById) Len added in v0.5.0

func (ms ById) Len() int

func (ById) Less added in v0.5.0

func (ms ById) Less(i, j int) bool

func (ById) Swap added in v0.5.0

func (ms ById) Swap(i, j int)

type Configuration

type Configuration struct {
	Address  string // Address to connect to. Default: "localhost:6379"
	Network  string // Network to use. Default: "tcp"
	Database int    // Database id to use (using SELECT). Default: 0
}

Configuration contains various options. It should be created once and passed in to the Init function during application startup.

type DefaultData added in v0.3.0

type DefaultData struct {
	Id string `redis:"-"`
}

DefaultData should be embedded in any struct you wish to save. It includes important fields and required methods to implement Model.

func (DefaultData) GetId added in v0.3.0

func (d DefaultData) GetId() string

func (*DefaultData) SetId added in v0.3.0

func (d *DefaultData) SetId(id string)

type KeyNotFoundError

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

KeyNotFoundError is returned from Find, Scan, and Query functions if the model you are trying to find does not exist in the database.

func NewKeyNotFoundError

func NewKeyNotFoundError(key string, modelType reflect.Type) *KeyNotFoundError

func (*KeyNotFoundError) Error

func (e *KeyNotFoundError) Error() string

type MarshalerUnmarshaler added in v0.4.0

type MarshalerUnmarshaler interface {
	Marshal(v interface{}) ([]byte, error)      // Return a byte-encoded representation of v
	Unmarshal(data []byte, v interface{}) error // Parse byte-encoded data and store the result in the value pointed to by v.
}

Interface MarshalerUnmarshaler defines a handler for marshaling arbitrary data structures into a byte format and unmarshaling bytes into arbitrary data structures. Any struct which correctly implements the interface should have the property that what you put in using Marshal is identical to what you get out using Unmarshal.

type Model

type Model interface {
	GetId() string
	SetId(string)
}

Model is an interface encapsulating anything that can be saved. Any struct which includes an embedded DefaultData field satisfies the Model interface.

func FindById

func FindById(modelName, id string) (Model, error)

FindById gets a model from the database. It returns an error if a model with that id does not exist or if there was a problem connecting to the database. By default modelName should be the string version of the type of model (without the asterisk, ampersand, or package prefix). If you used RegisterName instead of Register, modelName should be the custom name you used.

func MFindById added in v0.4.0

func MFindById(modelNames, ids []string) ([]Model, error)

MFindById is like FindById but accepts a slice of model names and ids and returns a slice of models. It executes the commands needed to retrieve the models in a single transaction. See http://redis.io/topics/transactions. The slice of modelNames and ids should be properly aligned so that, e.g., modelNames[0] corresponds to ids[0]. If there is an error in the middle of the transaction, the function will halt and return the models retrieved so far (as well as the error).

func Models added in v0.3.0

func Models(in interface{}) []Model

Models converts an interface to a slice of Model. It is typically used to convert a return value of a Query. Will panic if the type is invalid.

type ModelNameNotRegisteredError

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

ModelNameNotRegisteredError is returned if you attempt to perform certain operations for unregistered names.

func NewModelNameNotRegisteredError

func NewModelNameNotRegisteredError(name string) *ModelNameNotRegisteredError

func (*ModelNameNotRegisteredError) Error

type ModelNotFoundError added in v0.6.0

type ModelNotFoundError struct{}

ModelNotFoundError is returned from ScanOne and RunOne if a model that matches the query criteria was not found.

func NewModelNotFoundError added in v0.6.0

func NewModelNotFoundError() *ModelNotFoundError

func (*ModelNotFoundError) Error added in v0.6.0

func (e *ModelNotFoundError) Error() string

type ModelTypeNotRegisteredError

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

ModelTypeNotRegisteredError is returned if you attempt to perform certain operations for unregistered types.

func NewModelTypeNotRegisteredError

func NewModelTypeNotRegisteredError(typ reflect.Type) *ModelTypeNotRegisteredError

func (*ModelTypeNotRegisteredError) Error

type NameAlreadyRegisteredError

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

NameAlreadyRegisteredError is returned if you try to register a name which has already been registered.

func NewNameAlreadyRegisteredError

func NewNameAlreadyRegisteredError(name string) *NameAlreadyRegisteredError

func (*NameAlreadyRegisteredError) Error

type Query added in v0.3.0

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

Query represents a query which will retrieve some models from the database. A Query may consist of one or more query modifiers and can be run in several different ways with different query finishers.

func NewQuery added in v0.4.0

func NewQuery(modelName string) *Query

NewQuery is used to construct a query. modelName should be the name of a registered model. The query returned can be chained together with one or more query modifiers, and then executed using the Run, Scan, Count, or IdsOnly methods. If no query modifiers are used, running the query will return all models that match the type corresponding to modelName in uspecified order. NewQuery will set an error on the query if modelName is not the name of some registered model type. The error, same as any other error that occurs during the lifetime of the query, is not returned until the Query is executed. When the query is executed the first error that occured during the lifetime of the query object (if any) will be returned.

func (*Query) Count added in v0.4.0

func (q *Query) Count() (int, error)

Count counts the number of models that would be returned by the query without actually retreiving the models themselves. Count will also return the first error that occured during the lifetime of the query object (if any). Otherwise, the second return value will be nil.

func (*Query) Exclude added in v0.4.0

func (q *Query) Exclude(fields ...string) *Query

Exclude specifies one or more field names which will *not* be read from the database and scanned. Any other fields *will* be read and scanned into the resulting models when the query is run. You can only use one of Include or Exclude, not both on the same query. Exclude will set an error if you try to use it with Include on the same query. The error, same as any other error that occurs during the lifetime of the query, is not returned until the Query is executed. When the query is executed the first error that occured during the lifetime of the query object (if any) will be returned.

func (*Query) Filter added in v0.4.0

func (q *Query) Filter(filterString string, value interface{}) *Query

Filter applies a filter to the query, which will cause the query to only return models with attributes matching the expression. filterString should be an expression which includes a fieldName, a space, and an operator in that order. Operators must be one of "=", "!=", ">", "<", ">=", or "<=". You can only use Filter on fields which are indexed, i.e. those which have the `zoom:"index"` struct tag. If multiple filters are applied to the same query, the query will only return models which have matches for ALL of the filters. I.e. applying multiple filters is logially equivalent to combining them with a AND or INTERSECT operator. Filter will set an error on the query if the arguments are improperly formated, if the field you are attempting to filter is not indexed, or if the type of value does not match the type of the field. The error, same as any other error that occurs during the lifetime of the query, is not returned until the Query is executed. When the query is executed the first error that occured during the lifetime of the query object (if any) will be returned.

func (*Query) IdsOnly added in v0.4.0

func (q *Query) IdsOnly() ([]string, error)

IdsOnly returns only the ids of the models without actually retreiving the models themselves. IdsOnly will also return the first error that occured during the lifetime of the query object (if any). Otherwise, the second return value will be nil.

func (*Query) Include added in v0.4.0

func (q *Query) Include(fields ...string) *Query

Include specifies one or more field names which will be read from the database and scanned into the resulting models when the query is run. Field names which are not specified in Include will not be read or scanned. You can only use one of Include or Exclude, not both on the same query. Include will set an error if you try to use it with Exclude on the same query. The error, same as any other error that occurs during the lifetime of the query, is not returned until the Query is executed. When the query is executed the first error that occured during the lifetime of the query object (if any) will be returned.

func (*Query) Limit added in v0.4.0

func (q *Query) Limit(amount uint) *Query

Limit specifies an upper limit on the number of records to return. If amount is 0, no limit will be applied. The default value is 0.

func (*Query) Offset added in v0.4.0

func (q *Query) Offset(amount uint) *Query

Offset specifies a starting index (inclusive) from which to start counting records that will be returned. The default value is 0.

func (*Query) Order added in v0.4.0

func (q *Query) Order(fieldName string) *Query

Order specifies a field by which to sort the records and the order in which records should be sorted. fieldName should be a field in the struct type specified by the modelName argument in the query constructor. By default, the records are sorted by ascending order. To sort by descending order, put a negative sign before the field name. Zoom can only sort by fields which have been indexed, i.e. those which have the `zoom:"index"` struct tag. However, in the future this may change. Only one order may be specified per query. However in the future, secondary orders may be allowed, and will take effect when two or more models have the same value for the primary order field. Order will set an error on the query if the fieldName is invalid, if another order has already been applied to the query, or if the fieldName specified does not correspond to an indexed field. The error, same as any other error that occurs during the lifetime of the query, is not returned until the Query is executed. When the query is executed the first error that occured during the lifetime of the query object (if any) will be returned.

func (*Query) Run added in v0.3.0

func (q *Query) Run() (interface{}, error)

Run executes the query and returns the results in the form of an interface. The true type of the return value will be a slice of pointers to some regestired model type. If you need a type-safe way to run queries, look at the Scan method. Run will also return the first error that occured during the lifetime of the query object (if any). Otherwise, the second return value will be nil.

func (*Query) RunOne added in v0.6.0

func (q *Query) RunOne() (interface{}, error)

RunOne is exactly like Run, returns only the first model that fits the query criteria, or if no models fit the critera, returns an error. If you need to do this in a type-safe way, look at the ScanOne method.

func (*Query) Scan added in v0.4.0

func (q *Query) Scan(in interface{}) error

Scan (like Run) executes the query but instead of returning the results it attempts to scan the results into in. The type of in should be a pointer to a slice of pointers to a registered model type. Scan will return the first error that occured during the lifetime of the query object (if any), or will return an error if you provided an interface with an invalid type. Otherwise, the return value will be nil.

func (*Query) ScanOne added in v0.6.0

func (q *Query) ScanOne(in interface{}) error

ScanOne is exactly like Scan but scans only the first model that fits the query criteria. If no model fits the criteria, an error will be returned. The type of in should be a pointer to a slice of pointers to a registered model type.

func (*Query) String added in v0.5.0

func (q *Query) String() string

String returns a string representation of the query and its modifiers

type TypeAlreadyRegisteredError

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

TypeAlreadyRegisteredError is returned if you try to register a type which has already been registered.

func NewTypeAlreadyRegisteredError

func NewTypeAlreadyRegisteredError(typ reflect.Type) *TypeAlreadyRegisteredError

func (*TypeAlreadyRegisteredError) Error

Jump to

Keyboard shortcuts

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