Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create package query-search #44

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions querySearch/cleanAssetMap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// This code contains functions related to cleaning up an asset map by removing certain tags.
package querysearch

// List of default tags that will be removed if enabled
func GetDefaultRemoveTags() *[]string {
return &[]string{"@lastTouchBy", "@lastTx", "@lastUpdated", "@txId", "@txID"}
}

// Remove fields in map by default tags
func CleanAssetMapDefault(m map[string]interface{}) map[string]interface{} {
return CleanAssetMap(m, GetDefaultRemoveTags())
}

// This is a recursive function that cleans an asset map by removing tags.
// It takes a map and a slice of tags to remove.
// It iterates over the map and removes any tags that are in the removeTag slice.
// If a value in the map is another map or a slice,
// it recursively calls CleanAssetMap on that value.
func CleanAssetMap(m map[string]interface{}, removeTag *[]string) map[string]interface{} {
if removeTag != nil {
for _, tag := range *removeTag {
delete(m, tag)
}
}

for k, v := range m {
switch prop := v.(type) {
case map[string]interface{}:
m[k] = CleanAssetMap(prop, removeTag)

case []interface{}:
for idx, elem := range prop {
if elemMap, ok := elem.(map[string]interface{}); ok {
prop[idx] = CleanAssetMap(elemMap, removeTag)
}
}
}
}

return m
}

// Internal function for remove tags
func (q *QuerySearch) removeTags(m *map[string]interface{}) {
if q.config.NoRemoveTagsTransaction && len(q.config.RemoveTags) == 0 {
return
}

remove := []string{}
if !q.config.NoRemoveTagsTransaction {
remove = append(remove, q.config.removeDefaultTags...)
}

remove = append(remove, q.config.RemoveTags...)
*m = CleanAssetMap(*m, &remove)
}
80 changes: 80 additions & 0 deletions querySearch/date.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// These functions are useful when you need to create specific date and time queries,
// like when you need to filter results between a time interval or within a specific time of day.
package querysearch

import (
"fmt"
"time"
)

var defaultLocationTimezone = "America/Sao_Paulo"

// Set default timezone for reference
func SetDefaultTimezone(locationDefault string) {
defaultLocationTimezone = locationDefault
}

// Return date in format '2006-01-02 00:00:00 '
func GetDateFormatted(dateRef time.Time) string {
dateF := setTimeZone(dateRef)
return dateF.Format("2006-01-02") + " 00:00:00 " + GetTimeZoneOffSet(dateF)
}

// Return hour and minute in UTC, with offset from the local timezone
func GetTimeZoneOffSet(dateRef time.Time) string {
_, offset := dateRef.Zone()
offsetHours := offset / 3600
offsetMinutes := (offset % 3600) / 60
offsetStr := fmt.Sprintf("%03d:%02d", offsetHours, offsetMinutes)
return offsetStr
}

// Change date to timezone defined on variable default LocationTimezone
func setTimeZone(dateRef time.Time) time.Time {
loc, _ := time.LoadLocation(defaultLocationTimezone)
return dateRef.In(loc)
}

// Define fisrt hour of day
func SetDateFirstHour(dateRef time.Time) time.Time {
newDate := setTimeZone(dateRef)
newDate = time.Date(newDate.Year(), newDate.Month(), newDate.Day(), 0, 0, 0, 0, newDate.Location())
return newDate
}

// Define last hour of day
func SetDateLastHour(dateRef time.Time) time.Time {
newDate := setTimeZone(dateRef)
newDate = time.Date(newDate.Year(), newDate.Month(), newDate.Day(), 23, 59, 59, 0, newDate.Location())
return newDate
}

// Define the selection of a date >= the date entered
func StartDate(dateRef time.Time) map[string]interface{} {
return map[string]interface{}{
"$gte": SetDateFirstHour(dateRef),
}
}

// Define the selection of a date <= the date entered
func EndDate(dateRef time.Time) map[string]interface{} {
return map[string]interface{}{
"$lte": SetDateLastHour(dateRef),
}
}

// Define the date selection within a period of 1 day from the date entered
func OneDay(dateRef time.Time) map[string]interface{} {
return map[string]interface{}{
"$gte": SetDateFirstHour(dateRef),
"$lte": SetDateLastHour(dateRef),
}
}

// Define the date range between two dates
func PeriodDay(dateStart, dateEnd time.Time) map[string]interface{} {
return map[string]interface{}{
"$gte": SetDateFirstHour(dateStart),
"$lte": SetDateLastHour(dateEnd),
}
}
188 changes: 188 additions & 0 deletions querySearch/querySearch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// This package provides a way to query a database, possibly using a CouchDB-like interface,
// and defines several types, methods, and functions for querying. It facilitates the creation of queries
// without having to write queries in a raw form.
//
// Configuration
// -------------
// The Config struct contains the settings used for querying.
// These settings include RemoveTags (a list of tags to be removed from the results),
// AssetName (the name of the asset being queried), PageSize (the number of results per page),
// BookMark (the bookmark for pagination), Resolve (a list of relations to be resolved),
// Sort (a list of fields to sort by), IndexDoc (a document containing the index design and name),
// NoRemoveTagsTransaction (a boolean indicating whether to remove tags during the transaction),
// and CallBack (a function to be called after the query is executed).
package querysearch

