func (q *Query) URLQueryParameters(withOptions bool) URLQueryParameters {
	var queryParams URLQueryParameters
	for _, param := range q.Params() {
		k, v := param.getQueryParamAndValue()
		queryParams.Add(k, v)
	}
	if withOptions {
		options := q.Options()
		oQueryParams := options.URLQueryParameters()
		for _, oQueryParam := range oQueryParams.All() {
			if options.IsIncludeAll && oQueryParam.Key == "_include" {
				continue
			}
			if options.IsRevincludeAll && oQueryParam.Key == "_revinclude" {
				continue
			}
			queryParams.Add(oQueryParam.Key, oQueryParam.Value)
		}
		if options.IsIncludeAll {
			queryParams.Add("_include", "*")
		}
		if options.IsRevincludeAll {
			queryParams.Add("_revinclude", "*")
		}
	}
	return queryParams
}
URLQueryParameters reconstructs the URL-encoded query based on parsed
parameters.  This ensures better uniformity/consistency and also removes any
garbage parameters or bad formatting in the passed in parameters.  If
withOptions is specified, the query options will also be included in the
URLQueryParameters.
package search
import (
	"fmt"
	"math/big"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)
const (
	IDParam            = "_id"
	LastUpdatedParam   = "_lastUpdated"
	TagParam           = "_tag"
	ProfileParam       = "_profile"
	SecurityParam      = "_security"
	TextParam          = "_text"
	ContentParam       = "_content"
	ListParam          = "_list"
	QueryParam         = "_query"
	SortParam          = "_sort"
	CountParam         = "_count"
	IncludeParam       = "_include"
	RevIncludeParam    = "_revinclude"
	SummaryParam       = "_summary"
	ElementsParam      = "_elements"
	ContainedParam     = "_contained"
	ContainedTypeParam = "_containedType"
	OffsetParam        = "_offset"  
	FormatParam        = "_format"
)
var globalSearchParams = map[string]bool{IDParam: true, LastUpdatedParam: true, TagParam: true,
	ProfileParam: true, SecurityParam: true, TextParam: true, ContentParam: true, ListParam: true,
	QueryParam: true}
func isGlobalSearchParam(param string) bool {
	_, found := globalSearchParams[param]
	return found
}
var searchResultParams = map[string]bool{SortParam: true, CountParam: true, IncludeParam: true,
	RevIncludeParam: true, SummaryParam: true, ElementsParam: true, ContainedParam: true,
	ContainedTypeParam: true, OffsetParam: true, FormatParam: true}
