Documentation
¶
Overview ¶
Package query provides a query language parser and builder for the CMS plugin. It supports URL-based and JSON-based query syntax for filtering, sorting, and pagination.
Index ¶
- Constants
- Variables
- func GetSystemFieldName(field string) string
- func IsMetaField(field string) bool
- func IsSystemField(field string) bool
- func PopulateEntryDTO(entry *schema.ContentEntry, dto *core.ContentEntryDTO)
- type AggregateOperator
- type AggregateQuery
- type AggregateResult
- type Aggregation
- type Aggregator
- func (a *Aggregator) GetCMSStats(ctx context.Context, appID, envID xid.ID) (*core.CMSStatsDTO, error)
- func (a *Aggregator) GetEntryStats(ctx context.Context, contentTypeID xid.ID) (*core.ContentTypeStatsDTO, error)
- func (a *Aggregator) GetTimeSeriesStats(ctx context.Context, contentTypeID xid.ID, dateTrunc string, limit int) ([]map[string]any, error)
- func (a *Aggregator) SimpleAggregate(ctx context.Context, contentTypeID xid.ID, config *SimpleAggregateConfig) ([]*SimpleAggregateResult, error)
- type FilterCondition
- type FilterGroup
- type FilterOperator
- type JSONParser
- type JSONQuery
- type LogicalOperator
- type OperatorInfo
- type PopulateConfig
- type PopulateOption
- type Populator
- func (p *Populator) GetRelatedEntries(ctx context.Context, entryID xid.ID, fieldSlug string) ([]*schema.ContentEntry, error)
- func (p *Populator) GetRelatedEntryIDs(ctx context.Context, entryID xid.ID, fieldSlug string) ([]xid.ID, error)
- func (p *Populator) GetReverseRelatedEntries(ctx context.Context, entryID xid.ID, fieldSlug string) ([]*schema.ContentEntry, error)
- func (p *Populator) PopulateEntries(ctx context.Context, entries []*schema.ContentEntry, config *PopulateConfig) error
- func (p *Populator) PopulateEntry(ctx context.Context, entry *schema.ContentEntry, config *PopulateConfig) error
- type Query
- func (q *Query) AddFilter(field string, operator FilterOperator, value interface{}) *Query
- func (q *Query) AddPopulate(path string, selectFields ...string) *Query
- func (q *Query) AddSelect(fields ...string) *Query
- func (q *Query) AddSort(field string, descending bool) *Query
- func (q *Query) SetOffsetLimit(offset, limit int) *Query
- func (q *Query) SetPagination(page, pageSize int) *Query
- func (q *Query) Validate(fields map[string]*core.ContentFieldDTO) error
- type QueryBuilder
- type QueryExecutor
- func (e *QueryExecutor) Execute(ctx context.Context, contentType *schema.ContentType, q *Query) (*QueryResult, error)
- func (e *QueryExecutor) ExecuteAggregate(ctx context.Context, contentType *schema.ContentType, q *AggregateQuery) ([]AggregateResult, error)
- func (e *QueryExecutor) ExecuteByID(ctx context.Context, entryID xid.ID) (*schema.ContentEntry, error)
- func (e *QueryExecutor) ExecuteCount(ctx context.Context, contentType *schema.ContentType, q *Query) (int, error)
- func (e *QueryExecutor) ExecuteDistinct(ctx context.Context, contentType *schema.ContentType, field string, q *Query) ([]interface{}, error)
- func (e *QueryExecutor) ExecuteExists(ctx context.Context, contentType *schema.ContentType, q *Query) (bool, error)
- func (e *QueryExecutor) ExecuteIDs(ctx context.Context, contentType *schema.ContentType, q *Query) ([]xid.ID, error)
- type QueryResult
- type SearchConfig
- type SearchResult
- type Searcher
- func (s *Searcher) Search(ctx context.Context, contentTypeID xid.ID, config *SearchConfig, ...) ([]*SearchResult, int, error)
- func (s *Searcher) SearchAll(ctx context.Context, appID, envID xid.ID, config *SearchConfig, ...) ([]*SearchResult, int, error)
- func (s *Searcher) SuggestSearch(ctx context.Context, contentTypeID xid.ID, prefix string, limit int) ([]string, error)
- type SimpleAggregateConfig
- type SimpleAggregateResult
- type SortField
- type URLParser
Constants ¶
const MetaPrefix = "_meta."
MetaPrefix is the prefix used for system/internal fields to avoid conflicts with user-defined fields
Variables ¶
var OperatorAliases = map[string]FilterOperator{ "=": OpEqual, "==": OpEqual, "eq": OpEqual, "equals": OpEqual, "!=": OpNotEqual, "<>": OpNotEqual, "ne": OpNotEqual, "neq": OpNotEqual, "notEquals": OpNotEqual, ">": OpGreaterThan, "gt": OpGreaterThan, ">=": OpGreaterThanEqual, "gte": OpGreaterThanEqual, "<": OpLessThan, "lt": OpLessThan, "<=": OpLessThanEqual, "lte": OpLessThanEqual, "like": OpLike, "ilike": OpILike, "contains": OpContains, "startsWith": OpStartsWith, "endsWith": OpEndsWith, "in": OpIn, "notIn": OpNotIn, "nin": OpNotIn, "all": OpAll, "any": OpAny, "null": OpNull, "isNull": OpNull, "exists": OpExists, "between": OpBetween, "jsonContains": OpJsonContains, "jsonHasKey": OpJsonHasKey, }
OperatorAliases maps common aliases to operators
var SystemFields = map[string]bool{ "id": true, "status": true, "version": true, "createdAt": true, "updatedAt": true, "publishedAt": true, "scheduledAt": true, "createdBy": true, "updatedBy": true, }
SystemFields lists all valid system field names (without the _meta prefix)
Functions ¶
func GetSystemFieldName ¶
GetSystemFieldName extracts the actual field name from a potentially _meta prefixed field e.g., "_meta.status" -> "status", "status" -> "status"
func IsMetaField ¶
IsMetaField returns true if the field uses the _meta prefix format
func IsSystemField ¶
IsSystemField returns true if the field is a system field (not user-defined) Supports both legacy format (e.g., "status") and new meta format (e.g., "_meta.status")
func PopulateEntryDTO ¶
func PopulateEntryDTO(entry *schema.ContentEntry, dto *core.ContentEntryDTO)
PopulateEntryDTO populates relation fields in an entry DTO
Types ¶
type AggregateOperator ¶
type AggregateOperator string
AggregateOperator represents an aggregation function
const ( AggCount AggregateOperator = "count" AggSum AggregateOperator = "sum" AggAvg AggregateOperator = "avg" AggMin AggregateOperator = "min" AggMax AggregateOperator = "max" )
func (AggregateOperator) IsValid ¶
func (op AggregateOperator) IsValid() bool
IsValid returns true if the operator is valid
type AggregateQuery ¶
type AggregateQuery struct {
// GroupBy specifies fields to group by
GroupBy []string `json:"groupBy,omitempty"`
// Aggregations specifies aggregation operations
Aggregations []Aggregation `json:"aggregations"`
// Filters to apply before aggregation
Filters *FilterGroup `json:"filters,omitempty"`
// Having conditions (filters on aggregated values)
Having *FilterGroup `json:"having,omitempty"`
// Sort the aggregation results
Sort []SortField `json:"sort,omitempty"`
// Limit the number of results
Limit int `json:"limit,omitempty"`
}
AggregateQuery represents an aggregation query
type AggregateResult ¶
type AggregateResult struct {
// GroupKey holds the group by values (if any)
GroupKey map[string]interface{} `json:"groupKey,omitempty"`
// Values holds the aggregated values
Values map[string]interface{} `json:"values"`
}
AggregateResult holds the result of an aggregation
type Aggregation ¶
type Aggregation struct {
// Operator is the aggregation function (count, sum, avg, min, max)
Operator AggregateOperator `json:"operator"`
// Field is the field to aggregate (not needed for count)
Field string `json:"field,omitempty"`
// Alias is the name for the aggregated value in the result
Alias string `json:"alias"`
}
Aggregation represents a single aggregation operation
type Aggregator ¶
type Aggregator struct {
// contains filtered or unexported fields
}
Aggregator handles aggregation queries
func NewAggregator ¶
func NewAggregator(db *bun.DB) *Aggregator
NewAggregator creates a new aggregator
func (*Aggregator) GetCMSStats ¶
func (a *Aggregator) GetCMSStats(ctx context.Context, appID, envID xid.ID) (*core.CMSStatsDTO, error)
GetCMSStats returns overall CMS statistics
func (*Aggregator) GetEntryStats ¶
func (a *Aggregator) GetEntryStats(ctx context.Context, contentTypeID xid.ID) (*core.ContentTypeStatsDTO, error)
GetEntryStats returns statistics for entries of a content type
func (*Aggregator) GetTimeSeriesStats ¶
func (a *Aggregator) GetTimeSeriesStats(ctx context.Context, contentTypeID xid.ID, dateTrunc string, limit int) ([]map[string]any, error)
GetTimeSeriesStats returns entry counts over time
func (*Aggregator) SimpleAggregate ¶
func (a *Aggregator) SimpleAggregate(ctx context.Context, contentTypeID xid.ID, config *SimpleAggregateConfig) ([]*SimpleAggregateResult, error)
SimpleAggregate performs a simple aggregation query on content entries
type FilterCondition ¶
type FilterCondition struct {
// Field is the field name to filter on
Field string `json:"field"`
// Operator is the comparison operator
Operator FilterOperator `json:"operator"`
// Value is the value to compare against
Value interface{} `json:"value"`
// Type hint for value parsing (optional)
Type string `json:"type,omitempty"`
}
FilterCondition represents a single filter condition
type FilterGroup ¶
type FilterGroup struct {
// Operator is AND, OR, or NOT
Operator LogicalOperator `json:"operator,omitempty"`
// Conditions are the filter conditions in this group
Conditions []FilterCondition `json:"conditions,omitempty"`
// Groups are nested filter groups (for complex queries)
Groups []*FilterGroup `json:"groups,omitempty"`
}
FilterGroup represents a group of filters with a logical operator
type FilterOperator ¶
type FilterOperator string
FilterOperator represents a comparison operator
const ( // Comparison operators OpEqual FilterOperator = "eq" OpNotEqual FilterOperator = "ne" OpGreaterThan FilterOperator = "gt" OpGreaterThanEqual FilterOperator = "gte" OpLessThan FilterOperator = "lt" OpLessThanEqual FilterOperator = "lte" // String operators OpLike FilterOperator = "like" // Case-sensitive pattern match OpILike FilterOperator = "ilike" // Case-insensitive pattern match OpContains FilterOperator = "contains" // String contains OpStartsWith FilterOperator = "startsWith" OpEndsWith FilterOperator = "endsWith" // Array operators OpIn FilterOperator = "in" // Value is in array OpNotIn FilterOperator = "nin" // Value is not in array OpAll FilterOperator = "all" // Array contains all values OpAny FilterOperator = "any" // Array contains any value // Null operators OpNull FilterOperator = "null" // Field is null (value: true) or not null (value: false) OpExists FilterOperator = "exists" // Field exists (for JSON fields) // JSON operators OpJsonContains FilterOperator = "jsonContains" // JSON contains OpJsonHasKey FilterOperator = "jsonHasKey" // JSON has key // Date operators OpBetween FilterOperator = "between" // Value is between two values )
func ParseOperator ¶
func ParseOperator(s string) (FilterOperator, bool)
ParseOperator parses an operator string
func ResolveOperator ¶
func ResolveOperator(s string) (FilterOperator, bool)
ResolveOperator resolves an operator string to a FilterOperator
func (FilterOperator) IsValid ¶
func (op FilterOperator) IsValid() bool
IsValid returns true if the operator is valid
func (FilterOperator) RequiresValue ¶
func (op FilterOperator) RequiresValue() bool
RequiresValue returns true if the operator requires a value
type JSONParser ¶
type JSONParser struct{}
JSONParser parses JSON query bodies into Query objects
func (*JSONParser) Parse ¶
func (p *JSONParser) Parse(data []byte) (*Query, error)
Parse parses JSON data into a Query object
func (*JSONParser) ParseJSONQuery ¶
func (p *JSONParser) ParseJSONQuery(jq *JSONQuery) (*Query, error)
ParseJSONQuery parses a JSONQuery struct into a Query object
type JSONQuery ¶
type JSONQuery struct {
// Filters can be a simple object or complex nested structure
// Simple: {"status": "published", "title": {"$contains": "hello"}}
// Complex: {"$and": [{"status": "published"}, {"$or": [...]}]}
Filters interface{} `json:"filters,omitempty"`
// Filter is an alias for filters (singular form)
Filter interface{} `json:"filter,omitempty"`
// Alternative filter syntax
Where interface{} `json:"where,omitempty"`
// Sort can be a string or array
// String: "-createdAt" or "createdAt:desc"
// Array: ["-createdAt", "title"]
Sort interface{} `json:"sort,omitempty"`
// Select fields to return
Select []string `json:"select,omitempty"`
// Fields is an alias for select
Fields []string `json:"fields,omitempty"`
// Populate relations
Populate interface{} `json:"populate,omitempty"`
// Include is an alias for populate
Include interface{} `json:"include,omitempty"`
// Pagination
Page int `json:"page,omitempty"`
PageSize int `json:"pageSize,omitempty"`
PerPage int `json:"perPage,omitempty"`
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
// Search for full-text search
Search string `json:"search,omitempty"`
Q string `json:"q,omitempty"`
// Status shorthand
Status string `json:"status,omitempty"`
}
JSONQuery represents a JSON query body
type LogicalOperator ¶
type LogicalOperator string
LogicalOperator represents a logical operator for combining filters
const ( LogicalAnd LogicalOperator = "and" LogicalOr LogicalOperator = "or" LogicalNot LogicalOperator = "not" )
func (LogicalOperator) IsValid ¶
func (op LogicalOperator) IsValid() bool
IsValid returns true if the operator is valid
type OperatorInfo ¶
type OperatorInfo struct {
// Operator is the operator code
Operator FilterOperator `json:"operator"`
// Name is the human-readable name
Name string `json:"name"`
// Description describes what the operator does
Description string `json:"description"`
// Example shows usage example
Example string `json:"example"`
// ApplicableTo lists field types this operator can be used with
ApplicableTo []string `json:"applicableTo"`
// ValueType describes the expected value type
ValueType string `json:"valueType"`
}
OperatorInfo provides metadata about an operator
func GetAllOperators ¶
func GetAllOperators() []OperatorInfo
GetAllOperators returns information about all available operators
func GetOperatorInfo ¶
func GetOperatorInfo(op FilterOperator) *OperatorInfo
GetOperatorInfo returns information about a specific operator
func GetOperatorsForFieldType ¶
func GetOperatorsForFieldType(fieldType string) []OperatorInfo
GetOperatorsForFieldType returns operators applicable to a field type
type PopulateConfig ¶
type PopulateConfig struct {
// Fields to populate (comma-separated or array)
Fields []string
// MaxDepth limits recursive population (default: 1)
MaxDepth int
// SelectFields limits which fields to return from related entries
SelectFields map[string][]string
}
PopulateConfig configures relation population
func DefaultPopulateConfig ¶
func DefaultPopulateConfig() *PopulateConfig
DefaultPopulateConfig returns the default populate configuration
func ParsePopulate ¶
func ParsePopulate(populate string) *PopulateConfig
ParsePopulate parses a populate string into config Format: "field1,field2" or "field1.subfield,field2"
type PopulateOption ¶
type PopulateOption struct {
// Path is the field path to populate (e.g., "author" or "author.avatar")
Path string `json:"path"`
// Select specifies which fields to include from the related entity
Select []string `json:"select,omitempty"`
// Populate nested relations
Populate []PopulateOption `json:"populate,omitempty"`
}
PopulateOption specifies how to populate a relation
type Populator ¶
type Populator struct {
// contains filtered or unexported fields
}
Populator handles relation population for queries
func (*Populator) GetRelatedEntries ¶
func (p *Populator) GetRelatedEntries(ctx context.Context, entryID xid.ID, fieldSlug string) ([]*schema.ContentEntry, error)
GetRelatedEntries returns the related entries for a field
func (*Populator) GetRelatedEntryIDs ¶
func (p *Populator) GetRelatedEntryIDs(ctx context.Context, entryID xid.ID, fieldSlug string) ([]xid.ID, error)
GetRelatedEntryIDs returns the IDs of related entries for a field
func (*Populator) GetReverseRelatedEntries ¶
func (p *Populator) GetReverseRelatedEntries(ctx context.Context, entryID xid.ID, fieldSlug string) ([]*schema.ContentEntry, error)
GetReverseRelatedEntries returns entries that reference this entry
func (*Populator) PopulateEntries ¶
func (p *Populator) PopulateEntries(ctx context.Context, entries []*schema.ContentEntry, config *PopulateConfig) error
PopulateEntries populates relations for a slice of entries
func (*Populator) PopulateEntry ¶
func (p *Populator) PopulateEntry(ctx context.Context, entry *schema.ContentEntry, config *PopulateConfig) error
PopulateEntry populates relations for a single entry
type Query ¶
type Query struct {
// Filters contains all filter conditions
Filters *FilterGroup `json:"filters,omitempty"`
// Sort specifies the sort order
Sort []SortField `json:"sort,omitempty"`
// Select specifies which fields to return (projection)
Select []string `json:"select,omitempty"`
// Populate specifies which relations to populate
Populate []PopulateOption `json:"populate,omitempty"`
// Pagination options
Page int `json:"page,omitempty"`
PageSize int `json:"pageSize,omitempty"`
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
// Search for full-text search
Search string `json:"search,omitempty"`
// Status filter (shortcut)
Status string `json:"status,omitempty"`
}
Query represents a parsed content query
func (*Query) AddFilter ¶
func (q *Query) AddFilter(field string, operator FilterOperator, value interface{}) *Query
AddFilter adds a filter condition to the query
func (*Query) AddPopulate ¶
AddPopulate adds a relation to populate
func (*Query) SetOffsetLimit ¶
SetOffsetLimit sets offset-based pagination
func (*Query) SetPagination ¶
SetPagination sets pagination options
type QueryBuilder ¶
type QueryBuilder struct {
// contains filtered or unexported fields
}
QueryBuilder builds Bun queries from parsed Query objects
func NewQueryBuilder ¶
func NewQueryBuilder(db *bun.DB, contentTypeID xid.ID, fields []*schema.ContentField) *QueryBuilder
NewQueryBuilder creates a new query builder
func (*QueryBuilder) Build ¶
func (b *QueryBuilder) Build(q *Query) *bun.SelectQuery
Build builds a Bun select query from a parsed Query
func (*QueryBuilder) BuildCount ¶
func (b *QueryBuilder) BuildCount(q *Query) *bun.SelectQuery
BuildCount builds a count query from a parsed Query
type QueryExecutor ¶
type QueryExecutor struct {
// contains filtered or unexported fields
}
QueryExecutor executes queries and returns results
func NewQueryExecutor ¶
func NewQueryExecutor(db *bun.DB) *QueryExecutor
NewQueryExecutor creates a new query executor
func (*QueryExecutor) Execute ¶
func (e *QueryExecutor) Execute(ctx context.Context, contentType *schema.ContentType, q *Query) (*QueryResult, error)
Execute executes a query and returns the results
func (*QueryExecutor) ExecuteAggregate ¶
func (e *QueryExecutor) ExecuteAggregate(ctx context.Context, contentType *schema.ContentType, q *AggregateQuery) ([]AggregateResult, error)
ExecuteAggregate executes an aggregation query
func (*QueryExecutor) ExecuteByID ¶
func (e *QueryExecutor) ExecuteByID(ctx context.Context, entryID xid.ID) (*schema.ContentEntry, error)
ExecuteByID executes a query to find a single entry by ID
func (*QueryExecutor) ExecuteCount ¶
func (e *QueryExecutor) ExecuteCount(ctx context.Context, contentType *schema.ContentType, q *Query) (int, error)
ExecuteCount executes a count query
func (*QueryExecutor) ExecuteDistinct ¶
func (e *QueryExecutor) ExecuteDistinct(ctx context.Context, contentType *schema.ContentType, field string, q *Query) ([]interface{}, error)
ExecuteDistinct returns distinct values for a field
func (*QueryExecutor) ExecuteExists ¶
func (e *QueryExecutor) ExecuteExists(ctx context.Context, contentType *schema.ContentType, q *Query) (bool, error)
ExecuteExists checks if any entries match the query
func (*QueryExecutor) ExecuteIDs ¶
func (e *QueryExecutor) ExecuteIDs(ctx context.Context, contentType *schema.ContentType, q *Query) ([]xid.ID, error)
ExecuteIDs returns just the IDs matching a query
type QueryResult ¶
type QueryResult struct {
Entries []*schema.ContentEntry `json:"entries"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
TotalItems int `json:"totalItems"`
TotalPages int `json:"totalPages"`
}
QueryResult holds the result of a query execution
type SearchConfig ¶
type SearchConfig struct {
// Query is the search query string
Query string
// Fields to search (if empty, searches all text fields)
Fields []string
// Language for text search (default: english)
Language string
// IncludeHighlights returns highlighted snippets
IncludeHighlights bool
// HighlightStartTag is the tag for highlight start (default: <mark>)
HighlightStartTag string
// HighlightEndTag is the tag for highlight end (default: </mark>)
HighlightEndTag string
// MinScore filters out low-relevance results
MinScore float64
}
SearchConfig configures full-text search behavior
func DefaultSearchConfig ¶
func DefaultSearchConfig() *SearchConfig
DefaultSearchConfig returns the default search configuration
type SearchResult ¶
type SearchResult struct {
Entry *schema.ContentEntry
Score float64
Highlights map[string]string
}
SearchResult represents a search result with ranking
type Searcher ¶
type Searcher struct {
// contains filtered or unexported fields
}
Searcher handles full-text search operations
func (*Searcher) Search ¶
func (s *Searcher) Search(ctx context.Context, contentTypeID xid.ID, config *SearchConfig, page, pageSize int) ([]*SearchResult, int, error)
Search performs a full-text search on content entries
type SimpleAggregateConfig ¶
type SimpleAggregateConfig struct {
// Operator is the aggregation type (count, sum, avg, min, max)
Operator AggregateOperator
// Field is the field to aggregate (for sum, avg, min, max)
Field string
// GroupBy is the field to group results by
GroupBy string
// Filters are optional filters to apply
Filters map[string]any
// DateTrunc truncates dates for time-based grouping (day, week, month, year)
DateTrunc string
}
SimpleAggregateConfig configures a simple aggregation query This is a simpler interface than AggregateQuery for common use cases
type SimpleAggregateResult ¶
type SimpleAggregateResult struct {
GroupValue any `json:"groupValue,omitempty"`
Count int `json:"count,omitempty"`
Sum float64 `json:"sum,omitempty"`
Avg float64 `json:"avg,omitempty"`
Min any `json:"min,omitempty"`
Max any `json:"max,omitempty"`
}
SimpleAggregateResult represents a simple aggregation result
type SortField ¶
type SortField struct {
// Field is the field name to sort by
Field string `json:"field"`
// Descending is true for DESC, false for ASC
Descending bool `json:"descending"`
}
SortField represents a field to sort by
type URLParser ¶
type URLParser struct{}
URLParser parses query parameters from URLs into Query objects
func (*URLParser) Parse ¶
Parse parses URL query parameters into a Query object Supported formats:
- filter[field]=op.value (e.g., filter[_meta.status]=eq.published)
- filter[field]=value (shorthand for eq)
- sort=-field (descending) or sort=field (ascending)
- page=1&pageSize=20
- offset=0&limit=20
- select=field1,field2
- populate=relation1,relation2
- search=text
System fields use _meta prefix: _meta.status, _meta.createdAt, _meta.updatedAt, etc. Legacy format (e.g., "status") is still supported for backward compatibility.