Some checks failed
Coverage / coverage (push) Failing after 6s
Tests / test (1.24.x, ubuntu-latest) (push) Failing after 42s
Tests / test (1.26.x, ubuntu-latest) (push) Failing after 38s
Tests / test (1.25.x, ubuntu-latest) (push) Failing after 35s
Tests / test (1.25.x, windows-latest) (push) Has been cancelled
Tests / test (1.26.x, macos-latest) (push) Has been cancelled
Tests / test (1.26.x, windows-latest) (push) Has been cancelled
Tests / test (1.24.x, macos-latest) (push) Has been cancelled
Tests / test (1.24.x, windows-latest) (push) Has been cancelled
Tests / test (1.25.x, macos-latest) (push) Has been cancelled
291 lines
6.7 KiB
Go
291 lines
6.7 KiB
Go
// Copyright (c) 2016 Couchbase, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package facet
|
|
|
|
import (
|
|
"os"
|
|
"regexp"
|
|
"testing"
|
|
)
|
|
|
|
var terms []string
|
|
|
|
func init() {
|
|
wsRegexp := regexp.MustCompile(`\W+`)
|
|
input, err := os.ReadFile("benchmark_data.txt")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
terms = wsRegexp.Split(string(input), -1)
|
|
}
|
|
|
|
func BenchmarkTermsFacet10(b *testing.B) {
|
|
termsFacetN(b, 10)
|
|
}
|
|
|
|
func BenchmarkTermsFacet100(b *testing.B) {
|
|
termsFacetN(b, 100)
|
|
}
|
|
|
|
func BenchmarkTermsFacet1000(b *testing.B) {
|
|
termsFacetN(b, 1000)
|
|
}
|
|
|
|
func BenchmarkTermsFacet10000(b *testing.B) {
|
|
termsFacetN(b, 10000)
|
|
}
|
|
|
|
// func BenchmarkTermsFacet100000(b *testing.B) {
|
|
// termsFacetN(b, 100000)
|
|
// }
|
|
|
|
func termsFacetN(b *testing.B, numTerms int) {
|
|
field := "test"
|
|
termsLen := len(terms)
|
|
tfb := NewTermsFacetBuilder(field, 3)
|
|
i := 0
|
|
for len(tfb.termsCount) < numTerms && i <= termsLen {
|
|
j := i % termsLen
|
|
term := terms[j]
|
|
tfb.StartDoc()
|
|
tfb.UpdateVisitor([]byte(term))
|
|
tfb.EndDoc()
|
|
i++
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
tfb.Result()
|
|
}
|
|
}
|
|
|
|
func TestTermsFacetPrefix(t *testing.T) {
|
|
field := "category"
|
|
tfb := NewTermsFacetBuilder(field, 10)
|
|
tfb.SetPrefixFilter("prod-")
|
|
|
|
// Add terms with various prefixes
|
|
terms := []string{
|
|
"prod-server",
|
|
"prod-database",
|
|
"dev-server",
|
|
"dev-database",
|
|
"test-server",
|
|
"prod-cache",
|
|
}
|
|
|
|
for _, term := range terms {
|
|
tfb.StartDoc()
|
|
tfb.UpdateVisitor([]byte(term))
|
|
tfb.EndDoc()
|
|
}
|
|
|
|
result := tfb.Result()
|
|
|
|
// Should only have terms with "prod-" prefix
|
|
if result.Terms.Len() != 3 {
|
|
t.Fatalf("expected 3 matching terms, got %d", result.Terms.Len())
|
|
}
|
|
|
|
// Verify the terms are correct
|
|
expectedTerms := map[string]bool{
|
|
"prod-server": true,
|
|
"prod-database": true,
|
|
"prod-cache": true,
|
|
}
|
|
|
|
for _, facet := range result.Terms.Terms() {
|
|
if !expectedTerms[facet.Term] {
|
|
t.Errorf("unexpected term in results: %s", facet.Term)
|
|
}
|
|
if facet.Count != 1 {
|
|
t.Errorf("expected count 1 for %s, got %d", facet.Term, facet.Count)
|
|
}
|
|
}
|
|
|
|
// Total should include all terms (matching + non-matching)
|
|
if result.Total != 6 {
|
|
t.Errorf("expected total 6, got %d", result.Total)
|
|
}
|
|
|
|
// Other should be 3 (the non-matching terms)
|
|
if result.Other != 3 {
|
|
t.Errorf("expected other 3, got %d", result.Other)
|
|
}
|
|
}
|
|
|
|
func TestTermsFacetRegex(t *testing.T) {
|
|
field := "product_code"
|
|
// Match pattern: ABC-#### (3 letters, dash, 4 digits) - pattern: ^[A-Z]{3}-\\d{4}$
|
|
tfb := NewTermsFacetBuilder(field, 10)
|
|
regex, err := regexp.Compile("^[A-Z]{3}-\\d{4}$")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tfb.SetRegexFilter(regex)
|
|
|
|
// Add terms with various formats
|
|
terms := []string{
|
|
"ABC-1234",
|
|
"XYZ-5678",
|
|
"ABC-999", // too few digits
|
|
"ABCD-1234", // too many letters
|
|
"ABC-ABCD", // letters instead of digits
|
|
"DEF-0000",
|
|
}
|
|
|
|
for _, term := range terms {
|
|
tfb.StartDoc()
|
|
tfb.UpdateVisitor([]byte(term))
|
|
tfb.EndDoc()
|
|
}
|
|
|
|
result := tfb.Result()
|
|
|
|
// Should only have 3 terms matching the pattern
|
|
if result.Terms.Len() != 3 {
|
|
t.Fatalf("expected 3 matching terms, got %d", result.Terms.Len())
|
|
}
|
|
|
|
// Verify the terms are correct
|
|
expectedTerms := map[string]bool{
|
|
"ABC-1234": true,
|
|
"XYZ-5678": true,
|
|
"DEF-0000": true,
|
|
}
|
|
|
|
for _, facet := range result.Terms.Terms() {
|
|
if !expectedTerms[facet.Term] {
|
|
t.Errorf("unexpected term in results: %s", facet.Term)
|
|
}
|
|
if facet.Count != 1 {
|
|
t.Errorf("expected count 1 for %s, got %d", facet.Term, facet.Count)
|
|
}
|
|
}
|
|
|
|
// Total should include all terms
|
|
if result.Total != 6 {
|
|
t.Errorf("expected total 6, got %d", result.Total)
|
|
}
|
|
|
|
// Other should be 3 (the non-matching terms)
|
|
if result.Other != 3 {
|
|
t.Errorf("expected other 3, got %d", result.Other)
|
|
}
|
|
}
|
|
|
|
func TestTermsFacetPrefixAndRegex(t *testing.T) {
|
|
field := "tag"
|
|
// Both prefix "env:" and regex pattern for prod/staging only
|
|
tfb := NewTermsFacetBuilder(field, 10)
|
|
tfb.SetPrefixFilter("env:")
|
|
regex, err := regexp.Compile("^env:(prod|staging)$")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tfb.SetRegexFilter(regex)
|
|
|
|
// Add various terms
|
|
terms := []string{
|
|
"env:prod",
|
|
"env:staging",
|
|
"env:dev", // has prefix but doesn't match regex
|
|
"env:test", // has prefix but doesn't match regex
|
|
"type:server", // no prefix
|
|
"env:prod", // duplicate
|
|
"env:staging", // duplicate
|
|
}
|
|
|
|
for _, term := range terms {
|
|
tfb.StartDoc()
|
|
tfb.UpdateVisitor([]byte(term))
|
|
tfb.EndDoc()
|
|
}
|
|
|
|
result := tfb.Result()
|
|
|
|
// Should only have 2 unique terms (env:prod and env:staging)
|
|
if result.Terms.Len() != 2 {
|
|
t.Fatalf("expected 2 matching terms, got %d", result.Terms.Len())
|
|
}
|
|
|
|
// Verify the terms and counts
|
|
termCounts := make(map[string]int)
|
|
for _, facet := range result.Terms.Terms() {
|
|
termCounts[facet.Term] = facet.Count
|
|
}
|
|
|
|
if termCounts["env:prod"] != 2 {
|
|
t.Errorf("expected count 2 for env:prod, got %d", termCounts["env:prod"])
|
|
}
|
|
if termCounts["env:staging"] != 2 {
|
|
t.Errorf("expected count 2 for env:staging, got %d", termCounts["env:staging"])
|
|
}
|
|
|
|
// Total should be all 7 terms
|
|
if result.Total != 7 {
|
|
t.Errorf("expected total 7, got %d", result.Total)
|
|
}
|
|
|
|
// Other should be 3 (env:dev, env:test, type:server)
|
|
if result.Other != 3 {
|
|
t.Errorf("expected other 3, got %d", result.Other)
|
|
}
|
|
}
|
|
|
|
func TestTermsFacetInvalidRegex(t *testing.T) {
|
|
// Invalid regex pattern (unmatched bracket)
|
|
_, err := regexp.Compile("[invalid")
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid regex, got nil")
|
|
}
|
|
}
|
|
|
|
func TestTermsFacetNoFilter(t *testing.T) {
|
|
field := "tag"
|
|
tfb := NewTermsFacetBuilder(field, 2)
|
|
|
|
terms := []string{"apple", "banana", "cherry", "apple"}
|
|
|
|
for _, term := range terms {
|
|
tfb.StartDoc()
|
|
tfb.UpdateVisitor([]byte(term))
|
|
tfb.EndDoc()
|
|
}
|
|
|
|
result := tfb.Result()
|
|
|
|
// Should return top 2 by count
|
|
if result.Terms.Len() != 2 {
|
|
t.Fatalf("expected 2 terms, got %d", result.Terms.Len())
|
|
}
|
|
|
|
// Apple should be first with count 2
|
|
facets := result.Terms.Terms()
|
|
if facets[0].Term != "apple" || facets[0].Count != 2 {
|
|
t.Errorf("expected apple with count 2, got %s with count %d", facets[0].Term, facets[0].Count)
|
|
}
|
|
|
|
// Total should be 4
|
|
if result.Total != 4 {
|
|
t.Errorf("expected total 4, got %d", result.Total)
|
|
}
|
|
|
|
// Other should be 1 (cherry was trimmed)
|
|
if result.Other != 1 {
|
|
t.Errorf("expected other 1, got %d", result.Other)
|
|
}
|
|
}
|