Skip to content

Commit

Permalink
Adition of Group Finder (#14)
Browse files Browse the repository at this point in the history
* added group finder

* updated readme

* added information of group finder to the main README

* minor fixes on documentation
  • Loading branch information
pedroegsilva authored Jul 8, 2022
1 parent 602c8ef commit caa84ab
Show file tree
Hide file tree
Showing 21 changed files with 2,801 additions and 8 deletions.
10 changes: 10 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,13 @@ load("@bazel_gazelle//:def.bzl", "gazelle")

# gazelle:prefix github.com/pedroegsilva/gofindthem
gazelle(name = "gazelle")

gazelle(
name = "gazelle-update",
args = [
"-from_file=go.mod",
"-to_macro=deps.bzl%go_dependencies",
"-prune",
],
command = "update-repos",
)
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ from which is heavily influenced by the [InfluxQL parser](https://github.com/inf

## Usage/Examples

There are 2 libraries on this repository, the DSL and the Finder.
There are 3 libraries/packages on this repository, the DSL, the Finder and the GroupFinder.

### Finder
The finder is used to manage multiple expressions. It will use the DSL to extract the terms and regex from each expression and use them to process the text with the appropriate engine.
Expand Down Expand Up @@ -80,6 +80,15 @@ And finally you can check which expressions were match on each text.

The full example can be found at `/examples/finder/main.go`

### GroupFinder
The Group finder is a package that adds another DSL to improve the maintainability
of the searched patterns and enables searches on specific fields of structured documents.
It allows the configuration to be split into 2 categories(rules and tags) so that the tags
can be used by multiple rules. The Rules also enables to check if a given tag was found on
a specific field for a structured document.
You can find more about the usage of the Group Finder at its [README](https://github.com/pedroegsilva/gofindthem/tree/main/group)


### DSL
#### Definition
The DSL uses 5 operators (AND, OR, NOT, R, INORD), terms (defined by "") and parentheses to form expressions. A valid expression can be:
Expand Down
14 changes: 7 additions & 7 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
name = "io_bazel_rules_go",
sha256 = "8e968b5fcea1d2d64071872b12737bbb5514524ee5f0a4f54f5920266c261acb",
sha256 = "685052b498b6ddfe562ca7a97736741d87916fe536623afb7da2824c0211c369",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip",
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.33.0/rules_go-v0.33.0.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.33.0/rules_go-v0.33.0.zip",
],
)

http_archive(
name = "bazel_gazelle",
sha256 = "62ca106be173579c0a167deb23358fdfe71ffa1e4cfdddf5582af26520f1c66f",
sha256 = "501deb3d5695ab658e82f6f6f549ba681ea3ca2a5fb7911154b5aa45596183fa",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.26.0/bazel-gazelle-v0.26.0.tar.gz",
"https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.26.0/bazel-gazelle-v0.26.0.tar.gz",
],
)

Expand All @@ -27,6 +27,6 @@ go_dependencies()

go_rules_dependencies()

go_register_toolchains(version = "1.16.7")
go_register_toolchains(version = "1.18.3")

gazelle_dependencies()
Empty file added examples/group/BUILD.bazel
Empty file.
18 changes: 18 additions & 0 deletions examples/group/finder/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")

go_library(
name = "finder_lib",
srcs = ["main.go"],
importpath = "github.com/pedroegsilva/gofindthem/examples/group/finder",
visibility = ["//visibility:private"],
deps = [
"//finder",
"//group/finder",
],
)

go_binary(
name = "finder",
embed = [":finder_lib"],
visibility = ["//visibility:public"],
)
158 changes: 158 additions & 0 deletions examples/group/finder/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package main

import (
"fmt"

"github.com/pedroegsilva/gofindthem/finder"
gfinder "github.com/pedroegsilva/gofindthem/group/finder"
)

