rds

package module
v0.0.0-...-d2db416 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2021 License: MIT Imports: 15 Imported by: 0

README

REST Layer Redis Backend

Build Status codecov.io GitHub

Status:

Library is in the early alpha stage.

Usage

Storage uses Lua scripting, available only since Redis version 2.6 or greater.

import (
    "github.com/go-redis/redis"
    "github.com/kolotaev/rest-layer-redis"
)

Create a redis client:

client := redis.NewClient(&redis.Options{
    Addr:     "localhost:6379",
    // Any other viable config values here
})

// Check availability of the Redis server
pong, err := client.Ping().Result()

Create entities of your domain and a resource storage handlers for them:

user := schema.Schema{
		Description: `The User model`,
		Fields: schema.Fields{
			"name": {
				Required: true,
				ReadOnly: true,
				OnInit: schema.NewID,
				Filterable: true,
				Sortable:   true,
			},
			"age": {
				Required:   true,
				Filterable: true,
			},
		},
	}
usersHandler := redis.NewHandler(client, "user", user)

posts := schema.Schema{/* ... */}
postsHandler := rds.NewHandler(client, "posts", posts)

Use this handler with a resource:

index.Bind("users", user, usersHandler, resource.DefaultConf)
index.Bind("posts", posts, postsHandler, resource.DefaultConf)

You may want to create many Redis handlers as you have resources as long as you want each resources in a different collection. You can share the same Redis session across all you handlers.

Things you should be aware of

  • Under the hood storage handler creates secondary indices inside Redis for proper filtering support. These indices are created/updated/deleted for every Filterable field on every entity record. You should no worry about it, but don't be confused if you see some unknown sets in Redis explorer.

  • Storage handler heavily relies on types of resource fields to process results retrieved from Redis. So it's better you specify Validator type for every field - otherwise results coerced to string.

  • Sorting by more than 1 field is not supported due to Redis query semantics restriction.

License

Copyright © 2017-2019 Egor Kolotaev.

Distributed under MIT License.

Documentation

Overview

Example
package main

import (
	"log"
	"net/http"

	"github.com/go-redis/redis"
	"github.com/rs/cors"
	"github.com/rs/rest-layer/resource"
	"github.com/rs/rest-layer/rest"
	"github.com/rs/rest-layer/schema"

	"github.com/kolotaev/rest-layer-redis"
)

var (
	user = schema.Schema{
		Fields: schema.Fields{
			"id":      schema.IDField,
			"created": schema.CreatedField,
			"updated": schema.UpdatedField,
			"name": {
				Required:   true,
				Filterable: true,
				Sortable:   true,
				Validator: &schema.String{
					MaxLen: 150,
				},
			},
		},
	}

	// Define a post resource schema
	post = schema.Schema{
		Fields: schema.Fields{
			"id":      schema.IDField,
			"created": schema.CreatedField,
			"updated": schema.UpdatedField,
			"user": {
				Required:   true,
				Filterable: true,
				Validator: &schema.Reference{
					Path: "users",
				},
			},
			"public": {
				Filterable: true,
				Validator:  &schema.Bool{},
			},
			"meta": {
				Schema: &schema.Schema{
					Fields: schema.Fields{
						"title": {
							Required: true,
							Validator: &schema.String{
								MaxLen: 150,
							},
						},
						"body": {
							Validator: &schema.String{
								MaxLen: 100000,
							},
						},
					},
				},
			},
		},
	}
)

func main() {
	client := redis.NewClient(&redis.Options{
		Addr: "127.0.0.1:6379",
	})

	_, err := client.Ping().Result()
	if err != nil {
		panic(err)
	}

	index := resource.NewIndex()

	users := index.Bind("users", user, rds.NewHandler(client, "users", user), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	users.Bind("posts", "user", post, rds.NewHandler(client, "posts", post), resource.Conf{
		AllowedModes: resource.ReadWrite,
	})

	api, err := rest.NewHandler(index)
	if err != nil {
		log.Fatalf("Invalid API configuration: %s", err)
	}

	http.Handle("/", cors.New(cors.Options{OptionsPassthrough: true}).Handler(api))

	log.Print("Serving API on http://localhost:8080")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}
}
Output:

Index

Examples

Constants

