1//  Copyright (c) 2014 Couchbase, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// 		http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package query
16
17import (
18	"regexp"
19	"strings"
20
21	"github.com/blevesearch/bleve/index"
22	"github.com/blevesearch/bleve/mapping"
23	"github.com/blevesearch/bleve/search"
24	"github.com/blevesearch/bleve/search/searcher"
25)
26
27type RegexpQuery struct {
28	Regexp   string `json:"regexp"`
29	FieldVal string `json:"field,omitempty"`
30	BoostVal *Boost `json:"boost,omitempty"`
31	compiled *regexp.Regexp
32}
33
34// NewRegexpQuery creates a new Query which finds
35// documents containing terms that match the
36// specified regular expression.  The regexp pattern
37// SHOULD NOT include ^ or $ modifiers, the search
38// will only match entire terms even without them.
39func NewRegexpQuery(regexp string) *RegexpQuery {
40	return &RegexpQuery{
41		Regexp: regexp,
42	}
43}
44
45func (q *RegexpQuery) SetBoost(b float64) {
46	boost := Boost(b)
47	q.BoostVal = &boost
48}
49
50func (q *RegexpQuery) Boost() float64 {
51	return q.BoostVal.Value()
52}
53
54func (q *RegexpQuery) SetField(f string) {
55	q.FieldVal = f
56}
57
58func (q *RegexpQuery) Field() string {
59	return q.FieldVal
60}
61
62func (q *RegexpQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) {
63	field := q.FieldVal
64	if q.FieldVal == "" {
65		field = m.DefaultSearchField()
66	}
67	err := q.compile()
68	if err != nil {
69		return nil, err
70	}
71
72	return searcher.NewRegexpSearcher(i, q.compiled, field, q.BoostVal.Value(), options)
73}
74
75func (q *RegexpQuery) Validate() error {
76	return q.compile()
77}
78
79func (q *RegexpQuery) compile() error {
80	if q.compiled == nil {
81		// require that pattern NOT be anchored to start and end of term
82		actualRegexp := q.Regexp
83		if strings.HasPrefix(actualRegexp, "^") {
84			actualRegexp = actualRegexp[1:] // remove leading ^
85		}
86		// do not attempt to remove trailing $, it's presence is not
87		// known to interfere with LiteralPrefix() the way ^ does
88		// and removing $ introduces possible ambiguities with escaped \$, \\$, etc
89		var err error
90		q.compiled, err = regexp.Compile(actualRegexp)
91		if err != nil {
92			return err
93		}
94	}
95	return nil
96}
97