query

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Dec 17, 2020 License: MIT Imports: 10 Imported by: 0

README

URI to MongoDB Query

Go Reference Build Status Go Report Maintainability Test Coverage License

The URI to MongoDB query conversion library for Go programming language .

The library is deeply inspired by the query-params-mongo NodeJS library.

Installation

The recommended way to get started using the URI to MongoDB Query library is by using go modules to install the dependency in your project. This can be done by importing packages from github.com/garantexpress/mongo-uri-query and having the build step to install the dependency.

Another way is to get the library by explicitly running

go get github.com/garantexpress/mongo-uri-query

Usage

Example

A request of the form:

/employees?name=John&age__lte=45&category__in=A,B&__limit=10&__sort=-age

Is translated to:

Query{
    Filter: map[string]interface{}{
        "name":     "John",
        "age":      map[string]interface{}{"$lte": 45},
        "category": map[string]interface{}{$in: []interface{}{"A", "B"}},
    },
    Sort:  map[string]int{ "age": -1 },
    Limit: 10,
    Skip:  0,
}

Now the filter, sort, limit and skip can be alltogether used with mongo-go-driver or other MongoDB library, i.e. with an old go-mgo/mgo library.

go-mgo/mgo package.

package example

import (
	"errors"
	"net/http"

	query "github.com/Denisss025/mongo-uri-query"

	"gopkg.in/mgo.v2/bson"
	"gopkg.in/mgo.v2/mgo"
)

type primitives struct{}

func (p primitives) ObjectID(val string) (oid interface{}, err error) {
	if !bson.IsObjectIdHex(val) {
		return nil, errors.New("not an ObjectIdHex")
    }
    
    return bson.ObjectIdHex(val), nil
}

func (p primitives) RegEx(p, o string) (re interface{}, err error) {
	return bson.RegEx{Pattern: p, Options: o}
}

func (p primitives) DocElem(k string, v interface{}) (
	kv interface{}, err error) {
	return bson.DocElem{Name: k, Value: v}, nil
}

type RequestHandler struct {
	parser query.Parser
}

func NewHandler() *RequestHandler {
	return &RequestHandler{parser: query.Parser{
		Converter: query.NewDefaultConverter(primitives{}),
	}}
}

func (h *RequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var coll *mgo.Collection
	...
	q, err := h.parser.Parse(r.URL.Query())
	if err != nil { ... }

	cursor, err := coll.Find(q.Filter).Limit(q.Limit).Skip(q.Skip).Sort(q.Sort)
	if err != nil { ... }

	...
}

MongoDB driver

package example

import (
	"errors"
	"net/http"

	query "github.com/Denisss025/mongo-uri-query"

	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type primitives struct{}

func (p primitives) ObjectID(val string) (oid interface{}, err error) {
	return primitive.ObjectIDFromHex(val)
}

func (p primitives) RegEx(p, o string) (re interface{}, err error) {
    return primitive.Regex{Pattern: p, Options: o}, nil
}

func (p primitives) DocElem(k string, v interface{}) (
	kv interface{}, err error) {
	return primitive.E{Key: k, Value: v}, nil
}

type RequestHandler struct {
	parser query.Parser
}

func NewHandler() *RequestHandler {
	return &RequestHandler{parser: query.Parser{
		Converter: query.NewDefaultConverter(primitives{}),
	}}
}

func (h *RequestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var coll *mongo.Collection
	...
	q, err := h.parser.Parse(r.URL.Query())
	if err != nil { ... }

	cursor, err := coll.Find(r.Context(), q.Filter, &options.FindOptions{
		Limit: &q.Limit,
		Skip:  &q.Skip,
		Sort:  q.Sort,
	})

	...
}

API Reference

Create a parser

The parser is a structure that has a Parse() function that can process the request query. You can create as many instances as you like, but typically your app would need only one. The behaviour of the parser is controlled with TypeConverter, Fields and ValidateFields member fields.

parser := query.Parser{TypeConverter: ..., Fields: ..., ValidateFields: ...}
Fields
  • TypeConverter is a structure that is able to automatically detect value type and convert string to it.

  • Fields is a map that holds fields specifications:

    • Required: the parser checks all the required fields to be given in a query.

    • Converter is a custom type converter for a given field.

  • ValidateFields: when true the parser checks every given query param to be present in the Fields map.

The TypeConverter can be created either with NewConverter() or with NewDefaultConverter() functions. The NewDefaultConverter() function creates a TypeConverter that automatically detects such types as ObjectID ([0-9a-f]{12}), int64, float64, bool (true|yes|false|no) and time.Time (i.e. 2006-01-02T15:04:05Z0700).

The TypeConverter also has a Primitives field which is used to convert strings to ObjectID and RegEx. Primitives is an interface with two functions:

type Primitives interface {
    RegEx(val, opts string) (interface{}, error)
    ObjectID(val string) (interface{}, error)
    DocElem(key string, val interface{}) (interface{}, error)
}

The RegEx() function is used with re, co and sw operators.

The DocElem() function is used with __sort directive. It allows to define sort order for Sort() function or for FindOptions.Sort field.

Parse a query
q, err := parser.Parse(r.URL.Query())

r is a pointer to an http.Request{}, q is a Query{}.

The Query{} structure has Filter, Sort, Limit and Skip fields.

  • Filter is a mongo-db find filter.

  • Sort is a mongo-db sort specification.

  • Limit is a value for Cursor.Limit() to limit the number of documents in the query result.

  • Skip is a value for Cursor.Skip() to skip the number of documents in the query result.

License

The URI to MongoDB Query library is licensed under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNoMatch is returned when the converter cannot match a string
	// value with any pattern.
	ErrNoMatch = errors.New("does not match")
	// ErrUnknownOperator is returned when an unknown operator is found.
	ErrUnknownOperator = errors.New("unknown operator")
	// ErrNoFieldSpec is returned when there is no field specification, but
	// the field is present in a query.
	ErrNoFieldSpec = errors.New("no field spec")
	// ErrNoConverter is returned when there is no converter found.
	ErrNoConverter = errors.New("no converter")
	// ErrNoSortField is returned when there is no field specification for
	// the sort field from the query.
	ErrNoSortField = errors.New("no sort field spec")
	// ErrMissingField is returned when some required field is missing in
	// the query.
	ErrMissingField = errors.New("missing required filter on field")
	// ErrTooManyValues is returned when a single value operator is assigned
	// to multiple values.
	ErrTooManyValues = errors.New("too many values")
)