func isSearchResultParam(param string) bool {
	_, found := searchResultParams[param]
	return found
}
type Query struct {
	Resource string
	Query    string
}
func (q *Query) Params() []SearchParam {
	var results []SearchParam
	queryParams, _ := ParseQuery(q.Query)
	for _, queryParam := range queryParams.All() {
		param, modifier, postfix := ParseParamNameModifierAndPostFix(queryParam.Key)
		if isSearchResultParam(param) {
			continue
		}
		info, ok := SearchParameterDictionary[q.Resource][param]
		if ok {
			info.Postfix = postfix
			info.Modifier = modifier
			results = append(results, info.CreateSearchParam(queryParam.Value))
		} else {
			if isGlobalSearchParam(param) {
				panic(createUnsupportedSearchError("MSG_PARAM_UNKNOWN", fmt.Sprintf("Parameter \"%s\" not understood", param)))
			} else {
				panic(createInvalidSearchError("SEARCH_NONE", fmt.Sprintf("Error: no processable search found for %s search parameters \"%s\"", q.Resource, param)))
			}
		}
	}
	return results
}
func (q *Query) Options() *QueryOptions {
	options := NewQueryOptions()
	queryParams, _ := ParseQuery(q.Query)
	if strings.Contains(q.Query, "_include=*") {
		options.IsIncludeAll = true
	}
	if strings.Contains(q.Query, "_revinclude=*") {
		options.IsRevincludeAll = true
	}
	for _, queryParam := range queryParams.All() {
		param, modifier, _ := ParseParamNameModifierAndPostFix(queryParam.Key)
		if !strings.HasPrefix(param, "_") || isGlobalSearchParam(param) {
			continue
		}
		switch param {
		case CountParam:
			count, err := strconv.Atoi(queryParam.Value)
			if err != nil {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_count\" content is invalid"))
			}
			if count >= 0 {
				options.Count = count
			}
		case OffsetParam:
			offset, err := strconv.Atoi(queryParam.Value)
			if err != nil {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_offset\" content is invalid"))
			}
			if offset >= 0 {
				options.Offset = offset
			}
		case SortParam:
			keys := strings.Split(queryParam.Value, ",")
			for _, key := range keys {
				desc := strings.HasPrefix(key, "-") || modifier == "desc"
				sortParam, ok := SearchParameterDictionary[q.Resource][strings.TrimPrefix(key, "-")]
				if !ok {
					panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_sort\" content is invalid"))
				}
				options.Sort = append(options.Sort, SortOption{Descending: desc, Parameter: sortParam})
			}
			if len(keys) > 1 || strings.HasPrefix(queryParam.Value, "-") {
				options.IsSTU3Sort = true
			}
		case IncludeParam:
			if options.IsIncludeAll {
				continue
			}
			incls := strings.Split(queryParam.Value, ":")
			if len(incls) < 2 || len(incls) > 3 {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_include\" content is invalid"))
			}
			inclParam, ok := SearchParameterDictionary[incls[0]][incls[1]]
			if !ok {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_include\" content is invalid"))
			}
			if inclParam.Type != "reference" {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_include\" content is invalid"))
			}
			if len(incls) == 3 {
				if isValidTarget(incls[2], inclParam) {
					inclParam.Targets = []string{incls[2]}
				} else {
					panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_include\" content is invalid"))
				}
			}
			options.Include = append(options.Include, IncludeOption{Resource: incls[0], Parameter: inclParam})
		case RevIncludeParam:
			if options.IsRevincludeAll {
				continue
			}
			incls := strings.Split(queryParam.Value, ":")
			if len(incls) < 2 || len(incls) > 3 {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_revinclude\" content is invalid"))
			}
			revInclParam, ok := SearchParameterDictionary[incls[0]][incls[1]]
			if !ok {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_revinclude\" content is invalid"))
			}
			if revInclParam.Type != "reference" {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_revinclude\" content is invalid"))
			}
			target := q.Resource
			if len(incls) == 3 && incls[2] != target && incls[2] != "Any" {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_revinclude\" content is invalid"))
			}
			if isValidTarget(target, revInclParam) {
				revInclParam.Targets = []string{target}
			} else {
				panic(createInvalidSearchError("MSG_PARAM_INVALID", "Parameter \"_revinclude\" content is invalid"))
			}
			options.RevInclude = append(options.RevInclude, RevIncludeOption{Resource: incls[0], Parameter: revInclParam})
		case FormatParam:
			if queryParam.Value != "json" && queryParam.Value != "application/json" && queryParam.Value != "application/json+fhir" {
				panic(createUnsupportedSearchError("MSG_PARAM_INVALID", "Parameter \"_format\" content is invalid"))
			}
		default:
			panic(createUnsupportedSearchError("MSG_PARAM_UNKNOWN", fmt.Sprintf("Parameter \"%s\" not understood", param)))
		}
	}
	if options.IsIncludeAll {
		inclParams := SearchParameterDictionary[q.Resource]
		for _, inclParam := range inclParams {
			if inclParam.Type == "reference" {
				options.Include = append(options.Include, IncludeOption{Resource: q.Resource, Parameter: inclParam})
			}
		}
	}
	if options.IsRevincludeAll {
		for resource, resourceSearchParams := range SearchParameterDictionary {
			for _, revInclParam := range resourceSearchParams {
				if revInclParam.Type == "reference" && contains(revInclParam.Targets, q.Resource) {
					options.RevInclude = append(options.RevInclude, RevIncludeOption{Resource: resource, Parameter: revInclParam})
				}
			}
		}
	}
	return options
}
func getSingletonParamValue(param string, values []string) string {
	if len(values) != 1 {
		panic(createInvalidSearchError("MSG_PARAM_NO_REPEAT", fmt.Sprintf("Parameter \"%s\" is not allowed to repeat", param)))
	}
	return values[0]
}
func isValidTarget(target string, param SearchParamInfo) bool {
	for _, pTarget := range param.Targets {
		if pTarget == target || pTarget == "Any" {
			return true
		}
	}
	return false
}