https://github.com/flightaware/swift-speedtables
Framework for creating multi-way indexed tables in Swift, similar to Speedtables in Tcl
https://github.com/flightaware/swift-speedtables
Last synced: over 1 year ago
JSON representation
Framework for creating multi-way indexed tables in Swift, similar to Speedtables in Tcl
- Host: GitHub
- URL: https://github.com/flightaware/swift-speedtables
- Owner: flightaware
- Created: 2016-05-26T15:47:10.000Z (about 10 years ago)
- Default Branch: master
- Last Pushed: 2017-04-06T14:17:42.000Z (about 9 years ago)
- Last Synced: 2023-04-18T10:34:09.471Z (about 3 years ago)
- Language: Swift
- Homepage:
- Size: 95.7 KB
- Stars: 1
- Watchers: 14
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
## Overview
This is a work in progress. The goal of this project is to create a multi-way indexed relation in Swift. Right now creating an indexed table is pretty manual:
```swift
// Manually generated speedtable definition. This could be automatically generated
// from something SQL-ish like:
// TABLE Table (
// String name indexed
// Int age indexed
// String school optional
// String studentID unique optional indexed
// )
class Table: SpeedTable {
let nameIndex: SkipList
let ageIndex: SkipList
let studentIDIndex: SkipList
init(maxLevel: Int) {
nameIndex = SkipList(maxLevel: maxLevel)
ageIndex = SkipList(maxLevel: maxLevel)
studentIDIndex = SkipList(maxLevel: maxLevel)
}
init(size: Int) {
nameIndex = SkipList(maxNodes: size)
ageIndex = SkipList(maxNodes: size)
studentIDIndex = SkipList(maxNodes: size)
}
func insert(name: String, age: Int) -> TableRow {
// Creating the table row does all the insertion stuff
return TableRow(parent: self, name: name, age: age)
}
func delete(row: TableRow) {
// delegate to row
row.delete()
}
}
// Each speedtable requires two classes, one for the table as a whole, one for
// the row holding the data
class TableRow: SpeedTableRow, Equatable {
var parent: Table?
var name: String {
willSet { parent!.nameIndex.delete(name, value: self) }
didSet { parent!.nameIndex.insert(name, value: self) }
}
var age: Int {
willSet { parent!.ageIndex.delete(age, value: self) }
didSet { parent!.ageIndex.insert(age, value: self) }
}
var school: String? // Unindexed value
var studentIDStorage: String? // unique optional value
func getStudentID() -> String? {
return studentIDStorage
}
func setStudentID(ID: String?) throws {
if let key = ID {
if parent!.studentIDIndex.exists(key) {
throw SpeedTableError.KeyNotUnique(key: key)
}
}
parent!.studentIDIndex.replace(ID, keyStore: &studentIDStorage, value: self)
}
init(parent: Table, name: String, age: Int) {
self.parent = parent
self.name = name
self.age = age
// This needs to be done explicitly because the willSet/didSet doesn't
// fire on initialization.
parent.nameIndex.insert(self.name, value: self)
parent.ageIndex.insert(self.age, value: self)
}
func delete() {
parent!.nameIndex.delete(name, value: self)
parent!.ageIndex.delete(age, value:self)
if let ID = studentIDStorage {
parent!.studentIDIndex.delete(ID, value:self)
}
parent = nil // do not modify a row after it's deleted!
}
}
// This function can be anything guaranteed unique for the table. We're using === here
// but it can be a unique key within the row instead, if there is one. Possibly a "primary
// key" field in the generator can be used to drive this.
func ==(lhs: TableRow, rhs: TableRow) -> Bool {
return lhs === rhs
}
```
Eventually, we would like to have this created by a little applet that takes a Tcl
or SQL-ish speedtable definition and generates this framework.
There are a few operations you can do on a table right now. The first two are implicit
in the above definition.
* ```table.insert(column, column, column...)```
Insert a new row into the table, and all indexes, returns the row... or you can search for the row later.
* ```table.delete(row)```
Delete a row from the table, unthreads it from all the indexes and unlinks it from the table.
* ```row.column = value```
When you update a column in a row, it updates the index on the column automatically.
The rest of the operations are implied by the behaviour of the indexes (skiplists), for
example:
```swift
for row in table.nameIndex.search(equal: myName) {
if(row.age > = 16) {
row.school = "senior"
}
}
```
Indexes are skiplists:
* ```thingIndex = SkipList(maxLevel: maxLevel)```
* ```thingIndex = SkipList(maxSize: maxSize)```
** maxLevel - maximum depth of the list, at least log(2) maximum nodes
** maxSize - maximum number of nodes (convenience init)
** unique - true if the index is unique
The operations on indexes are:
* ```skiplist.search(key)```
Search looks up a key and returns an array of rows that match the key. This is just a simple skiplist lookup.
* ```skiplist.insert(key, value)```
* ```skiplist.delete(key, value)```
You will not be using these directly in speedtables. The insert and delete a key-value pair from the index.
* ```skiplist.exists(key)```
* ```skiplist.exists(key, value)```
Helper functions for handling unique lists.
* ```for (key, value) in skiplist```
Walks the entire skiplist and returns all the key-value pairs. The value in a speedtable will be a speedtable row.
* ```for (key, value) in skiplist.query(lessThan: key)```
* ```for (key, value) in skiplist.query(lessThanOrEqual: key)```
* ```for (key, value) in skiplist.query(greaterThan: key)```
* ```for (key, value) in skiplist.query(greaterThanOrEqual: key)```
* ```for (key, value) in skiplist.query(from: key, to: key)```
* ```for (key, value) in skiplist.query(from: key, through: key)```
There is no "equalTo" because skiplist.search() already provides this functionality
directly.
These are all convenience functions on the general:
* ```for (key value) in skiplist.query(min: key, max: key, minEquals: Bool, maxEquals: Bool)```
The query object actually suports two mechanisms for walking the results, either the
generator implied above, or the functions:
* ```(key, value)? = query.first()```
* ```(key, value)? = query.next()```