Documentation ¶
Overview ¶
Package quamina instances support adding Patterns and then presenting Events, generating a report of which Patterns match the Event. Patterns and Events are both represented as JSON objects, although there is a provided Flattener interface by which structured objects in formats other than JSON can be matched by quamina. Quamina instances match Events quickly and with a latency that is not strongly affected by the number of Patterns which have been added.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ArrayPos ¶
ArrayPos represents a Field's position in an Event's structure. Each array in the Event should get an integer which identifies it - in flattenJSON this is accomplished by keeping a counter and giving arrays numbers starting from 0. ArrayPos exists to ensure that Quamina MatchesForEvent will not return a match where two of the matching fields are in separate elements of the same array. Array uniquely identifies an array in an Event. Pos is the Field's index in the Array.
type Field ¶
Field represents a pathname/value combination, one of the data items which is matched against Patterns by the MatchesForEvent API. Path is the \n-separated path from the event root to this field value. Val is the value, a []byte forming a textual representation of the type ArrayTrail, for each array in the Path, identifies the array and the index in it.
type Flattener ¶
type Flattener interface { Flatten(event []byte, tracker NameTracker) ([]Field, error) Copy() Flattener }
nolint:goimports,gofmt Flattener is an interface which provides methods to turn a data structure into a list of path-names and values. The following example illustrates how it works for a JSON object: { "a": 1, "b": "two", "c": true", "d": nil, "e": { "e1": 2, "e2":, 3.02e-5} "f": [33, "x"]} } should produce
"a", "1" "b", "\"two\"", "c", "true" "d", "nil", "e\ne1", "2" "e\ne2", "3.02e-5" "f", "33" "f", "\"x\""
Let's call the first column, eg "d" and "e\ne1", the path. For each step i the path, e.g. "d" and "e1", the Flattener should call NameTracker.IsNameUsed(step) and if that comes back negative, not include any paths which don't contain that step. So in the example above, if nameTracker.IsNameUsed() only came back true for "a" and "f", then the output would be
"a", "1" "f", "33" "f", "\"x\""
type LivePatternsState ¶
type LivePatternsState interface { // Add adds a new pattern or updates an old pattern. // // Note that multiple patterns can be associated with the same X. Add(x X, pattern string) error // Delete removes all patterns associated with the given X and returns the // number of removed patterns. Delete(x X) (int, error) // Iterate calls the given function for every stored pattern. Iterate(func(x X, pattern string) error) error // Contains returns true if x is in the live set; false otherwise. Contains(x X) (bool, error) }
LivePatternsState represents the required capabilities for maintaining the set of live patterns.
type NameTracker ¶
nolint:goimports,gofmt NameTracker is an interface representing a wrapper for a set of byte slices. The intended use is by a Flattener which is traversing a data object with the goal of turning it into a list of name/value pairs. The cost of Quamina's MatchesForEvent API is strongly related to the number of fields it has to look at. Therefore, a Flattener should call NameTracker for each field it encounters and if it returns false, skip that field and not add it to the flattened output. Here's a clarifying example; consider this JSON event:
{"a": {"b": 1, "c": 2}}
Assuming NameTracker always returns positive, the flattened output should look like
"a\nb" 1 "a\nc" 2
Note that the values stored in NameTracker are not the full paths "a\nb" and "a\nc", but the path segments "a", "b", and "c". This reduces the work the Flattener has to do - whenever it processes an object, it need only look up the member names in the NameTracker. A little thought reveals that this can produce false positives and some potential wasted work, but is a good trade-off.
type Option ¶
Option is an interface type used in Quamina's New API to pass in options. By convention, Option names have a prefix of "With".
func WithFlattener ¶
WithFlattener allows the specification of a caller-provided Flattener instance to use on incoming Events. This option call may not be provided more than once, nor can it be combined on the same invocation of quamina.New() with the WithMediaType() option.
func WithMediaType ¶
WithMediaType provides a media-type to support the selection of an appropriate Flattener. This option call may not be provided more than once, nor can it be combined on the same invocation of quamina.New() with the WithFlattener() option.
func WithPatternDeletion ¶
WithPatternDeletion arranges, if the argument is true, that this Quamina instance will support the DeletePatterns() method. This option call may not be provided more than once.
func WithPatternStorage ¶
func WithPatternStorage(ps LivePatternsState) Option
WithPatternStorage supplies the Quamina instance with a LivePatternState instance to be used to store the active patterns, i.e. those that have been added with AddPattern but not deleted with DeletePattern. This option call may not be provided more than once.
type Quamina ¶
type Quamina struct {
// contains filtered or unexported fields
}
Quamina instances provide the public APIs of this pattern-matching library. A single Quamina instance is not thread-safe in that it cannot safely be used simultaneously in multiple goroutines. To re-use a Quamina instance concurrently in multiple goroutines, create copies using the Copy API.
func New ¶
New returns a new Quamina instance. Consult the APIs beginning with “With” for the options that may be used to configure the new instance.
Example ¶
package main import ( "fmt" "log" "github.com/timbray/quamina" ) const userRegisteredEvent = `{ "id": "1c0e1ce4-3d88-4786-a09d-7133c170d02a", "type": "UserRegistered", "user": { "name": "Doe, John", "premiumAccount": true } } ` const premiumUserPattern = `{ "type":["UserRegistered"], "user": {"premiumAccount": [true]} }` func main() { q, err := quamina.New() if err != nil { log.Fatalf("could not create quamina instance: %v", err) } const patternName = "premium user" err = q.AddPattern(patternName, premiumUserPattern) if err != nil { log.Fatalf("could not add pattern: %v", err) } matches, err := q.MatchesForEvent([]byte(userRegisteredEvent)) if err != nil { log.Fatalf("could not match for event: %v", err) } for _, m := range matches { if m == patternName { fmt.Printf("pattern matched for event: %q", patternName) return } } // you would typically handle no matches cases here, but in this example no // match is a bug, hence panic :) panic("no pattern match") }
Output: pattern matched for event: "premium user"
func (*Quamina) AddPattern ¶
AddPattern adds a pattern, identified by the x argument, to a Quamina instance. patternJSON is a JSON object. error is returned in the case that the PatternJSON is invalid JSON or has a leaf which is not provided as an array. AddPattern is single-threaded; if it is invoked concurrently from multiple goroutines (in instances created using the Copy method) calls will block until any other AddPattern call in progress succeeds.
func (*Quamina) Copy ¶
Copy produces a new Quamina instance designed to be used safely in parallel with existing instances on different goroutines. Copy'ed instances share the same underlying data structures, so a pattern added to any instance with AddPattern will be visible in all of them.
func (*Quamina) DeletePatterns ¶
DeletePatterns removes patterns identified by the x argument from the Quamina instance; the effect is that return values from future calls to MatchesForEvent will not include this x value.
func (*Quamina) MatchesForEvent ¶
MatchesForEvent returns a slice of X values which identify patterns that have previously been added to this Quamina instance and which “match” the event in the sense described in README. The matches slice may be empty if no patterns match. error can be returned in case that the event is not a valid JSON object or contains invalid UTF-8 byte sequences.