You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
3.1 KiB

  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. package query
  15. import (
  16. "fmt"
  17. "github.com/blevesearch/bleve/analysis"
  18. "github.com/blevesearch/bleve/index"
  19. "github.com/blevesearch/bleve/mapping"
  20. "github.com/blevesearch/bleve/search"
  21. )
  22. type MatchPhraseQuery struct {
  23. MatchPhrase string `json:"match_phrase"`
  24. FieldVal string `json:"field,omitempty"`
  25. Analyzer string `json:"analyzer,omitempty"`
  26. BoostVal *Boost `json:"boost,omitempty"`
  27. }
  28. // NewMatchPhraseQuery creates a new Query object
  29. // for matching phrases in the index.
  30. // An Analyzer is chosen based on the field.
  31. // Input text is analyzed using this analyzer.
  32. // Token terms resulting from this analysis are
  33. // used to build a search phrase. Result documents
  34. // must match this phrase. Queried field must have been indexed with
  35. // IncludeTermVectors set to true.
  36. func NewMatchPhraseQuery(matchPhrase string) *MatchPhraseQuery {
  37. return &MatchPhraseQuery{
  38. MatchPhrase: matchPhrase,
  39. }
  40. }
  41. func (q *MatchPhraseQuery) SetBoost(b float64) {
  42. boost := Boost(b)
  43. q.BoostVal = &boost
  44. }
  45. func (q *MatchPhraseQuery) Boost() float64{
  46. return q.BoostVal.Value()
  47. }
  48. func (q *MatchPhraseQuery) SetField(f string) {
  49. q.FieldVal = f
  50. }
  51. func (q *MatchPhraseQuery) Field() string{
  52. return q.FieldVal
  53. }
  54. func (q *MatchPhraseQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, explain bool) (search.Searcher, error) {
  55. field := q.FieldVal
  56. if q.FieldVal == "" {
  57. field = m.DefaultSearchField()
  58. }
  59. analyzerName := ""
  60. if q.Analyzer != "" {
  61. analyzerName = q.Analyzer
  62. } else {
  63. analyzerName = m.AnalyzerNameForPath(field)
  64. }
  65. analyzer := m.AnalyzerNamed(analyzerName)
  66. if analyzer == nil {
  67. return nil, fmt.Errorf("no analyzer named '%s' registered", q.Analyzer)
  68. }
  69. tokens := analyzer.Analyze([]byte(q.MatchPhrase))
  70. if len(tokens) > 0 {
  71. phrase := tokenStreamToPhrase(tokens)
  72. phraseQuery := NewPhraseQuery(phrase, field)
  73. phraseQuery.SetBoost(q.BoostVal.Value())
  74. return phraseQuery.Searcher(i, m, explain)
  75. }
  76. noneQuery := NewMatchNoneQuery()
  77. return noneQuery.Searcher(i, m, explain)
  78. }
  79. func tokenStreamToPhrase(tokens analysis.TokenStream) []string {
  80. firstPosition := int(^uint(0) >> 1)
  81. lastPosition := 0
  82. for _, token := range tokens {
  83. if token.Position < firstPosition {
  84. firstPosition = token.Position
  85. }
  86. if token.Position > lastPosition {
  87. lastPosition = token.Position
  88. }
  89. }
  90. phraseLen := lastPosition - firstPosition + 1
  91. if phraseLen > 0 {
  92. rv := make([]string, phraseLen)
  93. for i := 0; i < phraseLen; i++ {
  94. rv[i] = ""
  95. }
  96. for _, token := range tokens {
  97. pos := token.Position - firstPosition
  98. rv[pos] = string(token.Term)
  99. }
  100. return rv
  101. }
  102. return nil
  103. }