Categorygithub.com/Denisss025/mongo-uri-query
repositorypackage
0.3.1
Repository: https://github.com/denisss025/mongo-uri-query.git
Documentation: pkg.go.dev

# 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/Denisss025/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/Denisss025/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.