All checks were successful
ci / Build (push) Successful in 3m40s
- Fixed the 'M' bind to mark all/none items, rather than toggle - Fixed panic which was raised when reqesting @item with an index beyond the length of resultset - Added changing the table when calling @table with a table or string name
221 lines
4.7 KiB
Go
221 lines
4.7 KiB
Go
package models
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
|
)
|
|
|
|
type ResultSet struct {
|
|
// Query information
|
|
TableInfo *TableInfo
|
|
Query Queryable
|
|
Created time.Time
|
|
ExclusiveStartKey map[string]types.AttributeValue
|
|
|
|
// Result information
|
|
LastEvaluatedKey map[string]types.AttributeValue
|
|
items []Item
|
|
attributes []ItemAttribute
|
|
|
|
columns []string
|
|
sortCriteria SortCriteria
|
|
|
|
mutex sync.Mutex
|
|
cachedMarkedItems []ItemIndex
|
|
hasCachedMarkedItems bool
|
|
}
|
|
|
|
type Queryable interface {
|
|
String() string
|
|
SerializeToBytes() ([]byte, error)
|
|
HashCode() uint64
|
|
Plan(tableInfo *TableInfo) (*QueryExecutionPlan, error)
|
|
}
|
|
|
|
type ItemAttribute struct {
|
|
Marked bool
|
|
Hidden bool
|
|
Dirty bool
|
|
New bool
|
|
}
|
|
|
|
func (rs *ResultSet) NoResults() bool {
|
|
return len(rs.items) == 0 && rs.LastEvaluatedKey == nil
|
|
}
|
|
|
|
func (rs *ResultSet) Items() []Item {
|
|
return rs.items
|
|
}
|
|
|
|
func (rs *ResultSet) SetItems(items []Item) {
|
|
rs.items = items
|
|
rs.attributes = make([]ItemAttribute, len(items))
|
|
|
|
rs.mutex.Lock()
|
|
defer rs.mutex.Unlock()
|
|
rs.hasCachedMarkedItems = false
|
|
rs.cachedMarkedItems = nil
|
|
}
|
|
|
|
func (rs *ResultSet) SortCriteria() SortCriteria {
|
|
return rs.sortCriteria
|
|
}
|
|
|
|
func (rs *ResultSet) AddNewItem(item Item, attrs ItemAttribute) {
|
|
rs.items = append(rs.items, item)
|
|
rs.attributes = append(rs.attributes, attrs)
|
|
|
|
rs.mutex.Lock()
|
|
defer rs.mutex.Unlock()
|
|
rs.hasCachedMarkedItems = false
|
|
rs.cachedMarkedItems = nil
|
|
}
|
|
|
|
func (rs *ResultSet) SetMark(idx int, marked bool) {
|
|
rs.attributes[idx].Marked = marked
|
|
|
|
if !rs.hasCachedMarkedItems {
|
|
return
|
|
}
|
|
|
|
rs.mutex.Lock()
|
|
defer rs.mutex.Unlock()
|
|
rs.hasCachedMarkedItems = false
|
|
rs.cachedMarkedItems = nil
|
|
}
|
|
|
|
func (rs *ResultSet) SetHidden(idx int, hidden bool) {
|
|
rs.attributes[idx].Hidden = hidden
|
|
}
|
|
|
|
func (rs *ResultSet) SetDirty(idx int, dirty bool) {
|
|
rs.attributes[idx].Dirty = dirty
|
|
}
|
|
|
|
func (rs *ResultSet) SetNew(idx int, isNew bool) {
|
|
rs.attributes[idx].New = isNew
|
|
}
|
|
|
|
func (rs *ResultSet) Marked(idx int) bool {
|
|
return rs.attributes[idx].Marked
|
|
}
|
|
|
|
func (rs *ResultSet) Hidden(idx int) bool {
|
|
return rs.attributes[idx].Hidden
|
|
}
|
|
|
|
func (rs *ResultSet) IsDirty(idx int) bool {
|
|
return rs.attributes[idx].Dirty
|
|
}
|
|
|
|
func (rs *ResultSet) IsNew(idx int) bool {
|
|
return rs.attributes[idx].New
|
|
}
|
|
|
|
func (rs *ResultSet) MarkedItems() []ItemIndex {
|
|
rs.mutex.Lock()
|
|
defer rs.mutex.Unlock()
|
|
if rs.hasCachedMarkedItems {
|
|
return rs.cachedMarkedItems
|
|
}
|
|
|
|
items := make([]ItemIndex, 0)
|
|
for i, itemAttr := range rs.attributes {
|
|
if itemAttr.Marked && !itemAttr.Hidden {
|
|
items = append(items, ItemIndex{Index: i, Item: rs.items[i]})
|
|
}
|
|
}
|
|
rs.cachedMarkedItems = items
|
|
rs.hasCachedMarkedItems = true
|
|
return items
|
|
}
|
|
|
|
func (rs *ResultSet) Columns() []string {
|
|
if rs.columns == nil {
|
|
rs.RefreshColumns()
|
|
}
|
|
return rs.columns
|
|
}
|
|
|
|
func (rs *ResultSet) RefreshColumns() {
|
|
seenColumns := make(map[string]int)
|
|
seenColumns[rs.TableInfo.Keys.PartitionKey] = 0
|
|
if rs.TableInfo.Keys.SortKey != "" {
|
|
seenColumns[rs.TableInfo.Keys.SortKey] = 1
|
|
}
|
|
|
|
for _, definedAttribute := range rs.TableInfo.DefinedAttributes {
|
|
if _, seen := seenColumns[definedAttribute]; !seen {
|
|
seenColumns[definedAttribute] = len(seenColumns)
|
|
}
|
|
}
|
|
|
|
otherColsRank := len(seenColumns)
|
|
for _, result := range rs.items {
|
|
for k := range result {
|
|
if _, isSeen := seenColumns[k]; !isSeen {
|
|
seenColumns[k] = otherColsRank
|
|
}
|
|
}
|
|
}
|
|
|
|
columns := make([]string, 0, len(seenColumns))
|
|
for k := range seenColumns {
|
|
columns = append(columns, k)
|
|
}
|
|
sort.Slice(columns, func(i, j int) bool {
|
|
if seenColumns[columns[i]] == seenColumns[columns[j]] {
|
|
return columns[i] < columns[j]
|
|
}
|
|
return seenColumns[columns[i]] < seenColumns[columns[j]]
|
|
})
|
|
|
|
rs.columns = columns
|
|
}
|
|
|
|
func (rs *ResultSet) HasNextPage() bool {
|
|
return rs.LastEvaluatedKey != nil
|
|
}
|
|
|
|
func (rs *ResultSet) Sort(criteria SortCriteria) {
|
|
rs.sortCriteria = criteria
|
|
Sort(rs.items, criteria)
|
|
}
|
|
|
|
func (rs *ResultSet) MergeWith(otherRS *ResultSet) *ResultSet {
|
|
type pksk struct {
|
|
pk types.AttributeValue
|
|
sk types.AttributeValue
|
|
}
|
|
|
|
if !rs.TableInfo.Equal(otherRS.TableInfo) {
|
|
return nil
|
|
}
|
|
|
|
itemsInI := make(map[pksk]Item)
|
|
newItems := make([]Item, 0, len(rs.Items())+len(otherRS.Items()))
|
|
for _, item := range rs.Items() {
|
|
pk, sk := item.PKSK(rs.TableInfo)
|
|
itemsInI[pksk{pk, sk}] = item
|
|
newItems = append(newItems, item)
|
|
}
|
|
|
|
for _, item := range otherRS.Items() {
|
|
pk, sk := item.PKSK(rs.TableInfo)
|
|
if _, hasItem := itemsInI[pksk{pk, sk}]; !hasItem {
|
|
newItems = append(newItems, item)
|
|
}
|
|
}
|
|
|
|
newResultSet := &ResultSet{
|
|
Created: time.Now(),
|
|
TableInfo: rs.TableInfo,
|
|
}
|
|
newResultSet.SetItems(newItems)
|
|
|
|
return newResultSet
|
|
}
|