func main() {
gofindthemRules := map[string][]string{
"tag1": {
`"string1"`,
`"string2"`,
},
"tag2": {
`"string3"`,
`"string4"`,
},
"tag3": {
`"string5"`,
`"string6"`,
},
"tag4": {
`"string7"`,
`"string8"`,
},
}

rules := map[string][]string{
"rule1": {`"tag1" or "tag2"`},
"rule2": {`"tag3:Field3.SomeField1" or "tag4"`},
"rule3": {`"tag3:Field3" or "tag4"`},
}

gft, err := finder.NewFinderWithExpressions(
&finder.CloudflareForkEngine{},
&finder.RegexpEngine{},
false,
gofindthemRules,
)

if err != nil {
panic(err)
}

gftg, err := gfinder.NewFinderWithRules(gft, rules)
if err != nil {
panic(err)
}

someObject := struct {
Field1 string
Field2 int
Field3 struct {
SomeField1 string
SomeField2 []string
}
}{
Field1: "some pretty text with string1",
Field2: 42,
Field3: struct {
SomeField1 string
SomeField2 []string
}{
SomeField1: "some pretty text with string5",
SomeField2: []string{"some pretty text with string5", "some pretty text with string2", "some pretty text with string3"},
},
}

matchedExpByFieldByTag, err := gftg.TagObject(someObject, gftg.GetFieldNames(), nil)
if err != nil {
panic(err)
}

for tag, expressionsByField := range matchedExpByFieldByTag {
fmt.Println("Tag: ", tag)
for field, exprs := range expressionsByField {
fmt.Println(" Field: ", field)
for exp := range exprs {
fmt.Println(" Expressions: ", exp)
}
}
}

res, err := gftg.ProcessObject(someObject, gftg.GetFieldNames(), nil)
if err != nil {
panic(err)
}
fmt.Println("ProcessObject: ", res)

fmt.Println("-----------------------------")
arr := []struct {
FieldN string
FieldX string
}{
{FieldN: "some pretty text with string5"},
{FieldN: "some pretty text with string2"},
{FieldN: "some pretty text with string3"},
}

matchedExpByFieldByTag2, err := gftg.TagObject(arr, nil, nil)
if err != nil {
panic(err)
}
for tag, expressionsByField := range matchedExpByFieldByTag2 {
fmt.Println("Tag: ", tag)
for field, exprs := range expressionsByField {
fmt.Println(" Field: ", field)
for exp := range exprs {
fmt.Println(" Expressions: ", exp)
}
}
}

res2, err := gftg.ProcessObject(arr, nil, nil)
if err != nil {
panic(err)
}
fmt.Println("ProcessObject2: ", res2)

fmt.Println("-----------------------------")
rawJson := `
{
"Field1": "some pretty text with string1",
"Field2": 42,
"Field3":
{
"SomeField1": "some pretty text with string5",
"SomeField2":
[
"some pretty text with string5",
"some pretty text with string2",
"some pretty text with string3"
]
}
}
`

matchedExpByFieldByTag3, err := gftg.TagJson(rawJson, gftg.GetFieldNames(), nil)
if err != nil {
panic(err)
}
for tag, expressionsByField := range matchedExpByFieldByTag3 {
fmt.Println("Tag: ", tag)
for field, exprs := range expressionsByField {
fmt.Println(" Field: ", field)
for exp := range exprs {
fmt.Println(" Expressions: ", exp)
}
}
}
res3, err := gftg.ProcessJson(rawJson, gftg.GetFieldNames(), nil)
if err != nil {
panic(err)
}
fmt.Println("ProcessJson: ", res3)
}
19 changes: 19 additions & 0 deletions finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,25 @@ func NewFinder(subEng SubstringEngine, rgxEng RegexEngine, caseSensitive bool) (
}
}