Functions

This section is empty.

Types

type ConvertFunc

type ConvertFunc func(val string) (i interface{}, err error)

ConvertFunc is a function to check and convert a string val.

func Bool

func Bool() (convert ConvertFunc)

Bool tries to convert a val string to a boolean value.

func Date

func Date() (convert ConvertFunc)

Date checks if a string matches some of the known patterns and tries to convert it to time.Time.

func Double

func Double() (convert ConvertFunc)

Double tries to convert a val string to a float64 value.

func Int

func Int() (convert ConvertFunc)

Int tries to convert a val string to an int value.

func ObjectID

func ObjectID(primitive Primitives) (convert ConvertFunc)

ObjectID checks if a string can be converted to an ObjectID value and converts it.

func String

func String() (convert ConvertFunc)

String returns a string val.

func (ConvertFunc) Convert

func (c ConvertFunc) Convert(val string) (i interface{}, err error)

Convert calls ConvertFunc itself.

type Converter

type Converter interface {
	// Convert checks a string val and converts it when possible to some
	// type.
	Convert(val string) (i interface{}, err error)
}

Converter is an interface that converts strings to known types.

type Field

type Field struct {
	// Converter defines a type of the field.
	Converter Converter
	// Required defines if the field is required.
	Required bool
}

Field is a structure that holds field specification.

type Fields

type Fields map[string]Field

Fields is a map with fields specifications.

func (Fields) Converter

func (f Fields) Converter(name string) (converter Converter, ok bool)

Converter returns a specified converter for a field with a given name.

func (Fields) HasField

func (f Fields) HasField(name string) (ok bool)

HasField check if a field with a given name is present in the fields specifications.

func (Fields) IsRequired

func (f Fields) IsRequired(name string) (ok bool)

IsRequired returns true it a field with a given name is specified and is required.

type M

type M = map[string]interface{}

M is an alias for map[string]interface{}.

type Parser

type Parser struct {
	// Converter is a TypeConverter that converts unspecified fields.
	Converter *TypeConverter
	// Fields is a fields specification.
	Fields Fields
	// ValidateFields enables or disables field specification validator.
	// When true, the parser will return ErrNoFieldSpec for every
	// unspecified field in url query.
	ValidateFields bool
	// contains filtered or unexported fields
}

Parser is a structure that parses url queries.

func (*Parser) Parse

func (p *Parser) Parse(params url.Values) (filter Query, err error)

Parse parses a given url query.

type Primitives

type Primitives interface {
	// RegEx converts pattern and options to bson.Regex.
	RegEx(pattern, options string) (rx interface{}, err error)
	// ObjectID converts val to bson.ObjectID.
	ObjectID(val string) (oid interface{}, err error)
	// DocElem converts key and val o bson.DocElem, which is a bson.D element.
	DocElem(key string, val interface{}) (d interface{}, err error)
}

Primitives gives access to the RegEx and ObjectID convertors.

type Query

type Query struct {
	// Filter is a document containing query operators.
	Filter M
	// Sort is a document specifying the order in which documents should
	// be returned.
	Sort interface{}
	// Limit is the maximum number of documents to return.
	Limit int64
	// Skip is a number of documents to be skipped before adding documents
	// to the results.
	Skip int64
}

Query is a structure that holds information about DB request.

func (*Query) AddFilter

func (f *Query) AddFilter(field string, op operator, value interface{})

AddFilter appends an operator, field and value to the filter.

func (*Query) AddSort

func (f *Query) AddSort(val string,
	docElem func(string, interface{}) (interface{}, error)) (
	fieldName string, err error)

AddSort adds a field to sort to the Sort document.

type TypeConverter

type TypeConverter struct {
	// Bool is a boolean type converter
	Bool ConvertFunc
	// Primitives gives access to mongodb-driver primitives.
	Primitives Primitives
	// Funcs checks and converts strings to known types.
	Funcs []ConvertFunc
}

TypeConverter is a type that detects type and converts strings to that type.

func NewConverter

func NewConverter(boolConvert ConvertFunc, p Primitives,
	convert ...ConvertFunc) (c *TypeConverter)

NewConverter creates an instance of the TypeConverter.

func NewDefaultConverter

func NewDefaultConverter(p Primitives) (c *TypeConverter)

NewDefaultConverter creates a default TypeConverter instance.

func (TypeConverter) Convert

func (c TypeConverter) Convert(val string) (i interface{}, err error)

Convert checks string value for patterns and converts it to matched types.

Jump to

Keyboard shortcuts

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