Skip to content

Commit

Permalink
add example
Browse files Browse the repository at this point in the history
  • Loading branch information
kelindar committed Oct 29, 2022
1 parent 5034dbc commit 4ff28c4
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 12 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ for _, v := range loadFromJson("players.json") {
While the previous example demonstrated how to insert many objects, it was doing it one by one and is rather inefficient. This is due to the fact that each `InsertObject()` call directly on the collection initiates a separate transacion and there's a small performance cost associated with it. If you want to do a bulk insert and insert many values, faster, that can be done by calling `Insert()` on a transaction, as demonstrated in the example below. Note that the only difference is instantiating a transaction by calling the `Query()` method and calling the `txn.Insert()` method on the transaction instead the one on the collection.

```go
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
for _, v := range loadFromJson("players.json") {
txn.InsertObject(v)
}
Expand Down Expand Up @@ -129,7 +129,7 @@ First, let's try to merge two queries by applying a `Union()` operation with the

```go
// How many rogues and mages?
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
txn.With("rogue").Union("mage").Count()
return nil
})
Expand All @@ -139,7 +139,7 @@ Next, let's count everyone who isn't a rogue, for that we can use a `Without()`

```go
// How many rogues and mages?
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
txn.Without("rogue").Count()
return nil
})
Expand All @@ -149,7 +149,7 @@ Now, you can combine all of the methods and keep building more complex queries.

```go
// How many rogues that are over 30 years old?
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
txn.With("rogue").WithFloat("age", func(v float64) bool {
return v >= 30
}).Count()
Expand All @@ -168,7 +168,7 @@ In order to access the results of the iteration, prior to calling `Range()` meth
In the example below we select all of the rogues from our collection and print out their name by using the `Range()` method and accessing the "name" column using a column reader which is created by calling `txn.String("name")` method.

```go
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
names := txn.String("name") // Create a column reader

return txn.With("rogue").Range(func(i uint32) {
Expand All @@ -181,7 +181,7 @@ players.Query(func(txn *Txn) error {
Similarly, if you need to access more columns, you can simply create the appropriate column reader(s) and use them as shown in the example before.

```go
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
names := txn.String("name")
ages := txn.Int64("age")

