diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 0000000..7b7cd9a
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,24 @@
+name: Go
+
+on:
+  push:
+    branches: [ main ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Go
+        uses: actions/setup-go@v4
+        with:
+          go-version: '1.21.x'
+      - name: Install dependencies
+        run: go get .
+      - name: Build
+        run: go build -v ./...
+      - name: Test & prepare coverage
+        run: go test -coverprofile=coverage.out
+      - name: Upload coverage to Codecov
+        uses: codecov/codecov-action@v3
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..fa92975
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.out
\ No newline at end of file
diff --git a/README.md b/README.md
index 9b90104..6084762 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,136 @@
-# mgs
-Convert URL query parameters to MongoDB queries
+[![MIT License][license-shield]][license-url]
+
+<p align="center">
+  <h3 align="center">Mongo Golang Search</h3>
+  <p align="center">
+    Mongo Golang Search provides a query language to a MongoDB database.
+    <br />
+    <a href="https://github.com/ajclopez/mgs#usage"><strong>Explore the docs</strong></a>
+    <br />
+    <br />
+    <a href="https://github.com/ajclopez/mgs/issues">Report Bug</a>
+    ยท
+    <a href="https://github.com/ajclopez/mgs/issues">Request Feature</a>
+  </p>
+</p>
+
+# Mongo Golang Search (mgs)
+
+
+### Content index
+
+* [What is this?](#what-is-this)
+* [Getting Started](#getting-started)
+    *  [Installation](#installation)
+* [Usage](#usage)
+* [Supported features](#supported-features)
+    * [Filtering](#filtering)
+    * [Pagination](#pagination)
+    * [Sorting](#sorting)
+    * [Projection](#projection)
+* [Available options](#available-options)
+    * [Customize limit value](#customize-limit-value)
+    * [Specify casting per param keys](#specify-casting-per-param-keys)
+* [Contributing](#contributing)
+* [License](#license)
+
+## What is this?
+
+Mongo Golang Search provides a simple query language to perform advanced searches for your collections in **MongoDB**.
+
+You could also use **Mongo Golang Search** to searching, sorting, pagination and combining logical operators.
+
+## Getting Started
+
+### Installation
+
+
+## Usage
+
+
+## Supported features
+
+### Filtering
+
+| Operator      	| URI               	| Example                        	|
+| -----------------	| ---------------------	| ---------------------------------	|
+| `$eq`          	| `key=val`				| `type=public`        				|
+| `$ne`          	| `key!=val`        	| `status!=SENT`                    |
+| `$gt`          	| `key>val`             | `price>5`                        |
+| `$gte`         	| `key>=val`            | `price>=9`                       |
+| `$lt`          	| `key<val`             | `date<2020-01-01T14:00:00.000Z`   |
+| `$lte`         	| `key<=val`            | `priority<=-5`                    |
+| `$in`          	| `key=val1,val2`       | `status=QUEUED,DEQUEUED`          |
+| `$nin`          	| `key!=val1,val2`      | `status!=QUEUED,DEQUEUED`         |
+| `$exists`         | `key`          		| `email`              				|
+| `$exists`         | `!key`         		| `!email`                    		|
+| `$regex`      	| `key=/value/<opts>`	| `email=/@gmail\.com$/`			|
+| `$regex`        	| `key!=/value/<opts>`  | `phone!=/^58/`                    |
+
+
+### Pagination
+
+Useful to limit the number of records returned.
+
+- Operator keys are `skip` and `limit`.
+- Use `limit` operator to limit the number of records returned.
+- Use `skip` operator to skip the specified number of records.
+
+```json
+skip=20&limit=10
+```
+
+### Sorting
+
+Useful to sort returned records.
+
+- Operator key is `sort`.
+- It accepts a comma-separated list of fields.
+- Use `-` prefixes to sort in descending order.
+- Use `+` prefixes to sort in ascedending order.
+
+```json
+sort=id,-date
+```
+
+### Projection
+
+Useful to limit fields to return in each records.
+
+- Operator key is `fields`.
+- It accepts a comma-separated list of fields.
+
+```json
+fields=firstname,lastname,phone,email
+```
+
+**Note:**
+* The `_id` field (returned by default).
+
+## Available options
+
+You can use advanced options:
+
+### Customize limit value
+
+
+### Specify casting per param keys
+
+
+## Contributing
+
+Should you like to provide any feedback, please open up an Issue, I appreciate feedback and comments. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your feature branch (`git checkout -b feature/amazing-feature`)
+3. Commit your changes (`git commit -m 'Add some amazing-feature'`)
+4. Push to the branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+## License
+
+This software is released under the MIT license. See `LICENSE` for more information.
+
+
+[license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg
+[license-url]: https://github.com/ajclopez/mgs/blob/master/LICENSE
diff --git a/convert.go b/convert.go
new file mode 100644
index 0000000..82e00e5
--- /dev/null
+++ b/convert.go
@@ -0,0 +1,57 @@
+package mgs
+
+import (
+	"reflect"
+)
+
+// Convert converts the criteria value to a MongoDB query
+func Convert(criteria SearchCriteria, filter map[string]interface{}) {
+
+	value := ParseValue(criteria.Value, criteria.Caster)
+
+	switch criteria.Operation {
+	case EQUAL:
+		key := reflect.ValueOf(value).Kind()
+		if key == reflect.Slice {
+			filter[criteria.Key] = buildMongoQuery("$in", value)
+		} else if key == reflect.Struct {
+			filter[criteria.Key] = buildRegexOperation(value)
+		} else {
+			filter[criteria.Key] = value
+		}
+	case NOT_EQUAL:
+		key := reflect.ValueOf(value).Kind()
+		if key == reflect.Slice {
+			filter[criteria.Key] = buildMongoQuery("$nin", value)
+		} else if key == reflect.Struct {
+			filter[criteria.Key] = buildMongoQuery("$not", buildRegexOperation(value))
+		} else {
+			filter[criteria.Key] = buildMongoQuery("$ne", value)
+		}
+	case GREATER_THAN:
+		filter[criteria.Key] = buildMongoQuery("$gt", value)
+	case GREATER_THAN_EQUAL:
+		filter[criteria.Key] = buildMongoQuery("$gte", value)
+	case LESS_THAN:
+		filter[criteria.Key] = buildMongoQuery("$lt", value)
+	case LESS_THAN_EQUAL:
+		filter[criteria.Key] = buildMongoQuery("$lte", value)
+	case EXISTS:
+		filter[criteria.Key] = buildMongoQuery("$exists", !criteria.Prefix)
+	}
+}
+
+func buildMongoQuery(operator string, value interface{}) map[string]interface{} {
+	query := make(map[string]interface{})
+	query[operator] = value
+
+	return query
+}
+
+func buildRegexOperation(value interface{}) map[string]interface{} {
+	regex := make(map[string]interface{})
+	regex["$regex"] = value.(Regex).Pattern
+	regex["$options"] = value.(Regex).Option
+
+	return regex
+}
diff --git a/convert_test.go b/convert_test.go
new file mode 100644
index 0000000..be376e6
--- /dev/null
+++ b/convert_test.go
@@ -0,0 +1,131 @@
+package mgs
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var converetTests = []struct {
+	Criteria SearchCriteria
+	Filter   map[string]interface{}
+	Expected map[string]interface{}
+}{
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "name",
+			Operation: EQUAL,
+			Value:     "Jhon",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"name": "Jhon"},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "status",
+			Operation: EQUAL,
+			Value:     "QUEUED,DEQUEUED",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"status": map[string]interface{}{"$in": []interface{}{"QUEUED", "DEQUEUED"}}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "email",
+			Operation: EQUAL,
+			Value:     "/@gmail\\.com$/",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"email": map[string]interface{}{"$regex": "@gmail\\.com$", "$options": ""}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "status",
+			Operation: NOT_EQUAL,
+			Value:     "SENT",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"status": map[string]interface{}{"$ne": "SENT"}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "status",
+			Operation: NOT_EQUAL,
+			Value:     "QUEUED,DEQUEUED",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"status": map[string]interface{}{"$nin": []interface{}{"QUEUED", "DEQUEUED"}}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "phone",
+			Operation: NOT_EQUAL,
+			Value:     "/^58/",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"phone": map[string]interface{}{"$not": map[string]interface{}{"$regex": "^58", "$options": ""}}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "price",
+			Operation: GREATER_THAN,
+			Value:     "5",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"price": map[string]interface{}{"$gt": int64(5)}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "price",
+			Operation: GREATER_THAN_EQUAL,
+			Value:     "5",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"price": map[string]interface{}{"$gte": int64(5)}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "price",
+			Operation: LESS_THAN,
+			Value:     "5",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"price": map[string]interface{}{"$lt": int64(5)}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    false,
+			Key:       "price",
+			Operation: LESS_THAN_EQUAL,
+			Value:     "5",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"price": map[string]interface{}{"$lte": int64(5)}},
+	},
+	{
+		SearchCriteria{
+			Prefix:    true,
+			Key:       "email",
+			Operation: EXISTS,
+			Value:     "",
+		},
+		map[string]interface{}{},
+		map[string]interface{}{"email": map[string]interface{}{"$exists": false}},
+	},
+}
+
+func TestShouldConvertFromSearchCriteria(t *testing.T) {
+	for _, test := range converetTests {
+		Convert(test.Criteria, test.Filter)
+		assert.Equal(t, test.Expected, test.Filter)
+	}
+}
diff --git a/findoptions.go b/findoptions.go
new file mode 100644
index 0000000..1b161db
--- /dev/null
+++ b/findoptions.go
@@ -0,0 +1,45 @@
+package mgs
+
+type CastType int
+
+// list of allowed cast type.
+const (
+	NUMBER CastType = iota + 1
+	DATE
+	BOOLEAN
+	PATTERN
+	STRING
+)
+
+// FindOptions is a structure that allows to use advanced options.
+type FindOptions struct {
+	// Caster map to set object type on values by key (BOOLEAN, NUMBER, PATTERN, DATE, STRING).
+	Caster *map[string]CastType
+	// DefaultLimit default value for limit key.
+	DefaultLimit *int64
+	// MaxLimit maximum value for limit key.
+	MaxLimit *int64
+}
+
+// FindOption creates a new FindOptions instance.
+func FindOption() *FindOptions {
+	return &FindOptions{}
+}
+
+// SetCaster sets the value for the object type on values by key.
+func (f *FindOptions) SetCaster(m map[string]CastType) *FindOptions {
+	f.Caster = &m
+	return f
+}
+
+// SetDefaultLimit sets the value for the default value for limit key.
+func (f *FindOptions) SetDefaultLimit(i int64) *FindOptions {
+	f.DefaultLimit = &i
+	return f
+}
+
+// SetMaxLimit sets the value for the maximum value for limit key.
+func (f *FindOptions) SetMaxLimit(i int64) *FindOptions {
+	f.MaxLimit = &i
+	return f
+}
diff --git a/findoptions_test.go b/findoptions_test.go
new file mode 100644
index 0000000..2c14fbe
--- /dev/null
+++ b/findoptions_test.go
@@ -0,0 +1,34 @@
+package mgs
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestShouldCreateInstanceFindOpetions(t *testing.T) {
+
+	assert.Equal(t, &FindOptions{}, FindOption())
+}
+
+func TestShouldCreateInstanceFindOpetionsWithValues(t *testing.T) {
+
+	caster := map[string]CastType{
+		"mobile": STRING,
+	}
+
+	opts := FindOption()
+	opts.SetCaster(caster)
+	opts.SetDefaultLimit(100)
+	opts.SetMaxLimit(100)
+
+	dl := int64(100)
+	ml := int64(100)
+	expected := FindOptions{
+		Caster:       &caster,
+		DefaultLimit: &dl,
+		MaxLimit:     &ml,
+	}
+
+	assert.Equal(t, &expected, opts)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..5b37c67
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module github.com/ajclopez/mgs
+
+go 1.21.2
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/stretchr/testify v1.8.4 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..8cf6655
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,9 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/mgs.go b/mgs.go
new file mode 100644
index 0000000..070c174
--- /dev/null
+++ b/mgs.go
@@ -0,0 +1,118 @@
+package mgs
+
+import (
+	"errors"
+	"net/url"
+	"strings"
+)
+
+var (
+	// ErrUnescapeCharacters is returned when cannot converter an unescape string
+	ErrUnescapeCharacters = errors.New("unescape characters")
+	// ErrValueNoMatch is returned when the converter cannot match a string to
+	// an unsigned integer.
+	ErrValueNoMatch = errors.New("value does not match")
+)
+
+// MongoGoSearch converts query into a MongoDB query object.
+func MongoGoSearch(query string, opts *FindOptions) (Query, error) {
+
+	res := Query{}
+
+	if strings.TrimSpace(query) == "" {
+		return res, nil
+	}
+
+	var err error
+	query, err = url.QueryUnescape(strings.Replace(query, "+", "%2B", -1))
+
+	if err != nil {
+		return res, ErrUnescapeCharacters
+	}
+
+	filter := make(map[string]interface{})
+
+	for _, criteria := range Parse(query, opts.Caster) {
+		switch criteria.Key {
+		case "filter":
+			// advanced queries
+		case "skip":
+			err = parseSkip(&res, criteria.Value)
+		case "limit":
+			err = parseLimit(&res, criteria.Value, opts)
+		case "sort":
+			parseSort(&res, criteria.Value)
+		case "fields":
+			parseFields(&res, criteria.Value)
+		default:
+			Convert(criteria, filter)
+		}
+
+		if err != nil {
+			return res, err
+		}
+	}
+
+	res.Filter = filter
+
+	return res, err
+}
+
+func parseSkip(res *Query, value string) error {
+	skip, err := parseIntValueToInt(value)
+	if err != nil {
+		return err
+	}
+
+	res.Skip = skip
+	return nil
+}
+
+func parseLimit(res *Query, value string, opts *FindOptions) error {
+	limit, err := parseIntValueToInt(value)
+
+	if err != nil {
+		if opts.DefaultLimit != nil {
+			res.Limit = *opts.DefaultLimit
+			return nil
+		} else {
+			return err
+		}
+	}
+
+	maxLimit := opts.MaxLimit
+	if maxLimit != nil && limit > *maxLimit {
+		res.Limit = *maxLimit
+	} else {
+		res.Limit = limit
+	}
+
+	return nil
+}
+
+func parseSort(res *Query, value string) {
+	sort := make(map[string]int)
+	values := strings.Split(value, ",")
+
+	for i := range values {
+		if strings.HasPrefix(values[i], "+") {
+			sort[values[i][1:]] = 1
+		} else if strings.HasPrefix(values[i], "-") {
+			sort[values[i][1:]] = -1
+		} else {
+			sort[values[i]] = 1
+		}
+	}
+
+	res.Sort = sort
+}
+
+func parseFields(res *Query, value string) {
+	projection := make(map[string]int)
+	values := strings.Split(value, ",")
+	for i := range values {
+		projection[values[i]] = 1
+	}
+
+	res.Projection = projection
+}
diff --git a/mgs_test.go b/mgs_test.go
new file mode 100644
index 0000000..f1e3daa
--- /dev/null
+++ b/mgs_test.go
@@ -0,0 +1,223 @@
+package mgs
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+type result struct {
+	Query Query
+	Err   error
+}
+
+var tests = []struct {
+	Input    string
+	Expected result
+}{
+	{
+		"skip=10",
+		result{
+			Query{
+				Filter:     map[string]interface{}{},
+				Sort:       map[string]int(nil),
+				Limit:      0,
+				Skip:       10,
+				Projection: map[string]int(nil),
+			},
+			nil,
+		},
+	},
+	{
+		"skip=number",
+		result{
+			Query{
+				Filter:     map[string]interface{}(nil),
+				Sort:       map[string]int(nil),
+				Limit:      0,
+				Skip:       0,
+				Projection: map[string]int(nil),
+			},
+			ErrValueNoMatch,
+		},
+	},
+	{
+		"limit=25",
+		result{
+			Query{
+				Filter:     map[string]interface{}{},
+				Sort:       map[string]int(nil),
+				Limit:      25,
+				Skip:       0,
+				Projection: map[string]int(nil),
+			},
+			nil,
+		},
+	},
+	{
+		"limit=number",
+		result{
+			Query{
+				Filter:     map[string]interface{}(nil),
+				Sort:       map[string]int(nil),
+				Limit:      0,
+				Skip:       0,
+				Projection: map[string]int(nil),
+			},
+			ErrValueNoMatch,
+		},
+	},
+	{
+		"sort=+date,-age,name",
+		result{
+			Query{
+				Filter: map[string]interface{}{},
+				Sort: map[string]int{
+					"date": 1,
+					"age":  -1,
+					"name": 1,
+				},
+				Limit:      0,
+				Skip:       0,
+				Projection: map[string]int(nil),
+			},
+			nil,
+		},
+	},
+	{
+		"fields=firstname,lastname,email",
+		result{
+			Query{
+				Filter: map[string]interface{}{},
+				Sort:   map[string]int(nil),
+				Limit:  0,
+				Skip:   0,
+				Projection: map[string]int{
+					"firstname": 1,
+					"lastname":  1,
+					"email":     1,
+				},
+			},
+			nil,
+		},
+	},
+	{
+		"city=Madrid&age>=18",
+		result{
+			Query{
+				Filter: map[string]interface{}{
+					"city": "Madrid",
+					"age": map[string]interface{}{
+						"$gte": int64(18),
+					},
+				},
+				Sort:       map[string]int(nil),
+				Limit:      0,
+				Skip:       0,
+				Projection: map[string]int(nil),
+			},
+			nil,
+		},
+	},
+}
+
+func TestReturnDefaultQueryForMongoGoSearchWhenQueryIsEmpty(t *testing.T) {
+	result, err := MongoGoSearch("", &FindOptions{})
+
+	if err != nil {
+		t.Errorf("Error parse url to mongo query search")
+	}
+
+	assert.Equal(t, Query{}, result)
+}
+
+func TestReturnErrorForMongoGoSearchWhenQueryInvalidCharacters(t *testing.T) {
+	result, err := MongoGoSearch("name=Jho%%n", &FindOptions{})
+
+	if err == nil {
+		t.Errorf("Error parse url to mongo query search")
+	}
+
+	assert.Equal(t, Query{}, result)
+}
+
+func TestReturnQuery(t *testing.T) {
+	for _, test := range tests {
+		query, err := MongoGoSearch(test.Input, &FindOptions{})
+
+		assert.Equal(t, test.Expected.Query, query)
+		assert.Equal(t, test.Expected.Err, err)
+	}
+}
+
+func TestReturnQueryUseFindOptionsWithCaster(t *testing.T) {
+
+	caster := map[string]CastType{
+		"mobile": STRING,
+	}
+
+	opts := FindOption()
+	opts.SetCaster(caster)
+
+	expected := result{
+		Query{
+			Filter: map[string]interface{}{
+				"mobile": "+56900000000",
+			},
+			Sort:       map[string]int(nil),
+			Limit:      0,
+			Skip:       0,
+			Projection: map[string]int(nil),
+		},
+		nil,
+	}
+
+	query, err := MongoGoSearch("mobile=+56900000000", opts)
+
+	assert.Equal(t, expected.Query, query)
+	assert.Equal(t, expected.Err, err)
+}
+
+func TestReturnQueryUseFindOptionsWithDefaultLimit(t *testing.T) {
+
+	opts := FindOption()
+	opts.SetDefaultLimit(10)
+
+	expected := result{
+		Query{
+			Filter:     map[string]interface{}{},
+			Sort:       map[string]int(nil),
+			Limit:      10,
+			Skip:       0,
+			Projection: map[string]int(nil),
+		},
+		nil,
+	}
+
+	query, err := MongoGoSearch("limit=a", opts)
+
+	assert.Equal(t, expected.Query, query)
+	assert.Equal(t, expected.Err, err)
+}
+
+func TestReturnQueryUseFindOptionsWithMaxLimit(t *testing.T) {
+
+	opts := FindOption()
+	opts.SetMaxLimit(500)
+
+	expected := result{
+		Query{
+			Filter:     map[string]interface{}{},
+			Sort:       map[string]int(nil),
+			Limit:      500,
+			Skip:       0,
+			Projection: map[string]int(nil),
+		},
+		nil,
+	}
+
+	query, err := MongoGoSearch("limit=2000", opts)
+
+	assert.Equal(t, expected.Query, query)
+	assert.Equal(t, expected.Err, err)
+}
diff --git a/operator.go b/operator.go
new file mode 100644
index 0000000..9b21818
--- /dev/null
+++ b/operator.go
@@ -0,0 +1,39 @@
+package mgs
+
+type SearchOperator int
+
+// list of allowed operators.
+const (
+	EQUAL SearchOperator = iota + 1
+	NOT_EQUAL
+	GREATER_THAN
+	GREATER_THAN_EQUAL
+	LESS_THAN
+	LESS_THAN_EQUAL
+	EXISTS
+)
+
+// String gets operator string.
+func (s SearchOperator) String() string {
+	return [...]string{"EQUAL", "NOT_EQUAL", "GREATER_THAN", "GREATER_THAN_EQUAL", "LESS_THAN", "LESS_THAN_EQUAL", "EXISTS"}[s-1]
+}
+
+// GetOperation allows get operation type to filter query
+func (s SearchOperator) GetOperation(input string) SearchOperator {
+	switch input {
+	case "=":
+		return EQUAL
+	case "!=":
+		return NOT_EQUAL
+	case ">":
+		return GREATER_THAN
+	case ">=":
+		return GREATER_THAN_EQUAL
+	case "<":
+		return LESS_THAN
+	case "<=":
+		return LESS_THAN_EQUAL
+	default:
+		return EXISTS
+	}
+}
diff --git a/operator_test.go b/operator_test.go
new file mode 100644
index 0000000..cfaa67c
--- /dev/null
+++ b/operator_test.go
@@ -0,0 +1,49 @@
+package mgs
+
+import "testing"
+
+type operatorTest struct {
+	Input    string
+	Expected SearchOperator
+}
+
+type operatorStringTest struct {
+	Input    SearchOperator
+	Expected string
+}
+
+var operatorTests = []operatorTest{
+	{"=", EQUAL},
+	{"!=", NOT_EQUAL},
+	{">", GREATER_THAN},
+	{">=", GREATER_THAN_EQUAL},
+	{"<", LESS_THAN},
+	{"<=", LESS_THAN_EQUAL},
+	{"!", EXISTS},
+}
+
+var operatorStringTests = []operatorStringTest{
+	{EQUAL, "EQUAL"},
+	{NOT_EQUAL, "NOT_EQUAL"},
+	{GREATER_THAN, "GREATER_THAN"},
+	{GREATER_THAN_EQUAL, "GREATER_THAN_EQUAL"},
+	{LESS_THAN, "LESS_THAN"},
+	{LESS_THAN_EQUAL, "LESS_THAN_EQUAL"},
+	{EXISTS, "EXISTS"},
+}
+
+func TestShouldGetOperationFromString(t *testing.T) {
+	for _, test := range operatorTests {
+		if result := test.Expected.GetOperation(test.Input); result != test.Expected {
+			t.Errorf("Result %s not equal to expected %s", result, test.Expected)
+		}
+	}
+}
+
+func TestShouldReturnStringFromSearchOperator(t *testing.T) {
+	for _, test := range operatorStringTests {
+		if result := test.Input.String(); result != test.Expected {
+			t.Errorf("Result %s not equal to expected %s", result, test.Expected)
+		}
+	}
+}
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..6bdc92d
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,130 @@
+package mgs
+
+import (
+	"strconv"
+	"strings"
+	"time"
+)
+
+const DATE_FORMAT = "2006-01-02T15:04:05.000Z"
+
+// Parse parses a given url query.
+func Parse(query string, caster *map[string]CastType) []SearchCriteria {
+	criteria := []SearchCriteria{}
+	for _, condition := range strings.Split(query, "&") {
+		criteria = append(criteria, criteriaParser(condition, caster))
+	}
+	return criteria
+}
+
+func criteriaParser(condition string, caster *map[string]CastType) SearchCriteria {
+
+	pattern := GetOperatorPattern()
+	match := pattern.FindStringSubmatch(condition)
+
+	key := match[pattern.SubexpIndex("Key")]
+	var value CastType
+	if caster != nil {
+		value = (*caster)[key]
+	}
+
+	return SearchCriteria{
+		Prefix:    match[pattern.SubexpIndex("Prefix")] != "",
+		Key:       key,
+		Operation: SearchOperator(1).GetOperation(match[pattern.SubexpIndex("Operator")]),
+		Value:     match[pattern.SubexpIndex("Value")],
+		Caster:    &value,
+	}
+}
+
+// ParseValue converts a string to a data type
+func ParseValue(value string, cast *CastType) interface{} {
+
+	if cast == nil {
+		return parseValue(value)
+	}
+
+	switch option := *cast; option {
+	case BOOLEAN:
+		if b, err := strconv.ParseBool(value); err == nil {
+			return b
+		}
+	case DATE:
+		if datetime, err := time.Parse(DATE_FORMAT, value); err == nil {
+			return datetime
+		}
+	case NUMBER:
+		if integer, err := strconv.ParseInt(value, 10, 64); err == nil {
+			return integer
+		}
+
+		if float, err := strconv.ParseFloat(value, 64); err == nil {
+			return float
+		}
+	case PATTERN:
+		if match := GetRegexPattern().FindStringSubmatch(value); match != nil {
+			return Regex{
+				Pattern: match[GetRegexPattern().SubexpIndex("Pattern")],
+				Option:  match[GetRegexPattern().SubexpIndex("Option")],
+			}
+		}
+	case STRING:
+		return value
+	}
+
+	return parseValue(value)
+}
+
+func parseValue(value string) interface{} {
+
+	if strings.EqualFold(value, "true") || strings.EqualFold(value, "false") {
+		if b, err := strconv.ParseBool(strings.ToLower(value)); err == nil {
+			return b
+		}
+	}
+
+	var err error
+
+	var integer int64
+	if integer, err = strconv.ParseInt(value, 10, 64); err == nil {
+		return integer
+	}
+
+	var float float64
+	if float, err = strconv.ParseFloat(value, 64); err == nil {
+		return float
+	}
+
+	var datetime time.Time
+	if datetime, err = time.Parse(DATE_FORMAT, value); err == nil {
+		return datetime
+	}
+
+	list := strings.Split(value, ",")
+	if len(list) > 1 {
+		var characters []interface{}
+		for _, _value := range list {
+			characters = append(characters, parseValue(_value))
+		}
+		if len(characters) > 0 {
+			return characters
+		}
+	}
+
+	if match := GetRegexPattern().FindStringSubmatch(value); match != nil {
+		return Regex{
+			Pattern: match[GetRegexPattern().SubexpIndex("Pattern")],
+			Option:  match[GetRegexPattern().SubexpIndex("Option")],
+		}
+	}
+
+	return value
+}
+
+func parseIntValueToInt(value string) (int64, error) {
+	result, err := strconv.ParseInt(value, 10, 64)
+	if err != nil {
+		return 0, ErrValueNoMatch
+	}
+	return result, nil
+}
diff --git a/parser_test.go b/parser_test.go
new file mode 100644
index 0000000..d291c38
--- /dev/null
+++ b/parser_test.go
@@ -0,0 +1,148 @@
+package mgs
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var booleanTests = []struct {
+	Input    string
+	Caster   *CastType
+	Expected bool
+}{
+	{"true", nil, true},
+	{"True", nil, true},
+	{"TRUE", nil, true},
+	{"TrUe", nil, true},
+	{"false", nil, false},
+	{"False", nil, false},
+	{"FALSE", nil, false},
+	{"FaLsE", nil, false},
+}
+
+func TestShouldReturnCriteriaForParseWithQuery(t *testing.T) {
+	opts := &FindOptions{}
+	var value CastType
+	criteria := Parse("name=John", opts.Caster)
+	expected := []SearchCriteria{
+		{
+			Prefix:    false,
+			Key:       "name",
+			Operation: EQUAL,
+			Value:     "John",
+			Caster:    &value,
+		},
+	}
+
+	if len(criteria) == 0 {
+		t.Errorf("Error creating criteria array")
+	}
+
+	assert.Equal(t, expected, criteria)
+}
+
+func TestShouldBoolTypeForParseValue(t *testing.T) {
+	for _, test := range booleanTests {
+		assert.Equal(t, test.Expected, ParseValue(test.Input, nil))
+	}
+}
+
+func TestShouldIntegerTypeForParseValue(t *testing.T) {
+
+	assert.Equal(t, int64(10), ParseValue("10", nil))
+}
+
+func TestShouldFloatTypeForParseValue(t *testing.T) {
+
+	assert.Equal(t, float64(3.1415), ParseValue("3.1415", nil))
+}
+
+func TestShouldDateTimeTypeForParseValue(t *testing.T) {
+	expected := time.Date(2023, 10, 9, 20, 00, 00, 999000000, time.UTC)
+
+	assert.Equal(t, expected, ParseValue("2023-10-09T20:00:00.999Z", nil))
+}
+
+func TestShouldStringListTypeForParseValue(t *testing.T) {
+	expected := []interface{}{"apple", "banana", "grape"}
+
+	assert.Equal(t, expected, ParseValue("apple,banana,grape", nil))
+}
+
+func TestShouldIntegerListTypeForParseValue(t *testing.T) {
+	expected := []interface{}{int64(1), int64(2), int64(3)}
+
+	assert.Equal(t, expected, ParseValue("1,2,3", nil))
+}
+
+func TestShouldRegexTypeForParseValue(t *testing.T) {
+
+	assert.Equal(t, "John", ParseValue("John", nil))
+}
+
+func TestShouldStringTypeForParseValue(t *testing.T) {
+	expected := Regex{
+		Pattern: "@gmail\\.com$",
+	}
+
+	assert.Equal(t, expected, ParseValue("/@gmail\\.com$/", nil))
+}
+
+func TestShouldReturnUnsignedIntForParseIntValueToUnsignedIntWhenIsValidNumberString(t *testing.T) {
+	result, err := parseIntValueToInt("1")
+
+	if err != nil {
+		t.Errorf("Error format string to uint")
+	}
+
+	assert.Equal(t, int64(1), result)
+}
+
+func TestShouldReturnErrorForParseIntValueToUnsignedIntWhenIsInvalidNumberString(t *testing.T) {
+	result, err := parseIntValueToInt("a")
+
+	if err == nil {
+		t.Errorf("Error format string to uint")
+	}
+
+	assert.Equal(t, int64(0), result)
+}
+
+func TestShouldIntegerTypeForParseValueWithCastBoolean(t *testing.T) {
+
+	cast := BOOLEAN
+
+	assert.Equal(t, true, ParseValue("true", &cast))
+}
+
+func TestShouldIntegerTypeForParseValueWithCastDate(t *testing.T) {
+
+	cast := DATE
+	expected := time.Date(2023, 10, 9, 20, 00, 00, 999000000, time.UTC)
+
+	assert.Equal(t, expected, ParseValue("2023-10-09T20:00:00.999Z", &cast))
+}
+
+func TestShouldIntegerTypeForParseValueWithIntCastNumber(t *testing.T) {
+
+	cast := NUMBER
+	assert.Equal(t, int64(10), ParseValue("10", &cast))
+}
+
+func TestShouldIntegerTypeForParseValueWithFloatCastNumber(t *testing.T) {
+
+	cast := NUMBER
+	assert.Equal(t, float64(10.5), ParseValue("10.5", &cast))
+}
+
+func TestShouldIntegerTypeForParseValueWithCastRegex(t *testing.T) {
+
+	cast := PATTERN
+	expected := Regex{
+		Pattern: "@gmail\\.com$",
+	}
+
+	assert.Equal(t, expected, ParseValue("/@gmail\\.com$/", &cast))
+}
diff --git a/pattern.go b/pattern.go
new file mode 100644
index 0000000..d5ba28d
--- /dev/null
+++ b/pattern.go
@@ -0,0 +1,28 @@
+package mgs
+
+import (
+	"regexp"
+)
+
+const OPERATOR_PATTERN string = "(?P<Prefix>!?)(?P<Key>[^><!=]+)(?P<Operator>[><]=?|!?=|)(?P<Value>.*)"
+
+// const REGEX_PATTERN string = "^/(?P<Pattern>.*)(?P<Option>[igm]*)/$"
+const REGEX_PATTERN string = "^\\/(?P<Pattern>.*)\\/(?P<Option>[igm]*)$"
+
+var operator *regexp.Regexp = nil
+var pattern *regexp.Regexp = nil
+
+func init() {
+	operator = regexp.MustCompile(OPERATOR_PATTERN)
+	pattern = regexp.MustCompile(REGEX_PATTERN)
+}
+
+// GetOperatorPattern gets operator RegExp pattern
+func GetOperatorPattern() *regexp.Regexp {
+	return operator
+}
+
+// GetRegexPattern gets RegExp pattern
+func GetRegexPattern() *regexp.Regexp {
+	return pattern
+}
diff --git a/pattern_test.go b/pattern_test.go
new file mode 100644
index 0000000..8e90444
--- /dev/null
+++ b/pattern_test.go
@@ -0,0 +1,15 @@
+package mgs
+
+import "testing"
+
+func TestShouldGetOperatorPattern(t *testing.T) {
+	if GetOperatorPattern() == nil {
+		t.Errorf("Operator regular expression instance failed")
+	}
+}
+
+func TestShouldGetRegexpPattern(t *testing.T) {
+	if GetRegexPattern() == nil {
+		t.Errorf("Regular expression instance failed")
+	}
+}
diff --git a/structs.go b/structs.go
new file mode 100644
index 0000000..9802ddd
--- /dev/null
+++ b/structs.go
@@ -0,0 +1,37 @@
+package mgs
+
+// Query is a structure that holds information about DB request.
+type Query struct {
+	// Filter is a document containing query operators.
+	Filter map[string]interface{}
+	// Sort is a document specifying the order in which documents should be returned.
+	Sort map[string]int
+	// Limit is the maximum number of documents to return.
+	Limit int64
+	// Skip is a number of documents to be skipped.
+	Skip int64
+	// Projection is the limit fields to return in each records.
+	Projection map[string]int
+}
+
+// SearchCriteria is a structure that holds search criteria specification.
+type SearchCriteria struct {
+	// Prefix is a bool that allows to check that the search criteria contain prefixes.
+	Prefix bool
+	// key is a string that allows the key to be identified in the search criteria.
+	Key string
+	// Operation is a structure that containg operator type.
+	Operation SearchOperator
+	// Value is a string that allows the value to be identified in the search criteria.
+	Value string
+	// Caster is a cast type value.
+	Caster *CastType
+}
+
+// Regex is a structure that holds RegExp specification.
+type Regex struct {
+	// Pattern is a string contains regex pattern should be used.
+	Pattern string
+	// Opetion is a string contains regex options should be used.
+	Option string
+}