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 facet
16
17import (
18	"reflect"
19	"sort"
20
21	"github.com/blevesearch/bleve/search"
22	"github.com/blevesearch/bleve/size"
23)
24
25var reflectStaticSizeTermsFacetBuilder int
26
27func init() {
28	var tfb TermsFacetBuilder
29	reflectStaticSizeTermsFacetBuilder = int(reflect.TypeOf(tfb).Size())
30}
31
32type TermsFacetBuilder struct {
33	size       int
34	field      string
35	termsCount map[string]int
36	total      int
37	missing    int
38	sawValue   bool
39}
40
41func NewTermsFacetBuilder(field string, size int) *TermsFacetBuilder {
42	return &TermsFacetBuilder{
43		size:       size,
44		field:      field,
45		termsCount: make(map[string]int),
46	}
47}
48
49func (fb *TermsFacetBuilder) Size() int {
50	sizeInBytes := reflectStaticSizeTermsFacetBuilder + size.SizeOfPtr +
51		len(fb.field)
52
53	for k, _ := range fb.termsCount {
54		sizeInBytes += size.SizeOfString + len(k) +
55			size.SizeOfInt
56	}
57
58	return sizeInBytes
59}
60
61func (fb *TermsFacetBuilder) Field() string {
62	return fb.field
63}
64
65func (fb *TermsFacetBuilder) UpdateVisitor(field string, term []byte) {
66	if field == fb.field {
67		fb.sawValue = true
68		fb.termsCount[string(term)] = fb.termsCount[string(term)] + 1
69		fb.total++
70	}
71}
72
73func (fb *TermsFacetBuilder) StartDoc() {
74	fb.sawValue = false
75}
76
77func (fb *TermsFacetBuilder) EndDoc() {
78	if !fb.sawValue {
79		fb.missing++
80	}
81}
82
83func (fb *TermsFacetBuilder) Result() *search.FacetResult {
84	rv := search.FacetResult{
85		Field:   fb.field,
86		Total:   fb.total,
87		Missing: fb.missing,
88	}
89
90	rv.Terms = make([]*search.TermFacet, 0, len(fb.termsCount))
91
92	for term, count := range fb.termsCount {
93		tf := &search.TermFacet{
94			Term:  term,
95			Count: count,
96		}
97
98		rv.Terms = append(rv.Terms, tf)
99	}
100
101	sort.Sort(rv.Terms)
102
103	// we now have the list of the top N facets
104	trimTopN := fb.size
105	if trimTopN > len(rv.Terms) {
106		trimTopN = len(rv.Terms)
107	}
108	rv.Terms = rv.Terms[:trimTopN]
109
110	notOther := 0
111	for _, tf := range rv.Terms {
112		notOther += tf.Count
113	}
114	rv.Other = fb.total - notOther
115
116	return &rv
117}
118