Merge 9bcace90f3
into fd07fdb862
This commit is contained in:
commit
f6a2df99fd
|
@ -63,3 +63,7 @@ func NewBooleanFieldMapping() *mapping.FieldMapping {
|
|||
func NewGeoPointFieldMapping() *mapping.FieldMapping {
|
||||
return mapping.NewGeoPointFieldMapping()
|
||||
}
|
||||
|
||||
func NewIPFieldMapping() *mapping.FieldMapping {
|
||||
return mapping.NewIPFieldMapping()
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ func (dm *DocumentMapping) Validate(cache *registry.Cache) error {
|
|||
}
|
||||
}
|
||||
switch field.Type {
|
||||
case "text", "datetime", "number", "boolean", "geopoint":
|
||||
case "text", "datetime", "number", "boolean", "geopoint", "IP":
|
||||
default:
|
||||
return fmt.Errorf("unknown field type: '%s'", field.Type)
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
package mapping
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/blevesearch/bleve/analysis"
|
||||
|
@ -149,6 +151,16 @@ func NewGeoPointFieldMapping() *FieldMapping {
|
|||
}
|
||||
}
|
||||
|
||||
// NewIPFieldMapping returns a default field mapping for IP points
|
||||
func NewIPFieldMapping() *FieldMapping {
|
||||
return &FieldMapping{
|
||||
Type: "IP",
|
||||
Store: true,
|
||||
Index: true,
|
||||
IncludeInAll: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Options returns the indexing options for this field.
|
||||
func (fm *FieldMapping) Options() document.IndexingOptions {
|
||||
var rv document.IndexingOptions
|
||||
|
@ -190,6 +202,31 @@ func (fm *FieldMapping) processString(propertyValueString string, pathString str
|
|||
fm.processTime(parsedDateTime, pathString, path, indexes, context)
|
||||
}
|
||||
}
|
||||
} else if fm.Type == "IP" {
|
||||
ip := net.ParseIP(propertyValueString)
|
||||
isIPv4 := ip.DefaultMask() != nil // Only IPv4 addresses have default masks
|
||||
|
||||
if ip != nil {
|
||||
options := fm.Options()
|
||||
fieldv4 := document.NewBooleanFieldWithIndexingOptions(fieldName+"_isIPv4", indexes, isIPv4, options)
|
||||
context.doc.AddField(fieldv4)
|
||||
fieldip1 := document.NewNumericFieldWithIndexingOptions(fieldName+"_IP1", indexes, float64(binary.BigEndian.Uint32(ip[0:4])), options)
|
||||
context.doc.AddField(fieldip1)
|
||||
fieldip2 := document.NewNumericFieldWithIndexingOptions(fieldName+"_IP2", indexes, float64(binary.BigEndian.Uint32(ip[4:8])), options)
|
||||
context.doc.AddField(fieldip2)
|
||||
fieldip3 := document.NewNumericFieldWithIndexingOptions(fieldName+"_IP3", indexes, float64(binary.BigEndian.Uint32(ip[8:12])), options)
|
||||
context.doc.AddField(fieldip3)
|
||||
fieldip4 := document.NewNumericFieldWithIndexingOptions(fieldName+"_IP4", indexes, float64(binary.BigEndian.Uint32(ip[12:16])), options)
|
||||
context.doc.AddField(fieldip4)
|
||||
|
||||
fa := []string{fieldName + "_isIPv4", fieldName + "_IP1", fieldName + "_IP2", fieldName + "_IP3", fieldName + "_IP4"}
|
||||
field := document.NewCompositeFieldWithIndexingOptions(fieldName, true, fa, nil, options)
|
||||
context.doc.AddField(field)
|
||||
|
||||
if !fm.IncludeInAll {
|
||||
context.excludedFromAll = append(context.excludedFromAll, fieldName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
4
query.go
4
query.go
|
@ -216,3 +216,7 @@ func NewGeoBoundingBoxQuery(topLeftLon, topLeftLat, bottomRightLon, bottomRightL
|
|||
func NewGeoDistanceQuery(lon, lat float64, distance string) *query.GeoDistanceQuery {
|
||||
return query.NewGeoDistanceQuery(lon, lat, distance)
|
||||
}
|
||||
|
||||
func NewIPRangeQuery(cidr string) *query.IPRangeQuery {
|
||||
return query.NewIPRangeQuery(cidr)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/blevesearch/bleve/index"
|
||||
"github.com/blevesearch/bleve/mapping"
|
||||
"github.com/blevesearch/bleve/search"
|
||||
)
|
||||
|
||||
type IPRangeQuery struct {
|
||||
CIDRVal string `json:"cidr, omitempty"`
|
||||
FieldVal string `json:"field,omitempty"`
|
||||
BoostVal *Boost `json:"boost,omitempty"`
|
||||
}
|
||||
|
||||
func NewIPRangeQuery(cidr string) *IPRangeQuery {
|
||||
return &IPRangeQuery{
|
||||
CIDRVal: cidr,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *IPRangeQuery) SetBoost(b float64) {
|
||||
boost := Boost(b)
|
||||
q.BoostVal = &boost
|
||||
}
|
||||
|
||||
func (q *IPRangeQuery) Boost() float64 {
|
||||
return q.BoostVal.Value()
|
||||
}
|
||||
|
||||
func (q *IPRangeQuery) SetField(f string) {
|
||||
q.FieldVal = f
|
||||
}
|
||||
|
||||
func (q *IPRangeQuery) Field() string {
|
||||
return q.FieldVal
|
||||
}
|
||||
|
||||
func (q *IPRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) {
|
||||
_, ipNet, err := net.ParseCIDR(q.CIDRVal)
|
||||
if err != nil {
|
||||
isIP := net.ParseIP(q.CIDRVal)
|
||||
if isIP == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isIP.DefaultMask() != nil {
|
||||
q.CIDRVal = q.CIDRVal + `/32`
|
||||
} else {
|
||||
q.CIDRVal = q.CIDRVal + `/128`
|
||||
}
|
||||
_, ipNet, err = net.ParseCIDR(q.CIDRVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cq := ipRangeToConjuctionQuery(q.FieldVal, ipNet)
|
||||
return cq.Searcher(i, m, options)
|
||||
}
|
||||
|
||||
func (q *IPRangeQuery) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ipRangeToConjuctionQuery(fielName string, ipNet *net.IPNet) *ConjunctionQuery {
|
||||
bndrs := make([]struct {
|
||||
min uint32
|
||||
max uint32
|
||||
}, 4)
|
||||
isIPv4 := false
|
||||
if ipNet.IP.DefaultMask() != nil {
|
||||
isIPv4 = true
|
||||
}
|
||||
|
||||
if ok4 := ipNet.IP.To4(); ok4 != nil {
|
||||
bndrs[2].min = 0x0000FFFF
|
||||
bndrs[2].max = 0x0000FFFF
|
||||
bndrs[3].min = binary.BigEndian.Uint32(ok4)
|
||||
|
||||
a := make([]byte, 4)
|
||||
m := ipNet.Mask
|
||||
for i, b := range ok4 {
|
||||
// calculate broadcast address (end of range)
|
||||
a[i] = b | ^m[i]
|
||||
}
|
||||
bndrs[3].max = binary.BigEndian.Uint32(a)
|
||||
} else {
|
||||
// IPv6
|
||||
for i, _ := range bndrs {
|
||||
chunk := ipNet.IP[i*4 : (i+1)*4]
|
||||
bndrs[i].min = binary.BigEndian.Uint32(chunk)
|
||||
|
||||
t := make([]byte, 4)
|
||||
mask := ipNet.Mask[i*4 : (i+1)*4]
|
||||
for k, b := range chunk {
|
||||
t[k] = b | ^mask[k]
|
||||
}
|
||||
bndrs[i].max = binary.BigEndian.Uint32(t)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
qs := make([]Query, 4)
|
||||
for i, bs := range bndrs {
|
||||
tq := newNumericRangeInclusiveQuery(bs.min, bs.max, true, true)
|
||||
tq.SetField(fmt.Sprintf("%s_IP%d", fielName, i+1))
|
||||
qs[i] = tq
|
||||
}
|
||||
|
||||
cq := NewConjunctionQuery(qs)
|
||||
|
||||
blq := NewBoolFieldQuery(isIPv4)
|
||||
blq.SetField(fielName + "_isIPv4")
|
||||
cq.AddQuery(blq)
|
||||
|
||||
return cq
|
||||
}
|
||||
|
||||
func newNumericRangeInclusiveQuery(min, max uint32, minInclusive, maxInclusive bool) *NumericRangeQuery {
|
||||
m1 := float64(min)
|
||||
m2 := float64(max)
|
||||
return NewNumericRangeInclusiveQuery(&m1, &m2, &minInclusive, &maxInclusive)
|
||||
}
|
|
@ -273,6 +273,15 @@ func ParseQuery(input []byte) (Query, error) {
|
|||
}
|
||||
return &rv, nil
|
||||
}
|
||||
_, hasCidr := tmp["cidr"]
|
||||
if hasCidr {
|
||||
var rv IPRangeQuery
|
||||
err := json.Unmarshal(input, &rv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rv, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown query type")
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ var startDateStr = "2011-01-01T00:00:00Z"
|
|||
var endDateStr = "2012-01-01T00:00:00Z"
|
||||
var startDate time.Time
|
||||
var endDate time.Time
|
||||
var cidr = "192.168.0.1/32"
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
@ -189,6 +190,10 @@ func TestParseQuery(t *testing.T) {
|
|||
output: nil,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
input: []byte(`{"cidr":"` + cidr + `"}`),
|
||||
output: NewIPRangeQuery(cidr),
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
|
|
Loading…
Reference in New Issue