// NewFinderWithExpressions retruns a new instace of Finder with the
// expressions and tags given at expressionsByTag.
func NewFinderWithExpressions(
subEng SubstringEngine,
rgxEng RegexEngine,
caseSensitive bool,
expressionsByTag map[string][]string,
) (finder *Finder, err error) {
finder = NewFinder(subEng, rgxEng, caseSensitive)
for tag, expressions := range expressionsByTag {
err = finder.AddExpressionsWithTag(expressions, tag)
if err != nil {
return
}
}

return
}

// AddExpression adds the expression to the finder. It also collect
// and store the terms that are going to be used by the substring engine
// If the expression is malformed returns an error.
Expand Down
Empty file added group/BUILD.bazel
Empty file.
97 changes: 97 additions & 0 deletions group/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Group Finder
Group finder is a package that adds another DSL to improve the maintainability
of the searched patterns and enables searches on specific fields of structured documents.

## Finder
The finder is used to manage multiple rules. It will use the DSL along with the gofindthem finder
to verify if the tags where found on the specified fields.

### Usage
You will need 2 sets of expressions, one to define the patterns that are needed to
be searched with its tag and the second one with the expressions to define the tags
and field relations.

```golang
gofindthemRules := map[string][]string{
"tag1": {
`"string1"`,
`"string2"`,
},
"tag2": {
`"string3"`,
`"string4"`,
},
"tag3": {
`"string5"`,
`"string6"`,
},
"tag4": {
`"string7"`,
`"string8"`,
},
}

rules := map[string][]string{
"rule1": {`"tag1" or "tag2" and not "tag3"`},
"rule2": {`"tag3:Field3.SomeField1" or "tag4"`},
"rule3": {`"tag3:Field3" or "tag4"`},
}
```

With the 2 sets of expressions ready you will first need to create the
gofinthem finder and the group finder:

```golang
gft, err := finder.NewFinderWithExpressions(
&finder.CloudflareForkEngine{},
&finder.RegexpEngine{},
false,
gofindthemRules,
)

gftg, err := gfinder.NewFinderWithRules(gft, rules)
if err != nil {
panic(err)
}
```

Now its possible check which rules where evaluated as true on a text
or on a structured document:

```golang
// searching on a struct
res, err := gftg.ProcessObject(someObject, gftg.GetFieldNames(), nil)
if err != nil {
panic(err)
}
fmt.Println("ProcessObject: ", res)

// searching on a raw json
res3, err := gftg.ProcessJson(rawJson, gftg.GetFieldNames(), nil)
if err != nil {
panic(err)
}
fmt.Println("ProcessJson: ", res3)
```
The full example can be found at `/examples/group/finder/main.go`

## Group Finder DSL
### Definition
The DSL uses 3 operators (AND, OR, NOT), Tag (defined by "tag:(field)"),
where the field is optional, and parentheses to form expressions.
A valid expression can be:

- A single rule with or without a specific field. Eg: `"tag1"` `"tag1:field1"`
- The result of an operation. `"tag1" OR "tag2:field1"`
- An expression enclosed by parentheses `("tag1" OR "tag2:field1")`

Each operator functions as the following:

- **AND** - Uses the expression before and after it to solve them as a logical `AND` operator.
> (valid expression) AND (valid expression) eg: `"tag1" AND "tag2"`
- **OR** - Uses the expression before and after it to solve them as a logical `OR` operator.
> \<valid expression\> OR \<valid expression\> eg: `"tag1" OR "tag2"`
- **NOT** - Uses the expression after it to solve them as a logical `NOT` operator.
> NOT \<valid expression\> eg: `NOT "tag1"`
23 changes: 23 additions & 0 deletions group/dsl/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "dsl",
srcs = [
"expression.go",
"parser.go",
"scanner.go",
],
importpath = "github.com/pedroegsilva/gofindthem/group/dsl",
visibility = ["//visibility:public"],
)

go_test(
name = "dsl_test",
srcs = [
"expression_test.go",
"parser_test.go",
"scanner_test.go",
],
embed = [":dsl"],
deps = ["@com_github_stretchr_testify//assert"],
)
Loading

0 comments on commit caa84ab

Please sign in to comment.