import (
"encoding/json"
"time"

"github.com/hyperledger-labs/cc-tools/errors"
sw "github.com/hyperledger-labs/cc-tools/stubwrapper"
)

type Config struct {
RemoveTags []string
removeDefaultTags []string
AssetName string
PageSize int32
BookMark string
Resolve []string
Sort []map[string]string
IndexDoc IndexDocument
NoRemoveTagsTransaction bool
CallBack func(*sw.StubWrapper, map[string]interface{}, map[string]interface{}) error
CallBackList func(*sw.StubWrapper, map[string]interface{}, []map[string]interface{}, map[string]interface{}) (map[string]interface{}, error)
}

type IndexDocument struct {
Design string
IndexName string
}

type QuerySearch struct {
Query map[string]interface{}
config Config
QueryParser string
}

type FieldSearch struct {
KeyName string
Value interface{}
}

func NewQuery(cfg Config) *QuerySearch {
if len(cfg.AssetName) == 0 {
return nil
}

// ** Set default tags to remove
cfg.removeDefaultTags = *GetDefaultRemoveTags()
return &QuerySearch{
Query: map[string]interface{}{
"selector": map[string]interface{}{
"@assetType": cfg.AssetName,
},
},
config: cfg,
}
}

func (q *QuerySearch) GetConfig() Config {
return q.config
}

func (q *QuerySearch) AddCallBack(f func(*sw.StubWrapper, map[string]interface{}, map[string]interface{}) error) {
q.config.CallBack = f
}

func (q *QuerySearch) AddCallBackList(f func(*sw.StubWrapper, map[string]interface{}, []map[string]interface{}, map[string]interface{}) (map[string]interface{}, error)) {
q.config.CallBackList = f
}

func (q *QuerySearch) SetPagination(bookMark string, pageSize int32) {
q.config.BookMark = bookMark
q.config.PageSize = pageSize
}

func (q *QuerySearch) SetIndexDoc(designDoc, nameIndex string) {
q.config.IndexDoc.Design = designDoc
q.config.IndexDoc.IndexName = nameIndex
}

func (q *QuerySearch) SetIndexDocSimple(nameIndex string) {
q.config.IndexDoc.Design = nameIndex
q.config.IndexDoc.IndexName = nameIndex
}

func (q *QuerySearch) SetSort(sort ...Sort) {
s := make([]map[string]string, 0)
for _, v := range sort {
s = append(s, map[string]string{
v.Field: string(v.Type),
})
}
q.config.Sort = s
}

func (q *QuerySearch) SetResolve(resolv ...string) {
q.config.Resolve = append(q.config.Resolve, resolv...)
}

func (q *QuerySearch) SetRemoveCustomTags(tags ...string) {
q.config.RemoveTags = append(q.config.RemoveTags, tags...)
}

func (q *QuerySearch) NoContains(value interface{}) map[string]interface{} {
return map[string]interface{}{
"$ne": value,
}
}

func (q *QuerySearch) NoContainsValues(values []interface{}) map[string]interface{} {
return map[string]interface{}{
"$nin": values,
}
}

func (q *QuerySearch) AddFieldsOR(fields map[string]interface{}) {
aux := q.Query["selector"].(map[string]interface{})
listOfElementsFinds := make([]map[string]interface{}, 0)

for k, v := range fields {
listOfElementsFinds = append(listOfElementsFinds, map[string]interface{}{
k: v,
})
}

aux["$or"] = listOfElementsFinds
q.Query["selector"] = aux
}

func (q *QuerySearch) AddField(key string, value interface{}) {
q.AddFields(FieldSearch{
KeyName: key,
Value: value,
})
}

func (q *QuerySearch) AddFields(fields ...FieldSearch) {
for _, f := range fields {
if len(f.KeyName) > 0 {
aux := q.Query["selector"].(map[string]interface{})
aux[f.KeyName] = f.Value
q.Query["selector"] = aux
}
}
}

func (q *QuerySearch) AddDateRange(nameField string, dataStart, dataEnd time.Time) {
q.AddFields(FieldSearch{
KeyName: nameField,
Value: PeriodDay(dataStart, dataEnd),
})
}

func (q *QuerySearch) Parser() (string, error) {
qAux := q.Query

//** Add Fields for sort query
if len(q.config.Sort) > 0 {
qAux["sort"] = q.config.Sort
}

//** Add indexDoc and indexName for use in query
if len(q.config.IndexDoc.Design) > 0 && len(q.config.IndexDoc.IndexName) > 0 {
qAux["use_index"] = []string{
"_design/" + q.config.IndexDoc.Design,
q.config.IndexDoc.IndexName,
}
}

queryJson, err := json.Marshal(qAux)
if err != nil {
return "", errors.WrapError(err, "error marshaling query")
}

return string(queryJson), nil
}
Loading