Expand All @@ -198,7 +198,7 @@ players.Query(func(txn *Txn) error {
Taking the `Sum()` of a (numeric) column reader will take into account a transaction's current filtering index.

```go
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
totalAge := txn.With("rouge").Int64("age").Sum()
totalRouges := int64(txn.Count())

Expand All @@ -221,7 +221,7 @@ In order to update certain items in the collection, you can simply call `Range()
In the example below we're selecting all of the rogues and updating both their balance and age to certain values. The transaction returns `nil`, hence it will be automatically committed when `Query()` method returns.

```go
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
balance := txn.Float64("balance")
age := txn.Int64("age")

Expand All @@ -235,7 +235,7 @@ players.Query(func(txn *Txn) error {
In certain cases, you might want to atomically increment or decrement numerical values. In order to accomplish this you can use the provided `Merge()` operation. Note that the indexes will also be updated accordingly and the predicates re-evaluated with the most up-to-date values. In the below example we're incrementing the balance of all our rogues by _500_ atomically.

```go
players.Query(func(txn *Txn) error {
players.Query(func(txn *column.Txn) error {
balance := txn.Float64("balance")

return txn.With("rogue").Range(func(i uint32) {
Expand All @@ -256,17 +256,17 @@ concat := func(value, delta string) string {
}

// Create a column with a specified merge function
db := NewCollection()
db := column.NewCollection()
db.CreateColumn("alphabet", column.ForString(column.WithMerge(concat)))

// Insert letter "A"
db.Insert(func(r Row) error {
db.Insert(func(r column.Row) error {
r.SetString("alphabet", "A") // now contains "A"
return nil
})

// Insert letter "B"
db.QueryAt(0, func(r Row) error {
db.QueryAt(0, func(r column.Row) error {
r.MergeString("alphabet", "B") // now contains "A, B"
return nil
})
Expand Down
30 changes: 30 additions & 0 deletions examples/merge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Custom merging strategy

In this example we are creating a column called `location` that contains a JSON-encoded position and velocity. The position is updated by calling a `MergeString()` function with the velocity vector, the updates are then merged atomically using a merging function specified.

The merge happens when transaction is committed to ensure consistency. Hence, this technique allows for two concurrent transactions to update the same position.

## Example output

```json
00: {"position":[1,2],"velocity":[1,2]}
01: {"position":[2,4],"velocity":[1,2]}
02: {"position":[3,6],"velocity":[1,2]}
03: {"position":[4,8],"velocity":[1,2]}
04: {"position":[5,10],"velocity":[1,2]}
05: {"position":[6,12],"velocity":[1,2]}
06: {"position":[7,14],"velocity":[1,2]}
07: {"position":[8,16],"velocity":[1,2]}
08: {"position":[9,18],"velocity":[1,2]}
09: {"position":[10,20],"velocity":[1,2]}
10: {"position":[11,22],"velocity":[1,2]}
11: {"position":[12,24],"velocity":[1,2]}
12: {"position":[13,26],"velocity":[1,2]}
13: {"position":[14,28],"velocity":[1,2]}
14: {"position":[15,30],"velocity":[1,2]}
15: {"position":[16,32],"velocity":[1,2]}
16: {"position":[17,34],"velocity":[1,2]}
17: {"position":[18,36],"velocity":[1,2]}
18: {"position":[19,38],"velocity":[1,2]}
19: {"position":[20,40],"velocity":[1,2]}
```
103 changes: 103 additions & 0 deletions examples/merge/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) Roman Atachiants and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.

package main

import (
"encoding/json"
"fmt"

"github.com/kelindar/column"
)

// Movement represents a movement with a position and velocity
type Movement struct {
Position [2]float64 `json:"position,omitempty"`
Velocity [2]float64 `json:"velocity,omitempty"`
}

func main() {

// A merging function that accepts a velocity vector and updates
// the movement structure accordingly.
mergeVectors := func(value, delta string) string {
movement, ok := parseMovement(value)
if !ok {
movement = Movement{
Position: [2]float64{0, 0},
}
}

// Parse the incoming delta value
velocity, ok := parseVector(delta)
if !ok {
return value
}

// Update the current velocity and recalculate the position
movement.Velocity = velocity
movement.Position[0] += velocity[0] // Update X
movement.Position[1] += velocity[1] // Update Y

// Encode the movement as JSON and return the updated value
return encode(movement)
}

// Create a column with a specified merge function
db := column.NewCollection()
db.CreateColumn("location", column.ForString(
column.WithMerge(mergeVectors), // use our merging function
))

// Insert an empty row
id, _ := db.Insert(func(r column.Row) error {
r.SetString("location", "{}")
return nil
})

// Update 100 times
for i := 0; i < 20; i++ {

// Move the location by applying a same velocity vector
db.Query(func(txn *column.Txn) error {
location := txn.String("location")
return txn.QueryAt(id, func(r column.Row) error {
location.Merge(encode([2]float64{1, 2}))
return nil
})
})

// Print out current location
db.Query(func(txn *column.Txn) error {
location := txn.String("location")
return txn.QueryAt(id, func(r column.Row) error {
value, _ := location.Get()
fmt.Printf("%.2d: %v \n", i, value)
return nil
})
})
}

}

// parseMovement parses a value string into a Movement struct
func parseMovement(value string) (out Movement, ok bool) {
if err := json.Unmarshal([]byte(value), &out); err != nil {
return Movement{}, false
}
return out, true
}

// parseVector parses a value string into 2 dimensional array
func parseVector(value string) (out [2]float64, ok bool) {
if err := json.Unmarshal([]byte(value), &out); err != nil {
return [2]float64{}, false
}
return out, true
}

// encodes encodes the value as JSON
func encode(value any) string {
encoded, _ := json.Marshal(value)
return string(encoded)
}

0 comments on commit 4ff28c4

Please sign in to comment.