Skip to content

Commit 3264ad8

Browse files
Added support for Phonetic Matching (#92)
1 parent d3e63c6 commit 3264ad8

File tree

5 files changed

+115
-5
lines changed

5 files changed

+115
-5
lines changed

redisearch/client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (i *Client) CreateIndexWithIndexDefinition(schema *Schema, definition *Inde
5959

6060
// internal method
6161
func (i *Client) indexWithDefinition(indexName string, schema *Schema, definition *IndexDefinition) (err error) {
62-
args := redis.Args{i.name}
62+
args := redis.Args{indexName}
6363
if definition != nil {
6464
args = definition.Serialize(args)
6565
}

redisearch/client_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"reflect"
66
"testing"
7+
"time"
78

89
"github.com/gomodule/redigo/redis"
910
"github.com/stretchr/testify/assert"
@@ -850,3 +851,46 @@ func TestClient_CreateIndexWithIndexDefinition1(t *testing.T) {
850851

851852
}
852853
}
854+
855+
func TestClient_CreateIndex(t *testing.T) {
856+
c := createClient("create-index-phonetic")
857+
version, err := c.getRediSearchVersion()
858+
assert.Nil(t, err)
859+
if version <= 10699 {
860+
// IndexDefinition is available for RediSearch 2.0+
861+
return
862+
}
863+
864+
// Create a schema
865+
schema := NewSchema(DefaultOptions).
866+
AddField(NewTextFieldOptions("name", TextFieldOptions{Sortable: true, PhoneticMatcher: PhoneticDoubleMetaphoneEnglish})).
867+
AddField(NewNumericField("age"))
868+
869+
// IndexDefinition is available for RediSearch 2.0+
870+
// In this example we will only index keys started by product:
871+
indexDefinition := NewIndexDefinition().AddPrefix("create-index-phonetic:")
872+
873+
// Add the Index Definition
874+
c.CreateIndexWithIndexDefinition(schema, indexDefinition)
875+
assert.Nil(t, err)
876+
877+
// Create docs with a name that has the same phonetic matcher
878+
vanillaConnection := c.pool.Get()
879+
vanillaConnection.Do("HSET", "create-index-phonetic:doc1", "name", "Jon", "age", 25)
880+
vanillaConnection.Do("HSET", "create-index-phonetic:doc2", "name", "John", "age", 20)
881+
882+
// Wait for all documents to be indexed
883+
info, _ := c.Info()
884+
for info.IsIndexing {
885+
time.Sleep(time.Second)
886+
info, _ = c.Info()
887+
}
888+
889+
docs, total, err := c.Search(NewQuery("Jon").
890+
SetReturnFields("name"))
891+
assert.Nil(t, err)
892+
// Verify that the we've received 2 documents ( Jon and John )
893+
assert.Equal(t, 2, total)
894+
assert.Equal(t, "Jon", docs[0].Properties["name"])
895+
assert.Equal(t, "John", docs[1].Properties["name"])
896+
}

redisearch/example_schema_test.go

+48
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package redisearch_test
22

33
import (
44
"fmt"
5+
"github.com/gomodule/redigo/redis"
56
"log"
67
"time"
78

@@ -50,3 +51,50 @@ func ExampleCreateIndex_temporary() {
5051
// Output: ExampleCreateIndex_temporary:doc1 Hello world 1 <nil>
5152
// Unknown Index name
5253
}
54+
55+
// exemplifies the CreateIndex function with phonetic matching on it in searches by default
56+
func ExampleClient_CreateIndexWithIndexDefinition_phonetic() {
57+
// Create a client
58+
host := "localhost:6379"
59+
password := ""
60+
pool := &redis.Pool{Dial: func() (redis.Conn, error) {
61+
return redis.Dial("tcp", host, redis.DialPassword(password))
62+
}}
63+
c := redisearch.NewClientFromPool(pool, "myPhoneticIndex")
64+
65+
// Create a schema
66+
schema := redisearch.NewSchema(redisearch.DefaultOptions).
67+
AddField(redisearch.NewTextFieldOptions("name", redisearch.TextFieldOptions{Sortable: true, PhoneticMatcher: redisearch.PhoneticDoubleMetaphoneEnglish})).
68+
AddField(redisearch.NewNumericField("age"))
69+
70+
// IndexDefinition is available for RediSearch 2.0+
71+
// Create a index definition for automatic indexing on Hash updates.
72+
// In this example we will only index keys started by product:
73+
indexDefinition := redisearch.NewIndexDefinition().AddPrefix("myPhoneticIndex:")
74+
75+
// Add the Index Definition
76+
c.CreateIndexWithIndexDefinition(schema, indexDefinition)
77+
78+
// Create docs with a name that has the same phonetic matcher
79+
vanillaConnection := pool.Get()
80+
vanillaConnection.Do("HSET", "myPhoneticIndex:doc1", "name", "Jon", "age", 25)
81+
// Create a second document with a name that has the same phonetic matcher
82+
vanillaConnection.Do("HSET", "myPhoneticIndex:doc2", "name", "John", "age", 20)
83+
// Create a third document with a name that does not have the same phonetic matcher
84+
vanillaConnection.Do("HSET", "myPhoneticIndex:doc3", "name", "Pieter", "age", 30)
85+
86+
// Wait for all documents to be indexed
87+
info, _ := c.Info()
88+
for info.IsIndexing {
89+
time.Sleep(time.Second)
90+
info, _ = c.Info()
91+
}
92+
93+
_, total, _ := c.Search(redisearch.NewQuery("Jon").
94+
SetReturnFields("name"))
95+
96+
// Verify that the we've received 2 documents ( Jon and John )
97+
fmt.Printf("Total docs replied %d\n", total)
98+
99+
// Output: Total docs replied 2
100+
}

redisearch/schema.go

+20-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88
// FieldType is an enumeration of field/property types
99
type FieldType int
1010

11+
// PhoneticMatcherType is an enumeration of the phonetic algorithm and language used.
12+
type PhoneticMatcherType string
13+
1114
// Options are flags passed to the the abstract Index call, which receives them as interface{}, allowing
1215
// for implementation specific options
1316
type Options struct {
@@ -98,6 +101,7 @@ var DefaultOptions = Options{
98101
MaxTextFieldsFlag: false,
99102
}
100103

104+
// Field Types
101105
const (
102106
// TextField full-text field
103107
TextField FieldType = iota
@@ -112,6 +116,14 @@ const (
112116
TagField
113117
)
114118

119+
// Phonetic Matchers
120+
const (
121+
PhoneticDoubleMetaphoneEnglish PhoneticMatcherType = "dm:en"
122+
PhoneticDoubleMetaphoneFrench PhoneticMatcherType = "dm:fr"
123+
PhoneticDoubleMetaphonePortuguese PhoneticMatcherType = "dm:pt"
124+
PhoneticDoubleMetaphoneSpanish PhoneticMatcherType = "dm:es"
125+
)
126+
115127
// Field represents a single field's Schema
116128
type Field struct {
117129
Name string
@@ -122,10 +134,11 @@ type Field struct {
122134

123135
// TextFieldOptions Options for text fields - weight and stemming enabled/disabled.
124136
type TextFieldOptions struct {
125-
Weight float32
126-
Sortable bool
127-
NoStem bool
128-
NoIndex bool
137+
Weight float32
138+
Sortable bool
139+
NoStem bool
140+
NoIndex bool
141+
PhoneticMatcher PhoneticMatcherType
129142
}
130143

131144
// TagFieldOptions options for indexing tag fields
@@ -306,6 +319,9 @@ func serializeField(f Field, args redis.Args) (argsOut redis.Args, err error) {
306319
if opts.NoStem {
307320
argsOut = append(argsOut, "NOSTEM")
308321
}
322+
if opts.PhoneticMatcher != "" {
323+
argsOut = append(argsOut, "PHONETIC", string(opts.PhoneticMatcher))
324+
}
309325
if opts.Sortable {
310326
argsOut = append(argsOut, "SORTABLE")
311327
}

redisearch/schema_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ func TestSerializeSchema(t *testing.T) {
6060
{"default-and-text", args{NewSchema(DefaultOptions).AddField(NewTextField("text-field")), redis.Args{}}, redis.Args{"SCHEMA", "text-field", "TEXT"}, false},
6161
{"default-and-sortable-text-field", args{NewSchema(DefaultOptions).AddField(NewSortableTextField("text-field", 10)), redis.Args{}}, redis.Args{"SCHEMA", "text-field", "TEXT", "WEIGHT", float32(10.0), "SORTABLE"}, false},
6262
{"default-and-text-with-options", args{NewSchema(DefaultOptions).AddField(NewTextFieldOptions("text-field", TextFieldOptions{Weight: 5.0, Sortable: true, NoStem: false, NoIndex: false})), redis.Args{}}, redis.Args{"SCHEMA", "text-field", "TEXT", "WEIGHT", float32(5.0), "SORTABLE"}, false},
63+
{"default-and-text-with-phonetic-en", args{NewSchema(DefaultOptions).AddField(NewTextFieldOptions("text-field", TextFieldOptions{PhoneticMatcher: PhoneticDoubleMetaphoneEnglish})), redis.Args{}}, redis.Args{"SCHEMA", "text-field", "TEXT", "PHONETIC", "dm:en"}, false},
64+
{"default-and-text-with-phonetic-pt", args{NewSchema(DefaultOptions).AddField(NewTextFieldOptions("text-field", TextFieldOptions{PhoneticMatcher: PhoneticDoubleMetaphonePortuguese})), redis.Args{}}, redis.Args{"SCHEMA", "text-field", "TEXT", "PHONETIC", "dm:pt"}, false},
6365
{"default-and-tag", args{NewSchema(DefaultOptions).AddField(NewTagField("tag-field")), redis.Args{}}, redis.Args{"SCHEMA", "tag-field", "TAG", "SEPARATOR", ","}, false},
6466
{"default-and-tag-with-options", args{NewSchema(DefaultOptions).AddField(NewTagFieldOptions("tag-field", TagFieldOptions{Sortable: true, NoIndex: false, Separator: byte(',')})), redis.Args{}}, redis.Args{"SCHEMA", "tag-field", "TAG", "SEPARATOR", ",", "SORTABLE"}, false},
6567
{"error-unsupported", args{NewSchema(DefaultOptions).AddField(Field{Type: 10}), redis.Args{}}, nil, true},

0 commit comments

Comments
 (0)