View Source
const (
	// TODO - Do we need them if we marshall everything?
	ETagField = "_etag"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

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

Handler handles resource storage in Redis.

func NewHandler

func NewHandler(c *redis.Client, entityName string, schema schema.Schema) *Handler

NewHandler creates a new redis handler

func (Handler) Clear

func (h Handler) Clear(ctx context.Context, q *query.Query) (int, error)

Clear purges all items from Redis matching the query

func (Handler) Delete

func (h Handler) Delete(ctx context.Context, item *resource.Item) error

Delete deletes an item from Redis

func (Handler) Find

func (h Handler) Find(ctx context.Context, q *query.Query) (*resource.ItemList, error)

Find items from Redis matching the provided query

func (*Handler) Insert

func (h *Handler) Insert(ctx context.Context, items []*resource.Item) error

Insert inserts new items in the Redis database

func (Handler) Update

func (h Handler) Update(ctx context.Context, item *resource.Item, original *resource.Item) error

Update updates item properties in Redis

type ItemManager

type ItemManager struct {
	EntityName string
	// TODO - not needed with json
	FieldNames []string
	// needed to determine what secondary indices we are going to create to allow filtering (see predicate.go).
	Filterable []string
	// needed for SORT type determination.
	Numeric  []string
	Sortable []string
}

func (*ItemManager) AddIDToAllIDsSet

func (im *ItemManager) AddIDToAllIDsSet(pipe redis.Pipeliner, i *resource.Item)

TODO - generalize to secondary idxs? AddIDToAllIDsSet adds item's ID to a set of all stored IDs

func (*ItemManager) AddSecondaryIndices

func (im *ItemManager) AddSecondaryIndices(pipe redis.Pipeliner, item *resource.Item)

AddSecondaryIndices adds: - new values to a secondary index for a given item. - index names to a maintained auxiliary list of item's indices. Action is appended to a Redis pipeline.

func (*ItemManager) DeleteIDFromAllIDsSet

func (im *ItemManager) DeleteIDFromAllIDsSet(pipe redis.Pipeliner, i *resource.Item)

DeleteIDFromAllIDsSet removes item's ID from a set of all stored IDs

func (*ItemManager) DeleteSecondaryIndices

func (im *ItemManager) DeleteSecondaryIndices(pipe redis.Pipeliner, item *resource.Item)

DeleteSecondaryIndices removes: - a secondary index for a given item. - index names to a maintained auxiliary list of item's indices. Action is appended to a Redis pipeline.

func (*ItemManager) IndexSetKeys

func (im *ItemManager) IndexSetKeys(i *resource.Item) []string

IndexSetKeys returns a secondary index keys for a resource's filterable fields suited for SET. Is used so that we can find them when needed. Ex: for user A returns ["users:hair:brown", "users:city:NYC"]

for user B returns ["users:hair:red", "users:city:Boston"]

func (*ItemManager) IndexZSetKeys

func (im *ItemManager) IndexZSetKeys(i *resource.Item) map[string]float64

IndexZSetKeys returns a secondary index keys for a resource's filterable fields suited for ZSET. Is used so that we can find them when needed. Ex: for user A returns {"users:age": 24, "users:salary": 75000}

for user B returns {"users:age": 56, "users:salary": 125000}

func (*ItemManager) NewItem

func (im *ItemManager) NewItem(data []interface{}) *resource.Item

NewItem converts a Redis item from DB into resource.Item

func (*ItemManager) NewRedisItem

func (im *ItemManager) NewRedisItem(i *resource.Item) (string, map[string]interface{})

NewRedisItem converts a resource.Item into a suitable for go-redis HMSet [key, value] pair

func (*ItemManager) RedisItemKey

func (im *ItemManager) RedisItemKey(i *resource.Item) string

RedisItemKey returns a redis-compatible string key to denote a Hash key of an item. E.g. 'users:1234'.

type LuaQuery

type LuaQuery struct {
	// Script that will be executed on Redis Lua engine
	Script string
	// LastKey is the key where the ids against which the final query will be executed.
	LastKey string
	// AllKeys are temporary keys created in Redis during Query building process.
	// They should be eventually deleted after query returned some result.
	AllKeys []string
}

LuaQuery represents a result of building Redis select query as a Lua script

Jump to

Keyboard shortcuts

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