0
0
Fork 0

added support for stored fields and highlighting results

This commit is contained in:
Marty Schoch 2014-06-26 11:43:13 -04:00
parent 4af76f539d
commit 9bebbec267
7334 changed files with 8537 additions and 521 deletions

1
.gitignore vendored
View File

@ -6,5 +6,6 @@
.settings
.DS_Store
/examples/bleve_index_json/bleve_index_json
/examples/bleve_index_json/index/
/examples/bleve_query/bleve_query
/utils/bleve_dump/bleve_dump

View File

@ -13,17 +13,17 @@ import (
)
type Field struct {
Name string
IndexingOptions int
Analyzer *analysis.Analyzer
Value []byte
Name string
Options IndexingOptions
Analyzer *analysis.Analyzer
Value []byte
}
func NewField(name string, value []byte, indexingOptions int, analyzer *analysis.Analyzer) *Field {
func NewField(name string, value []byte, options IndexingOptions, analyzer *analysis.Analyzer) *Field {
return &Field{
Name: name,
IndexingOptions: indexingOptions,
Analyzer: analyzer,
Value: value,
Name: name,
Options: options,
Analyzer: analyzer,
Value: value,
}
}

View File

@ -31,11 +31,11 @@ func NewTextField(name string, value []byte) *Field {
return NewTextFieldWithIndexingOptions(name, value, DEFAULT_TEXT_INDEXING_OPTIONS)
}
func NewTextFieldWithIndexingOptions(name string, value []byte, indexingOptions int) *Field {
func NewTextFieldWithIndexingOptions(name string, value []byte, options IndexingOptions) *Field {
return &Field{
Name: name,
IndexingOptions: indexingOptions,
Analyzer: standardAnalyzer,
Value: value,
Name: name,
Options: options,
Analyzer: standardAnalyzer,
Value: value,
}
}

View File

@ -8,20 +8,22 @@
// and limitations under the License.
package document
type IndexingOptions int
const (
INDEX_FIELD = 1 << iota
INDEX_FIELD IndexingOptions = 1 << iota
STORE_FIELD
INCLUDE_TERM_VECTORS
)
func IsIndexedField(arg int) bool {
return arg&INDEX_FIELD != 0
func (o IndexingOptions) IsIndexed() bool {
return o&INDEX_FIELD != 0
}
func IsStoredField(arg int) bool {
return arg&STORE_FIELD != 0
func (o IndexingOptions) IsStored() bool {
return o&STORE_FIELD != 0
}
func IncludeTermVectors(arg int) bool {
return arg&INCLUDE_TERM_VECTORS != 0
func (o IndexingOptions) IncludeTermVectors() bool {
return o&INCLUDE_TERM_VECTORS != 0
}

View File

@ -14,37 +14,37 @@ import (
func TestIndexingOptions(t *testing.T) {
tests := []struct {
indexingOptions int
options IndexingOptions
isIndexed bool
isStored bool
includeTermVectors bool
}{
{
indexingOptions: INDEX_FIELD | STORE_FIELD | INCLUDE_TERM_VECTORS,
options: INDEX_FIELD | STORE_FIELD | INCLUDE_TERM_VECTORS,
isIndexed: true,
isStored: true,
includeTermVectors: true,
},
{
indexingOptions: INDEX_FIELD | INCLUDE_TERM_VECTORS,
options: INDEX_FIELD | INCLUDE_TERM_VECTORS,
isIndexed: true,
isStored: false,
includeTermVectors: true,
},
{
indexingOptions: STORE_FIELD | INCLUDE_TERM_VECTORS,
options: STORE_FIELD | INCLUDE_TERM_VECTORS,
isIndexed: false,
isStored: true,
includeTermVectors: true,
},
{
indexingOptions: INDEX_FIELD,
options: INDEX_FIELD,
isIndexed: true,
isStored: false,
includeTermVectors: false,
},
{
indexingOptions: STORE_FIELD,
options: STORE_FIELD,
isIndexed: false,
isStored: true,
includeTermVectors: false,
@ -52,17 +52,17 @@ func TestIndexingOptions(t *testing.T) {
}
for _, test := range tests {
actuallyIndexed := IsIndexedField(test.indexingOptions)
actuallyIndexed := test.options.IsIndexed()
if actuallyIndexed != test.isIndexed {
t.Errorf("expected indexed to be %v, got %v for %d", test.isIndexed, actuallyIndexed, test.indexingOptions)
t.Errorf("expected indexed to be %v, got %v for %d", test.isIndexed, actuallyIndexed, test.options)
}
actuallyStored := IsStoredField(test.indexingOptions)
actuallyStored := test.options.IsStored()
if actuallyStored != test.isStored {
t.Errorf("expected stored to be %v, got %v for %d", test.isStored, actuallyStored, test.indexingOptions)
t.Errorf("expected stored to be %v, got %v for %d", test.isStored, actuallyStored, test.options)
}
actuallyIncludeTermVectors := IncludeTermVectors(test.indexingOptions)
actuallyIncludeTermVectors := test.options.IncludeTermVectors()
if actuallyIncludeTermVectors != test.includeTermVectors {
t.Errorf("expected includeTermVectors to be %v, got %v for %d", test.includeTermVectors, actuallyIncludeTermVectors, test.indexingOptions)
t.Errorf("expected includeTermVectors to be %v, got %v for %d", test.includeTermVectors, actuallyIncludeTermVectors, test.options)
}
}
}

View File

@ -13,6 +13,7 @@ import (
"io/ioutil"
"log"
"github.com/couchbaselabs/bleve/document"
"github.com/couchbaselabs/bleve/index/store/leveldb"
"github.com/couchbaselabs/bleve/index/upside_down"
"github.com/couchbaselabs/bleve/shredder"
@ -20,13 +21,23 @@ import (
var jsonDir = flag.String("jsonDir", "json", "json directory")
var indexDir = flag.String("indexDir", "index", "index directory")
var storeFields = flag.Bool("storeFields", false, "store field data")
var includeTermVectors = flag.Bool("includeTermVectors", false, "include term vectors")
func main() {
flag.Parse()
indexOptions := document.INDEX_FIELD
if *storeFields {
indexOptions |= document.STORE_FIELD
}
if *includeTermVectors {
indexOptions |= document.INCLUDE_TERM_VECTORS
}
// create a automatic JSON document shredder
jsonShredder := shredder.NewAutoJsonShredder()
jsonShredder := shredder.NewAutoJsonShredderWithOptions(indexOptions)
// create a new index
store, err := leveldb.Open(*indexDir)

View File

@ -21,6 +21,7 @@ import (
var field = flag.String("field", "description", "field to query")
var indexDir = flag.String("indexDir", "index", "index directory")
var limit = flag.Int("limit", 10, "limit to first N results")
var includeHighlights = flag.Bool("highlight", false, "highlight matches")
func main() {
@ -70,6 +71,26 @@ func main() {
fmt.Printf("%d matches, showing %d through %d\n", searcher.Count(), 1, last)
for i, result := range results {
fmt.Printf("%2d. %s (%f)\n", i+1, result.ID, result.Score)
if *includeHighlights {
highlighter := search.NewSimpleHighlighter()
doc, err := index.Document(result.ID)
if err != nil {
fmt.Print(err)
return
}
fragments := highlighter.BestFragmentsInField(result, doc, *field, 5)
for _, fragment := range fragments {
fmt.Printf("\t%s\n", fragment)
}
if len(fragments) == 0 {
for _, f := range doc.Fields {
fmt.Printf("\tfield: %s\n", f)
}
}
}
}
}
}

View File

@ -23,6 +23,8 @@ type Index interface {
DocCount() uint64
Document(id string) (*document.Document, error)
Dump()
}

View File

@ -1,231 +0,0 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package mock
import (
"fmt"
"math"
"sort"
"github.com/couchbaselabs/bleve/analysis"
"github.com/couchbaselabs/bleve/document"
"github.com/couchbaselabs/bleve/index"
)
type mockFreq struct {
freq uint64
norm float64
vectors []*index.TermFieldVector
}
// key doc id
type mockDocFreq map[string]*mockFreq
//key field
type mockFieldDocFreq map[string]mockDocFreq
// 2 dim array
// inner level are always pairs (field name, term)
type mockBackIndexEntry [][]string
type MockIndex struct {
//this level of the map, the key is the term
termIndex map[string]mockFieldDocFreq
// key is docid
backIndex map[string]mockBackIndexEntry
docCount uint64
analyzer map[string]*analysis.Analyzer
}
func NewMockIndexWithDocs(docs []*document.Document) *MockIndex {
rv := NewMockIndex()
for _, doc := range docs {
rv.Update(doc)
}
return rv
}
func NewMockIndex() *MockIndex {
mi := MockIndex{
termIndex: make(map[string]mockFieldDocFreq),
backIndex: make(map[string]mockBackIndexEntry),
analyzer: make(map[string]*analysis.Analyzer),
}
return &mi
}
func (index *MockIndex) Open() error {
return nil
}
func (index *MockIndex) Close() {}
// for this implementation we dont care about performance
// update is simply delete then add
func (index *MockIndex) Update(doc *document.Document) error {
index.Delete(doc.ID)
backIndexEntry := make(mockBackIndexEntry, 0)
for _, field := range doc.Fields {
analyzer := field.Analyzer
tokens := analyzer.Analyze(field.Value)
fieldLength := len(tokens) // number of tokens in this doc field
fieldNorm := 1.0 / math.Sqrt(float64(fieldLength))
tokenFreqs := analysis.TokenFrequency(tokens)
for _, tf := range tokenFreqs {
mf := mockFreq{
freq: uint64(len(tf.Locations)),
norm: fieldNorm,
}
if document.IncludeTermVectors(field.IndexingOptions) {
mf.vectors = index.mockVectorsFromTokenFreq(field.Name, tf)
}
termString := string(tf.Term)
fieldMap, ok := index.termIndex[termString]
if !ok {
fieldMap = make(map[string]mockDocFreq)
index.termIndex[termString] = fieldMap
}
docMap, ok := fieldMap[field.Name]
if !ok {
docMap = make(map[string]*mockFreq)
fieldMap[field.Name] = docMap
}
docMap[doc.ID] = &mf
backIndexInnerEntry := []string{field.Name, termString}
backIndexEntry = append(backIndexEntry, backIndexInnerEntry)
}
}
index.backIndex[doc.ID] = backIndexEntry
index.docCount += 1
return nil
}
func (index *MockIndex) Delete(id string) error {
backIndexEntry, existed := index.backIndex[id]
if existed {
for _, backIndexPair := range backIndexEntry {
if len(backIndexPair) == 2 {
field := backIndexPair[0]
term := backIndexPair[1]
delete(index.termIndex[term][field], id)
if len(index.termIndex[term][field]) == 0 {
delete(index.termIndex[term], field)
if len(index.termIndex[term]) == 0 {
delete(index.termIndex, term)
}
}
}
}
delete(index.backIndex, id)
index.docCount -= 1
}
return nil
}
func (index *MockIndex) TermFieldReader(term []byte, field string) (index.TermFieldReader, error) {
fdf, ok := index.termIndex[string(term)]
if !ok {
fdf = make(mockFieldDocFreq)
}
docFreqs, ok := fdf[field]
if !ok {
docFreqs = make(mockDocFreq)
}
mtfr := mockTermFieldReader{
index: docFreqs,
sortedDocIds: make(sort.StringSlice, len(docFreqs)),
curr: -1,
}
i := 0
for k, _ := range docFreqs {
mtfr.sortedDocIds[i] = k
i += 1
}
sort.Sort(mtfr.sortedDocIds)
return &mtfr, nil
}
func (index *MockIndex) DocCount() uint64 {
return index.docCount
}
type mockTermFieldReader struct {
index mockDocFreq
sortedDocIds sort.StringSlice
curr int
}
func (reader *mockTermFieldReader) Next() (*index.TermFieldDoc, error) {
next := reader.curr + 1
if next < len(reader.sortedDocIds) {
nextTermKey := reader.sortedDocIds[next]
nextTerm := reader.index[nextTermKey]
reader.curr = next
return &index.TermFieldDoc{ID: nextTermKey, Freq: nextTerm.freq, Norm: nextTerm.norm, Vectors: nextTerm.vectors}, nil
}
return nil, nil
}
func (reader *mockTermFieldReader) Advance(ID string) (*index.TermFieldDoc, error) {
if reader.curr < 0 {
reader.curr += 1
}
if reader.curr >= len(reader.sortedDocIds) {
return nil, nil
}
i := reader.curr
for currTermID := reader.sortedDocIds[i]; currTermID < ID && i < len(reader.sortedDocIds); i += 1 {
reader.curr = i
currTermID = reader.sortedDocIds[reader.curr]
}
if reader.curr < len(reader.sortedDocIds) && reader.sortedDocIds[reader.curr] >= ID {
nextTermKey := reader.sortedDocIds[reader.curr]
nextTerm := reader.index[nextTermKey]
return &index.TermFieldDoc{ID: nextTermKey, Freq: nextTerm.freq, Norm: nextTerm.norm, Vectors: nextTerm.vectors}, nil
}
return nil, nil
}
func (reader *mockTermFieldReader) Count() uint64 {
return uint64(len(reader.sortedDocIds))
}
func (reader *mockTermFieldReader) Close() {}
func (mi *MockIndex) mockVectorsFromTokenFreq(field string, tf *analysis.TokenFreq) []*index.TermFieldVector {
rv := make([]*index.TermFieldVector, len(tf.Locations))
for i, l := range tf.Locations {
mv := index.TermFieldVector{
Field: field,
Pos: uint64(l.Position),
Start: uint64(l.Start),
End: uint64(l.End),
}
rv[i] = &mv
}
return rv
}
func (mi *MockIndex) Dump() {
fmt.Println("dump not implemented")
}

View File

@ -1,178 +0,0 @@
// Copyright (c) 2014 Couchbase, Inc.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
package mock
import (
"reflect"
"testing"
_ "github.com/couchbaselabs/bleve/analysis/analyzers/standard_analyzer"
"github.com/couchbaselabs/bleve/document"
"github.com/couchbaselabs/bleve/index"
)
func TestCRUD(t *testing.T) {
// create a document to seed this mock index with
doc1 := document.NewDocument("1")
doc1.AddField(document.NewTextField("name", []byte("marty")))
i := NewMockIndexWithDocs([]*document.Document{
doc1,
})
// open it
err := i.Open()
if err != nil {
t.Fatal(err)
}
// assert doc count is 1
count := i.DocCount()
if count != 1 {
t.Errorf("expected document count to be 1, was: %d", count)
}
// add another doc, assert doc count goes up again
doc2 := document.NewDocument("2")
doc2.AddField(document.NewTextField("name", []byte("bob")))
i.Update(doc2)
count = i.DocCount()
if count != 2 {
t.Errorf("expected document count to be 2, was: %d", count)
}
// search for doc with term that should exist
expectedMatch := &index.TermFieldDoc{
ID: "1",
Freq: 1,
Norm: 1,
}
tfr, err := i.TermFieldReader([]byte("marty"), "name")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
match, err := tfr.Next()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(expectedMatch, match) {
t.Errorf("got %v, expected %v", match, expectedMatch)
}
nomatch, err := tfr.Next()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if nomatch != nil {
t.Errorf("expected nil after last match")
}
tfr.Close()
// update doc, assert doc count doesn't go up
doc1 = document.NewDocument("1")
doc1.AddField(document.NewTextField("name", []byte("salad")))
doc1.AddField(document.NewTextFieldWithIndexingOptions("desc", []byte("eat more rice"), document.INDEX_FIELD|document.INCLUDE_TERM_VECTORS))
i.Update(doc1)
count = i.DocCount()
if count != 2 {
t.Errorf("expected document count to be 2, was: %d", count)
}
// perform the original search again, should NOT find anything this time
tfr, err = i.TermFieldReader([]byte("marty"), "name")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
nomatch, err = tfr.Next()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if nomatch != nil {
t.Errorf("expected no matches, found one")
t.Logf("%v", i)
}
tfr.Close()
// delete a doc, ensure the count is 1
err = i.Delete("2")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
count = i.DocCount()
if count != 1 {
t.Errorf("expected document count to be 1, was: %d", count)
}
expectedMatch = &index.TermFieldDoc{
ID: "1",
Freq: 1,
Norm: 0.5773502691896258,
Vectors: []*index.TermFieldVector{
&index.TermFieldVector{
Field: "desc",
Pos: 3,
Start: 9,
End: 13,
},
},
}
tfr, err = i.TermFieldReader([]byte("rice"), "desc")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
match, err = tfr.Next()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(expectedMatch, match) {
t.Errorf("got %#v, expected %#v", match, expectedMatch)
}
tfr.Close()
// now test usage of advance
// add another doc,
doc5 := document.NewDocument("5")
doc5.AddField(document.NewTextField("name", []byte("salad")))
i.Update(doc5)
tfr, err = i.TermFieldReader([]byte("salad"), "name")
if err != nil {
t.Errorf("Error accessing term field reader: %v", err)
}
readerCount := tfr.Count()
if readerCount != 2 {
t.Errorf("expected 2 docs in reader, got %d", readerCount)
}
match, err = tfr.Advance("1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if match.ID != "1" {
t.Errorf("Expected ID '1', got '%s'", match.ID)
}
match, err = tfr.Advance("7")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if match != nil {
t.Errorf("expected nil, got %v", match)
}
// try to do it again
match, err = tfr.Advance("7")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if match != nil {
t.Errorf("expected nil, got %v", match)
}
tfr.Close()
// close it
i.Close()
}

View File

@ -1,10 +1,7 @@
package inmem
import (
//"bytes"
"github.com/couchbaselabs/bleve/index/store"
"github.com/ryszard/goskiplist/skiplist"
)
@ -14,19 +11,19 @@ type InMemStore struct {
func Open() (*InMemStore, error) {
rv := InMemStore{
//list: skiplist.NewCustomMap(byteArrayLessThan),
list: skiplist.NewStringMap(),
}
return &rv, nil
}
// func byteArrayLessThan(l, r interface{}) bool {
// if bytes.Compare(l.([]byte), r.([]byte)) < 0 {
// return true
// }
// return false
// }
func MustOpen() *InMemStore {
rv := InMemStore{
list: skiplist.NewStringMap(),
}
return &rv
}
func (i *InMemStore) Get(key []byte) ([]byte, error) {
val, ok := i.list.Get(string(key))

View File

@ -0,0 +1,17 @@
package upside_down
import (
"testing"
"github.com/couchbaselabs/bleve/index/store/inmem"
)
func BenchmarkInMemIndexing(b *testing.B) {
s, err := inmem.Open()
if err != nil {
b.Fatal(err)
}
defer s.Close()
CommonBenchmarkIndex(b, s)
}

View File

@ -36,6 +36,8 @@ func ParseFromKeyValue(key, value []byte) (UpsideDownCouchRow, error) {
return NewTermFrequencyRowKV(key, value)
case 'b':
return NewBackIndexRowKV(key, value)
case 's':
return NewStoredRowKV(key, value)
}
return nil, fmt.Errorf("Unknown field type '%s'", string(key[0]))
}
@ -53,9 +55,7 @@ func (v *VersionRow) Key() []byte {
}
func (v *VersionRow) Value() []byte {
buf := new(bytes.Buffer)
buf.WriteByte(byte(v.version))
return buf.Bytes()
return []byte{byte(v.version)}
}
func (v *VersionRow) String() string {
@ -86,19 +86,14 @@ type FieldRow struct {
}
func (f *FieldRow) Key() []byte {
buf := new(bytes.Buffer)
buf.WriteByte('f')
indexbuf := make([]byte, 2)
binary.LittleEndian.PutUint16(indexbuf, f.index)
buf.Write(indexbuf)
return buf.Bytes()
buf := make([]byte, 3)
buf[0] = 'f'
binary.LittleEndian.PutUint16(buf[1:3], f.index)
return buf
}
func (f *FieldRow) Value() []byte {
buf := new(bytes.Buffer)
buf.WriteString(f.name)
buf.WriteByte(BYTE_SEPARATOR)
return buf.Bytes()
return append([]byte(f.name), BYTE_SEPARATOR)
}
func (f *FieldRow) String() string {
@ -289,8 +284,9 @@ func (bie *BackIndexEntry) String() string {
}
type BackIndexRow struct {
doc []byte
entries []*BackIndexEntry
doc []byte
entries []*BackIndexEntry
storedFields []uint16
}
func (br *BackIndexRow) Key() []byte {
@ -309,17 +305,24 @@ func (br *BackIndexRow) Value() []byte {
binary.LittleEndian.PutUint16(fieldbuf, e.field)
buf.Write(fieldbuf)
}
for _, sf := range br.storedFields {
buf.WriteByte(BYTE_SEPARATOR)
fieldbuf := make([]byte, 2)
binary.LittleEndian.PutUint16(fieldbuf, sf)
buf.Write(fieldbuf)
}
return buf.Bytes()
}
func (br *BackIndexRow) String() string {
return fmt.Sprintf("Backindex DocId: `%s` Entries: %v", string(br.doc), br.entries)
return fmt.Sprintf("Backindex DocId: `%s` Entries: %v, Stored Fields: %v", string(br.doc), br.entries, br.storedFields)
}
func NewBackIndexRow(doc string, entries []*BackIndexEntry) *BackIndexRow {
func NewBackIndexRow(doc string, entries []*BackIndexEntry, storedFields []uint16) *BackIndexRow {
return &BackIndexRow{
doc: []byte(doc),
entries: entries,
doc: []byte(doc),
entries: entries,
storedFields: storedFields,
}
}
@ -340,6 +343,7 @@ func NewBackIndexRowKV(key, value []byte) (*BackIndexRow, error) {
buf = bytes.NewBuffer(value)
rv.entries = make([]*BackIndexEntry, 0)
rv.storedFields = make([]uint16, 0)
var term []byte
term, err = buf.ReadBytes(BYTE_SEPARATOR)
@ -350,14 +354,25 @@ func NewBackIndexRowKV(key, value []byte) (*BackIndexRow, error) {
return nil, err
}
for err != io.EOF {
ent := BackIndexEntry{}
ent.term = term[:len(term)-1] // trim off separator byte
if len(term) > 2 {
// this is a back index entry
ent := BackIndexEntry{}
ent.term = term[:len(term)-1] // trim off separator byte
err = binary.Read(buf, binary.LittleEndian, &ent.field)
if err != nil {
return nil, err
err = binary.Read(buf, binary.LittleEndian, &ent.field)
if err != nil {
return nil, err
}
rv.entries = append(rv.entries, &ent)
} else {
// this is a stored field entry
var sf uint16
err = binary.Read(buf, binary.LittleEndian, &sf)
if err != nil {
return nil, err
}
rv.storedFields = append(rv.storedFields, sf)
}
rv.entries = append(rv.entries, &ent)
term, err = buf.ReadBytes(BYTE_SEPARATOR)
if err != nil && err != io.EOF {
@ -367,3 +382,70 @@ func NewBackIndexRowKV(key, value []byte) (*BackIndexRow, error) {
return &rv, nil
}
// STORED
type StoredRow struct {
doc []byte
field uint16
value []byte
}
func (s *StoredRow) Key() []byte {
buf := new(bytes.Buffer)
buf.WriteByte('s')
buf.Write(s.doc)
buf.WriteByte(BYTE_SEPARATOR)
fieldbuf := make([]byte, 2)
binary.LittleEndian.PutUint16(fieldbuf, s.field)
buf.Write(fieldbuf)
return buf.Bytes()
}
func (s *StoredRow) Value() []byte {
return s.value
}
func (s *StoredRow) String() string {
return fmt.Sprintf("")
}
func (s *StoredRow) ScanPrefixForDoc() []byte {
buf := new(bytes.Buffer)
buf.WriteByte('s')
buf.Write(s.doc)
return buf.Bytes()
}
func NewStoredRow(doc string, field uint16, value []byte) *StoredRow {
return &StoredRow{
doc: []byte(doc),
field: field,
value: value,
}
}
func NewStoredRowKV(key, value []byte) (*StoredRow, error) {
rv := StoredRow{}
buf := bytes.NewBuffer(key)
buf.ReadByte() // type
var err error
rv.doc, err = buf.ReadBytes(BYTE_SEPARATOR)
if len(rv.doc) < 2 { // 1 for min doc id length, 1 for separator
err = fmt.Errorf("invalid doc length 0")
return nil, err
}
rv.doc = rv.doc[:len(rv.doc)-1] // trim off separator byte
err = binary.Read(buf, binary.LittleEndian, &rv.field)
if err != nil {
return nil, err
}
rv.value = value
return &rv, nil
}

View File

@ -55,15 +55,25 @@ func TestRows(t *testing.T) {
[]byte{3, 0, 0, 0, 0, 0, 0, 0, 195, 245, 72, 64, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0},
},
{
NewBackIndexRow("budweiser", []*BackIndexEntry{&BackIndexEntry{[]byte{'b', 'e', 'e', 'r'}, 0}}),
NewBackIndexRow("budweiser", []*BackIndexEntry{&BackIndexEntry{[]byte{'b', 'e', 'e', 'r'}, 0}}, []uint16{}),
[]byte{'b', 'b', 'u', 'd', 'w', 'e', 'i', 's', 'e', 'r'},
[]byte{'b', 'e', 'e', 'r', BYTE_SEPARATOR, 0, 0},
},
{
NewBackIndexRow("budweiser", []*BackIndexEntry{&BackIndexEntry{[]byte{'b', 'e', 'e', 'r'}, 0}, &BackIndexEntry{[]byte{'b', 'e', 'a', 't'}, 1}}),
NewBackIndexRow("budweiser", []*BackIndexEntry{&BackIndexEntry{[]byte{'b', 'e', 'e', 'r'}, 0}, &BackIndexEntry{[]byte{'b', 'e', 'a', 't'}, 1}}, []uint16{}),
[]byte{'b', 'b', 'u', 'd', 'w', 'e', 'i', 's', 'e', 'r'},
[]byte{'b', 'e', 'e', 'r', BYTE_SEPARATOR, 0, 0, 'b', 'e', 'a', 't', BYTE_SEPARATOR, 1, 0},
},
{
NewBackIndexRow("budweiser", []*BackIndexEntry{&BackIndexEntry{[]byte{'b', 'e', 'e', 'r'}, 0}, &BackIndexEntry{[]byte{'b', 'e', 'a', 't'}, 1}}, []uint16{3, 4, 5}),
[]byte{'b', 'b', 'u', 'd', 'w', 'e', 'i', 's', 'e', 'r'},
[]byte{'b', 'e', 'e', 'r', BYTE_SEPARATOR, 0, 0, 'b', 'e', 'a', 't', BYTE_SEPARATOR, 1, 0, BYTE_SEPARATOR, 3, 0, BYTE_SEPARATOR, 4, 0, BYTE_SEPARATOR, 5, 0},
},
{
NewStoredRow("budweiser", 0, []byte("an american beer")),
[]byte{'s', 'b', 'u', 'd', 'w', 'e', 'i', 's', 'e', 'r', BYTE_SEPARATOR, 0, 0},
[]byte{'a', 'n', ' ', 'a', 'm', 'e', 'r', 'i', 'c', 'a', 'n', ' ', 'b', 'e', 'e', 'r'},
},
}
// test going from struct to k/v bytes
@ -181,6 +191,16 @@ func TestInvalidRows(t *testing.T) {
[]byte{'b', 'b', 'u', 'd', 'w', 'e', 'i', 's', 'e', 'r'},
[]byte{'b', 'e', 'e', 'r', BYTE_SEPARATOR},
},
// type s, invalid key (missing id)
{
[]byte{'s'},
[]byte{'a', 'n', ' ', 'a', 'm', 'e', 'r', 'i', 'c', 'a', 'n', ' ', 'b', 'e', 'e', 'r'},
},
// type b, invalid val (missing field)
{
[]byte{'s', 'b', 'u', 'd', 'w', 'e', 'i', 's', 'e', 'r', BYTE_SEPARATOR},
[]byte{'a', 'n', ' ', 'a', 'm', 'e', 'r', 'i', 'c', 'a', 'n', ' ', 'b', 'e', 'e', 'r'},
},
}
for _, test := range tests {

View File

@ -246,6 +246,12 @@ func (udc *UpsideDownCouch) Update(doc *document.Document) error {
existingTermFieldMap[string(entry.term)] = true
}
}
existingStoredFieldMap := make(map[uint16]bool)
if backIndexRow != nil {
for _, sf := range backIndexRow.storedFields {
existingStoredFieldMap[sf] = true
}
}
// prepare a list of rows
updateRows := make([]UpsideDownCouchRow, 0)
@ -253,6 +259,7 @@ func (udc *UpsideDownCouch) Update(doc *document.Document) error {
// track our back index entries
backIndexEntries := make([]*BackIndexEntry, 0)
backIndexStoredFields := make([]uint16, 0)
for _, field := range doc.Fields {
fieldIndex, fieldExists := udc.fieldIndexes[field.Name]
@ -268,47 +275,63 @@ func (udc *UpsideDownCouch) Update(doc *document.Document) error {
existingTermFieldMap := existingTermFieldMaps[fieldIndex]
analyzer := field.Analyzer
tokens := analyzer.Analyze(field.Value)
fieldLength := len(tokens) // number of tokens in this doc field
fieldNorm := float32(1.0 / math.Sqrt(float64(fieldLength)))
tokenFreqs := analysis.TokenFrequency(tokens)
for _, tf := range tokenFreqs {
var termFreqRow *TermFrequencyRow
if document.IncludeTermVectors(field.IndexingOptions) {
tv := termVectorsFromTokenFreq(uint16(fieldIndex), tf)
termFreqRow = NewTermFrequencyRowWithTermVectors(tf.Term, uint16(fieldIndex), doc.ID, uint64(frequencyFromTokenFreq(tf)), fieldNorm, tv)
} else {
termFreqRow = NewTermFrequencyRow(tf.Term, uint16(fieldIndex), doc.ID, uint64(frequencyFromTokenFreq(tf)), fieldNorm)
}
if field.Options.IsIndexed() {
// record the back index entry
backIndexEntry := BackIndexEntry{tf.Term, uint16(fieldIndex)}
backIndexEntries = append(backIndexEntries, &backIndexEntry)
analyzer := field.Analyzer
tokens := analyzer.Analyze(field.Value)
fieldLength := len(tokens) // number of tokens in this doc field
fieldNorm := float32(1.0 / math.Sqrt(float64(fieldLength)))
tokenFreqs := analysis.TokenFrequency(tokens)
for _, tf := range tokenFreqs {
var termFreqRow *TermFrequencyRow
if field.Options.IncludeTermVectors() {
tv := termVectorsFromTokenFreq(uint16(fieldIndex), tf)
termFreqRow = NewTermFrequencyRowWithTermVectors(tf.Term, uint16(fieldIndex), doc.ID, uint64(frequencyFromTokenFreq(tf)), fieldNorm, tv)
} else {
termFreqRow = NewTermFrequencyRow(tf.Term, uint16(fieldIndex), doc.ID, uint64(frequencyFromTokenFreq(tf)), fieldNorm)
}
// remove the entry from the map of existing term fields if it exists
if existingTermFieldMap != nil {
termString := string(tf.Term)
_, ok := existingTermFieldMap[termString]
if ok {
// this is an update
updateRows = append(updateRows, termFreqRow)
// this term existed last time, delete it from that map
delete(existingTermFieldMap, termString)
// record the back index entry
backIndexEntry := BackIndexEntry{tf.Term, uint16(fieldIndex)}
backIndexEntries = append(backIndexEntries, &backIndexEntry)
// remove the entry from the map of existing term fields if it exists
if existingTermFieldMap != nil {
termString := string(tf.Term)
_, ok := existingTermFieldMap[termString]
if ok {
// this is an update
updateRows = append(updateRows, termFreqRow)
// this term existed last time, delete it from that map
delete(existingTermFieldMap, termString)
} else {
// this is an add
addRows = append(addRows, termFreqRow)
}
} else {
// this is an add
addRows = append(addRows, termFreqRow)
}
}
}
if field.Options.IsStored() {
storedRow := NewStoredRow(doc.ID, uint16(fieldIndex), field.Value)
_, ok := existingStoredFieldMap[uint16(fieldIndex)]
if ok {
// this is an update
updateRows = append(updateRows, storedRow)
// this field was stored last time, delete it from that map
delete(existingStoredFieldMap, uint16(fieldIndex))
} else {
// this is an add
addRows = append(addRows, termFreqRow)
addRows = append(addRows, storedRow)
}
}
}
// build the back index row
backIndexRow = NewBackIndexRow(doc.ID, backIndexEntries)
backIndexRow = NewBackIndexRow(doc.ID, backIndexEntries, backIndexStoredFields)
updateRows = append(updateRows, backIndexRow)
// any of the existing rows that weren't updated need to be deleted
@ -321,6 +344,11 @@ func (udc *UpsideDownCouch) Update(doc *document.Document) error {
}
}
}
// any of the existing stored fields that weren't updated need to be deleted
for storedFieldIndex, _ := range existingStoredFieldMap {
storedRow := NewStoredRow(doc.ID, storedFieldIndex, nil)
deleteRows = append(deleteRows, storedRow)
}
err = udc.batchRows(addRows, updateRows, deleteRows)
if err == nil && isAdd {
@ -345,6 +373,10 @@ func (udc *UpsideDownCouch) Delete(id string) error {
tfr := NewTermFrequencyRow(backIndexEntry.term, backIndexEntry.field, id, 0, 0)
rows = append(rows, tfr)
}
for _, sf := range backIndexRow.storedFields {
sf := NewStoredRow(id, sf, nil)
rows = append(rows, sf)
}
// also delete the back entry itself
rows = append(rows, backIndexRow)
@ -405,6 +437,33 @@ func (udc *UpsideDownCouch) TermFieldReader(term []byte, fieldName string) (inde
return newUpsideDownCouchTermFieldReader(udc, []byte{BYTE_SEPARATOR}, 0)
}
func (udc *UpsideDownCouch) Document(id string) (*document.Document, error) {
rv := document.NewDocument(id)
storedRow := NewStoredRow(id, 0, nil)
storedRowScanPrefix := storedRow.ScanPrefixForDoc()
it := udc.store.Iterator(storedRowScanPrefix)
key, val, valid := it.Current()
for valid {
if !bytes.HasPrefix(key, storedRowScanPrefix) {
break
}
row, err := NewStoredRowKV(key, val)
if err != nil {
return nil, err
}
if row != nil {
rv.AddField(&document.Field{
Name: udc.fieldIndexToName(row.field),
Value: row.Value(),
})
}
it.Next()
key, val, valid = it.Current()
}
return rv, nil
}
func frequencyFromTokenFreq(tf *analysis.TokenFreq) int {
return len(tf.Locations)
}

View File

@ -266,3 +266,53 @@ func TestIndexInsertMultiple(t *testing.T) {
t.Errorf("expected doc count: %d, got %d", expectedCount, docCount)
}
}
func TestIndexInsertWithStore(t *testing.T) {
defer os.RemoveAll("test")
store, err := gouchstore.Open("test")
idx := NewUpsideDownCouch(store)
err = idx.Open()
if err != nil {
t.Errorf("error opening index: %v", err)
}
defer idx.Close()
var expectedCount uint64 = 0
docCount := idx.DocCount()
if docCount != expectedCount {
t.Errorf("Expected document count to be %d got %d", expectedCount, docCount)
}
doc := document.NewDocument("1")
doc.AddField(document.NewTextFieldWithIndexingOptions("name", []byte("test"), document.INDEX_FIELD|document.STORE_FIELD))
err = idx.Update(doc)
if err != nil {
t.Errorf("Error updating index: %v", err)
}
expectedCount += 1
docCount = idx.DocCount()
if docCount != expectedCount {
t.Errorf("Expected document count to be %d got %d", expectedCount, docCount)
}
// should have 6 rows (1 for version, 1 for schema field, and 1 for single term, and 1 for the stored field and 1 for the term count, and 1 for the back index entry)
expectedLength := uint64(1 + 1 + 1 + 1 + 1 + 1)
rowCount := idx.rowCount()
if rowCount != expectedLength {
t.Errorf("expected %d rows, got: %d", expectedLength, rowCount)
}
storedDoc, err := idx.Document("1")
if err != nil {
t.Error(err)
}
if len(storedDoc.Fields) != 1 {
t.Errorf("expected 1 stored field, got %d", len(storedDoc.Fields))
}
if string(storedDoc.Fields[0].Value) != "test" {
t.Errorf("expected field content 'test', got '%s'", string(storedDoc.Fields[0].Value))
}
}

View File

@ -0,0 +1 @@
{"name":"21A IPA","abv":7.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Deep golden color. Citrus and piney hop aromas. Assertive malt backbone supporting the overwhelming bitterness. Dry hopped in the fermenter with four types of hops giving an explosive hop aroma. Many refer to this IPA as Nectar of the Gods. Judge for yourself. Now Available in Cans!","style":"American-Style India Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"563 Stout","abv":5.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Deep black color, toasted black burnt coffee flavors and aroma. Dispensed with Nitrogen through a slow-flow faucet giving it the characteristic cascading effect, resulting in a rich dense creamy head.","style":"American-Style Stout","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Amendment Pale Ale","abv":5.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Rich golden hue color. Floral hop with sweet malt aroma. Medium mouth feel with malt sweetness, hop quenching flavor and well-balanced bitterness.","style":"American-Style Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Bitter American","abv":3.6,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"An American session beer. Loaded with hop character and a malty presence, but lower in alcohol.","style":"Special Bitter or Best Bitter","category":"British Ale"}

View File

@ -0,0 +1 @@
{"name":"Double Trouble IPA","abv":9.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Deep, golden, rich malt flavor huge citrus, fruity grassy, ethanol sweetness aroma with a profound bitterness, yet balanced malt back bone with grapefruit, mellow citric overtones. Dry hopped three times in the fermenter. Brewed with over 65 lbs of hops for 300 gallons of beer. The beer to bring world peace and end the war. Bronze Medal - 2006 Imperial IPA Festival at the Bistro in Hayward, California.","style":"Imperial or Double India Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"General Pippo's Porter","abv":5.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Deep toffee color with rich roasty and subtle hop aroma. Chocolate flavors dominate the palate and interact with back-end sweetness.","style":"Porter","category":"Irish Ale"}

View File

@ -0,0 +1 @@
{"name":"North Star Red","abv":5.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Deep amber color. Subtle hop floral nose intertwined with sweet crystal malt aromas. Rich malt flavors supporting a slight bitterness finish.","style":"American-Style Amber/Red Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Oyster Point Oyster Stout","abv":5.9,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Deep black color. Chocolate milk color head, providing an array of Belgian lace. Toffee and light roasty aromas and flavors. A malty sweet taste is evident but, this rich oatmeal based stout finishes dry. Made with 20 lbs. of oysters, in the boil, from our good friends at Hog Island Oyster Company.","style":"American-Style Stout","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Potrero ESB","abv":5.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Traditional English E.S.B. made with English malt and hops. Fruity aroma with an imparted tart flavor brought about by replicating the water profile in England at Burton on Trent.","style":"Special Bitter or Best Bitter","category":"British Ale"}

View File

@ -0,0 +1 @@
{"name":"South Park Blonde","abv":5.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"Light golden color. Sweet dry aroma with crisp, clear bitterness. Brewed with imported German hops.The perfect beer to have when you'd like to have more than one.","style":"Golden or Blonde Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Watermelon Wheat","abv":5.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"21st_amendment_brewery_cafe","updated":"2010-07-22 20:00:20","description":"The definition of summer in a pint glass. This unique, American-style wheat beer, is brewed with 400 lbs. of fresh pressed watermelon in each batch. Light turbid, straw color, with the taste and essence of fresh watermelon. Finishes dry and clean. Now Available in Cans!","style":"Belgian-Style Fruit Lambic","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"21st Amendment Brewery Cafe","city":"San Francisco","state":"California","code":"94107","country":"United States","phone":"1-415-369-0900","website":"http://www.21st-amendment.com/","type":"brewery","updated":"2010-10-24 13:54:07","description":"The 21st Amendment Brewery offers a variety of award winning house made brews and American grilled cuisine in a comfortable loft like setting. Join us before and after Giants baseball games in our outdoor beer garden. A great location for functions and parties in our semi-private Brewers Loft. See you soon at the 21A!","address":["563 Second Street"],"geo":{"accuracy":"ROOFTOP","lat":37.7825,"lon":-122.393}}

View File

@ -0,0 +1 @@
{"name":"357","city":"","state":"","code":"","country":"","phone":"","website":"","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":[]}

View File

@ -0,0 +1 @@
{"name":"Drie Fonteinen Kriek","abv":5.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"3_fonteinen_brouwerij_ambachtelijke_geuzestekerij","updated":"2010-07-22 20:00:20","description":"","style":"Belgian-Style Fruit Lambic","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"Oude Geuze","abv":6.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"3_fonteinen_brouwerij_ambachtelijke_geuzestekerij","updated":"2010-07-22 20:00:20","description":"","style":"Belgian-Style Fruit Lambic","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"3 Fonteinen Brouwerij Ambachtelijke Geuzestekerij","city":"Beersel","state":"Vlaams Brabant","code":"","country":"Belgium","phone":"32-02-/-306-71-03","website":"http://www.3fonteinen.be/index.htm","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["Hoogstraat 2A"],"geo":{"accuracy":"RANGE_INTERPOLATED","lat":50.7668,"lon":4.3081}}

View File

@ -0,0 +1 @@
{"name":"(512) ALT","abv":6.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"(512) ALT is a German-style amber ale that is fermented cooler than typical ales and cold conditioned like a lager. ALT means “old” in German and refers to a beer style made using ale yeast after many German brewers had switched to newly discovered lager yeast. This ale has a very smooth, yet pronounced, hop bitterness with a malty backbone and a characteristic German yeast character. Made with 98% Organic 2-row and Munch malts and US noble hops.","style":"German-Style Brown Ale/Altbier","category":"German Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) Bruin","abv":7.6,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"At once cuddly and ferocious, (512) BRUIN combines a smooth, rich maltiness and mahogany color with a solid hop backbone and stealthy 7.6% alcohol. Made with Organic 2 Row and Munich malts, plus Chocolate and Crystal malts, domestic hops, and a touch of molasses, this brew has notes of raisins, dark sugars, and cocoa, and pairs perfectly with food and the crisp fall air.","style":"American-Style Brown Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) IPA","abv":7.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"(512) India Pale Ale is a big, aggressively dry-hopped American IPA with smooth bitterness (~65 IBU) balanced by medium maltiness. Organic 2-row malted barley, loads of hops, and great Austin water create an ale with apricot and vanilla aromatics that lure you in for more.","style":"American-Style India Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) Pale","abv":5.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"With Organic 2-row malted barley, (512) Pale is a copper colored American Pale Ale that balances earthy hop bitterness and hop flavor with a rich malty body.","style":"American-Style Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) Pecan Porter","abv":6.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"Nearly black in color, (512) Pecan Porter is made with Organic US 2-row and Crystal malts along with Bairds Chocolate and Black malts. Its full body and malty sweetness are balanced with subtle pecan aroma and flavor from locally grown pecans. Yet another true Austin original!","style":"Porter","category":"Irish Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) Whiskey Barrel Aged Double Pecan Porter","abv":8.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"Our first barrel project is in kegs and on its way to beer bars around Austin. This is a bigger, bolder version of our mainstay Pecan Porter, with a richer finish. Two months on recently emptied Jack Daniels select barrels imparted a wonderful vanilla character from the oak and a pleasant amount of whiskey nose and flavor. All in all, Im really proud of the hard work and effort put into this beer. Our first attempt at brewing it and our first attempt at managing barrels has paid off for everyone! Seek out this beer, but dont put it off. There is a very limited number of kegs available and it might go fast…","style":"Porter","category":"Irish Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) Wit","abv":5.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-22 20:00:20","description":"Made in the style of the Belgian wheat beers that are so refreshing, (512) Wit is a hazy ale spiced with coriander and domestic grapefruit peel. 50% US Organic 2-row malted barley and 50% US unmalted wheat and oats make this a light, crisp ale well suited for any occasion.","style":"Belgian-Style White","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"One","abv":8.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"512_brewing_company","updated":"2010-07-29 14:11:16","description":"Our first anniversary release is a Belgian-style strong ale that is amber in color, with a light to medium body. Subtle malt sweetness is balanced with noticeable hop flavor, light raisin and mildly spicy, cake-like flavors, and is finished with local wildflower honey aromas. Made with 80% Organic Malted Barley, Belgian Specialty grains, Forbidden Fruit yeast, domestic hops and Round Rock local wildflower honey, this beer is deceptively high in alcohol. ","style":"Belgian-Style Pale Strong Ale","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"(512) Brewing Company","city":"Austin","state":"Texas","code":"78745","country":"United States","phone":"512.707.2337","website":"http://512brewing.com/","type":"brewery","updated":"2010-07-22 20:00:20","description":"(512) Brewing Company is a microbrewery located in the heart of Austin that brews for the community using as many local, domestic and organic ingredients as possible.","address":["407 Radam, F200"],"geo":{"accuracy":"ROOFTOP","lat":30.2234,"lon":-97.7697}}

View File

@ -0,0 +1 @@
{"name":"Bock Beer","abv":5.9,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aass_brewery","updated":"2010-07-22 20:00:20","description":"The Aass Bock is a dark \"lager\" also produced in accordance with the \"Purity law\". \r\n\r\nThe malted barely is produced in the Scandinavian countries. It is a combination of bayer-, color- and caramel malt. \r\n\r\nWe use the very best of hops from the Hallertau area in Germany, and the water is pure Norwegian mountain water. \r\n\r\nThe Aass Bock is largered at cool temperatures, and it is allowed to mature for as long as 6 months before bottling. \r\n\r\nThe beer is sold in a caracteristic nice-looking green bottle containing 11.2 fl. oz or 330 ml.","style":"Traditional German-Style Bock","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Classic Special Brew","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aass_brewery","updated":"2010-07-22 20:00:20","description":"","style":"American-Style Lager","category":"North American Lager"}

View File

@ -0,0 +1 @@
{"name":"Genuine Pilsner","abv":5.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aass_brewery","updated":"2010-07-22 20:00:20","description":"The Aass Genuine Pilsner is a premium \"lager\" produced in accordance with the \"Purity law\". The malted barely is a pilsnermalt produced in the Sandinavian countries. We use the very best of hops known under the name as Sazer and Hallertau, and the liquid is pure Norwegian mountain water.\r\n\r\nThe pilsner is largered at cool temperatures, and it is allowed to mature for as long at 3 months before bottling. \r\n\r\nThe beer is sold in a caracteristic nice- looking green bottel containing 11.2 fl. oz or 330 ml.","style":"German-Style Pilsener","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Gull Classic","abv":5.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aass_brewery","updated":"2010-07-22 20:00:20","description":"\"Aass Classic\" is a classical \"lager\" The beer is slightly darker in color than our Genuine Pilsner. It is of course produced in accordance with the \"Purity law\". \r\n\r\nThe malted barely is produced in the Scadinavian countries. It is a combination of pilsner-, bayer- and caramelmalt. We use the famous Sazer and Hallertau hops, and the water is as pure as the Norwegian nature. \r\n\r\nThe Classic is largered at cool temperatures, and it is allowed to mature for approximately 3 months before bottling. \r\n\r\nThe beer is sold in caracteristic nice looking green bottel containing 11.2 fl. oz or 330 ml.","style":"American-Style Lager","category":"North American Lager"}

View File

@ -0,0 +1 @@
{"name":"Juleøl","abv":5.9,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aass_brewery","updated":"2010-07-22 20:00:20","description":"The Norwegian Juleøl can be traced back more that 1000 years. The \"Gulatinglov\", one of the first Norwegian laws written in the 9th century, has a chapter on brewing beer for the midwinter celebration in January. The festivities were held to celebrate the Norse Gods; Odin, Frøy and Njord as well as the \"return\" of the sun.\r\nFurthermore, the tradition was adopted by the Vikings that brought Christianity to the country between 1000 and 1100. They moved the tradition to December in connection with the birth of Jesus. \r\nIn those days a farmer that did not set aside the best crop to produce the beer could lose his house to the king and the church. The law has been changed, but we promise you that we still use the very best of malt, yeast, hops, and pure Norwegian water to produce our Christmas beer.\r\n\r\nAass Juleøl has won most of the Norwegian Juleøl taste test conducted over time. For those of you that know what \"lutefisk\" is, Aass Juleøl is the perfect choice of beverage to this traditional Norwegian dish.","style":"Dark American-Belgo-Style Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Aass Brewery","city":"Drammen","state":"","code":"","country":"Norway","phone":"47-32-26-60-00","website":"http://www.aass.no","type":"brewery","updated":"2010-07-22 20:00:20","description":"Aass Brewery was established in 1834 and is the oldest brewery in Norway today. It is located in the city of Drammen, approximately 25 miles south of our capital, Oslo. You will spot it at the banks of the Drammen River at the very same place as it has been since 1834. The annual production of beer is aprox. 10 mill liter (85 000 barrels), and together with the production of 18 mill liters of soft drinks and mineralwater it gives employment to approximately 150 people. You may wonder how we got our name ? It was a young fellow with the name Poul Lauritz Aass that bought the brewery in 1860. Since then it has been in the same family, and is today run by the 4th generation of Aass. (By the way - A real Norwegian viking would pronounce it Ouse) The brewery is proud of its history. To us the quality of what we produce has always been and always will be the main issue. In 1516, Duke Wilhelm of Bavaria established a law admitting only malted barley, hops yeast, and water as ingredients in the production of beer. The law is known as the purity law and Aass Brewery is still loyal to Duke Wilhelm of Bavaria. In Norway the brewery is known for outstanding quality and large number of different beer types.","address":["Ole Steensgt. 10 Postboks 1530"],"geo":{"accuracy":"APPROXIMATE","lat":59.7451,"lon":10.2135}}

View File

@ -0,0 +1 @@
{"name":"Brune (Brown)","abv":6.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_de_leffe","updated":"2010-07-22 20:00:20","description":"Dark brown with a nice tan head.\r\nSweet and aromatic.\r\nSmooth and best served cold from a goblet or chalice.\r\n\r\nI have had this on tab, in a 12oz bottle and 1 liter bottle. It is very common on to be on tap in Belgium and Paris as well as in a bottle in the UK.\r\n\r\nI have not seen it in the US nor can I find a distributor that could get it."}

View File

@ -0,0 +1 @@
{"name":"Leffe Blonde","abv":6.6,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_de_leffe","updated":"2010-07-22 20:00:20","description":"","style":"Golden or Blonde Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Abbaye de Leffe","city":"Dinant","state":"Namur","code":"","country":"Belgium","phone":"","website":"","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["Dinant"],"geo":{"accuracy":"APPROXIMATE","lat":50.2606,"lon":4.9122}}

View File

@ -0,0 +1 @@
{"name":"10","abv":10.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_de_maredsous","updated":"2010-07-22 20:00:20","description":"Moortgat's Maredsous 10 is an orangy-blond big sweet tripel nudging into a barley wine.","style":"Belgian-Style Tripel","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"6","abv":6.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_de_maredsous","updated":"2010-07-22 20:00:20","description":"Blonde Ales are one of the fastest growing beer styles in Belgium, France and the UK. Moorgat's Maredsous 6 is an easy drinking Blonde with a balance of graininess and aromatic hop.","style":"Golden or Blonde Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"8","abv":8.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_de_maredsous","updated":"2010-07-22 20:00:20","description":"Moortgat's Maredsous 8 is a fine strong dubbel that is sweet when young but develops a stout like flavour as it ages.","style":"Belgian-Style Dubbel","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"Abbaye de Maredsous","city":"Dene","state":"Namur","code":"","country":"Belgium","phone":"32-(0)82-69-82-11","website":"http://www.maredsous10.be/","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["Rue de Maredsous, 11"],"geo":{"accuracy":"GEOMETRIC_CENTER","lat":50.3093,"lon":4.7646}}

View File

@ -0,0 +1 @@
{"name":"Rochefort 10","abv":11.3,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_notre_dame_du_st_remy","updated":"2010-07-22 20:00:20","description":"Reddish-brown colour, with a very compact head and an aroma of figs, feels like honey in the mouth. The alcohol profile is a major component in the flavour of this rich ale. It is very similar to 6 and 8, but has much more of everything. Some may find the high alcohol content to be disagreeable.","style":"Belgian-Style Quadrupel","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"Rochefort 6","abv":7.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_notre_dame_du_st_remy","updated":"2010-07-22 20:00:20","description":"eddish colour, almost like autumn leaves, very consistent texture with a slightly spicy aroma and an intense taste of caramel, fruit, and hints of raisins. It is only brewed about once per year, representing approximately 1% of total beer production, thus is quite difficult to obtain.","style":"Dark American-Belgo-Style Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Rochefort 8","abv":9.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abbaye_notre_dame_du_st_remy","updated":"2010-07-22 20:00:20","description":"Yellowish-brown colour, with a more pronounced aroma, more fruits and a slight amount of Demi-Sec. This variety constitutes the largest proportion of production."}

View File

@ -0,0 +1 @@
{"name":"Abbaye Notre Dame du St Remy","city":"Rochefort","state":"Namur","code":"","country":"Belgium","phone":"32-084/22.01.47","website":"","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["Rue de l'Abbaye 8"],"geo":{"accuracy":"RANGE_INTERPOLATED","lat":50.1999,"lon":5.2277}}

View File

@ -0,0 +1 @@
{"name":"Abbey Wright Brewing/Valley Inn","city":"Williamsport","state":"Pennsylvania","code":"17702","country":"United States","phone":"570.326.3383","website":"http://www.valleyinnonline.com/","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["204 Valley Street"],"geo":{"accuracy":"RANGE_INTERPOLATED","lat":41.2225,"lon":-77.0369}}

View File

@ -0,0 +1 @@
{"name":"Scottish Ale","abv":4.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aberdeen_brewing","updated":"2010-07-22 20:00:20","description":""}

View File

@ -0,0 +1 @@
{"name":"Aberdeen Brewing","city":"Valparaiso","state":"Indiana","code":"","country":"United States","phone":"(219) 548-3300","website":"","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["210 Aberdeen Dr."],"geo":{"accuracy":"RANGE_INTERPOLATED","lat":41.4392,"lon":-87.1078}}

View File

@ -0,0 +1 @@
{"name":"Abhi beer","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abhi_brewery","updated":"2011-09-27 00:36:14","description":"","style":"Bamberg-Style Bock Rauchbier","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Abhi Brewery","city":"","state":"","code":"","country":"India","phone":"","website":"","type":"brewery","updated":"2011-09-27 00:35:48","description":"","address":[]}

View File

@ -0,0 +1 @@
{"name":"Abbey Ale","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:51:39","description":"Abbey Ale is a strong malty ale brewed in the style of the Trappist Monks. 22oz bottles only","style":"Classic English-Style Pale Ale","category":"British Ale"}

View File

@ -0,0 +1 @@
{"name":"Abita Amber","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:46:53","description":"Abita Amber is a Munich style lager. It has a smooth, malty, slightly caramel flavor and a rich amber color.","style":"Vienna-Style Lager","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Abita Golden","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:49:38","description":"Abita Golden is a crisp, clean continental lager. Just four ingredients are all it takes.","style":"American-Style Lager","category":"North American Lager"}

View File

@ -0,0 +1 @@
{"name":"Abita Jockamo IPA","abv":6.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"","style":"American-Style India Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Abita Light Beer","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:50:18","description":"Uniquely hand-crafted using 100% all natural ingredients: The result is the smoothest, most flavorful light beer.","style":"American-Style Light Lager","category":"North American Lager"}

View File

@ -0,0 +1 @@
{"name":"Abita Purple Haze","abv":4.75,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"Purple Haze is a crisp, American style wheat beer with raspberry puree added after filtration. Therefore, you may see raspberry pulp in the beer. The raspberries provide the lager with a subtle purple coloration and haze, a fruity aroma, and a tartly sweet taste.","style":"Light American Wheat Ale or Lager","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Andygator","abv":8.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"The Andygator is a fearsome beast...much too big for any little bottle. Don't let his toothy grin, slightly sweet flavor and subtle fruit aroma fool you: this cold-blooded creature is a Helles Doppelbock that can sneak up on you. Sip, don't gulp and taste the wild of Abita Andygator","style":"German-Style Doppelbock","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Christmas Ale","abv":5.5,"ibu":38.0,"srm":35.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-12-19 09:11:43","description":"Abita Christmas Ale is an American style Brown Ale. It is hopped with Willamette, Cascade, and Columbus hops and has a good hop flavor and aroma.","style":"American-Style Brown Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Fall Fest","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"Abita Fall Fest is an Octoberfest lager available September-November. It is brewed with German Hersbrucker hops and pale, crystal and chocolate malts.\r\n\r\nThe result is a full-bodied, malty beer with a strong hop character and a beautiful amber color.\r\n\r\nCelebrate the season with Abita Fall Fest and your favorite German food","style":"German-Style Oktoberfest","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Honey Rye Ale","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:54:55","description":"This beer is not very bitter which allows the flavors of the rye and honey to come through. The result is a full bodied, sweet, and malty beer.","style":"Specialty Honey Lager or Ale","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Purple Haze","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"Purple Haze is a crisp, American style wheat beer with raspberry puree added after filtration. Therefore, you may see raspberry pulp in the beer. The raspberries provide the lager with a subtle purple coloration and haze, a fruity aroma, and a tartly sweet taste.","style":"Light American Wheat Ale or Lager","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Restoration Pale Ale","abv":5.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"Abita Restoration Pale Ale was created after the back-to-back hurricanes devastated Louisiana. With your help, the abita Brewing Company raised over $500,000 for hurricane relief. This brilliant, golden pale ale has a rich body, mild bitterness and a snappy, fresh citrus hop flavor and aroma. Cold filtered and brewed in small batches using no additives or preservatives, the Abita Company is proud to be Louisiana True.","style":"American-Style Pale Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"S.O.S","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:48:20","description":"Save Our Shores (S.O.S) is an unfiltered weizen pils and has a brilliant golden color. A Charitable Pilsner","style":"German-Style Pilsener","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Satsuma Harvest Wit","abv":5.1,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"Real Louisiana Satsumas, golden wheat, oats and the finest barley create Abita Satsuma Harvest Wit. Pale and cloudy, like the haze on a hot summer day, this white beer has a sweet and subtle citrus flavor with a touch of spice that is cool and refreshing. \r\n\r\nAbita Satsuma Harvest Wit is very versatile and can compliment a number of dishes. This brew pairs well with salads, fish, shrimp and lobster, as long as the dishes are not too spicy. Thai dishes, which often have citric notes in their flavor profile, would also perfectly compliment the orange flavors in Abita Satsuma Harvest Wit.","style":"Belgian-Style White","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"Satsuma Wit","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:56:18","description":"This Wit beer has a sweet and subtle citrus flavor with a touch of spice that is cool and refreshing.","style":"Fruit Beer","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Strawberry","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:55:43","description":"Strawberry Harvest Lager is a wheat beer made with real Louisiana strawberries.","style":"Fruit Beer","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Strawberry Harvest Lager","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"","style":"Fruit Beer","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Triple Citra Hopped Satsuma Wit","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:53:48","description":"Similar to the Harvest Wit but with a lot more punch. A very pronounce citrus aroma with a triple hop flavor makes for one great beer.","style":"Fruit Beer","category":"Other Style"}

View File

@ -0,0 +1 @@
{"name":"Turbodog","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2010-07-22 20:00:20","description":"Abita Turbodog is a dark brown ale brewed with Willamette hops and a combination of British pale, crystal and chocolate malts.\r\n\r\nThis combination gives Turbodog its rich body and color and a sweet chocolate-toffee like flavor. Turbodog began as a specialty ale but has gained a huge loyal following and has become one of our three flagship brews.\r\n\r\nJust a bit stronger than our other brews, so . . .beware of the dog!","style":"Dark American-Belgo-Style Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Wheat","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"abita_brewing_company","updated":"2011-08-15 11:52:34","description":"Abita Wheat is a lager, not ale, and contains a generous amount of wheat which produces a clean, simple flavor.","style":"Vienna-Style Lager","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Abita Brewing Company","city":"Abita Springs","state":"Louisiana","code":"70420","country":"United States","phone":"800-737-2311","website":"http://www.abita.com/","type":"brewery","updated":"2010-07-22 20:00:20","description":"Founded in 1986, the Abita Brewing Company is nestled in the piney woods 30 miles north of New Orleans. In its first year, the brewery produced 1,500 barrels of beer. We had no idea what we started. Customers loved our beer! By 1994, we outgrew the original site (now our 100 seat brew pub) and were forced to move up the road to a larger facility to keep up with demand. Today, we brew over 62,000 barrels of beer and 3,000 barrels of root beer, and we're still growing! But don't let our growth fool you. Our lagers and ales are still brewed in small batches, hand-crafted by a team of dedicated workers with only the highest ideals of quality. This pride, along with our brewing process, is what creates our great brews. Abita uses only the finest ingredients British and American barley, German yeast, European and Washington State hops and the pure artesian water of Abita Springs. In Abita, we are blessed with the purest of water. Drawn from our deep wells, our pristine water is not altered in any way. Abita Beer has no preservatives, additives or stabilizers and is cold filtered. The result is beer that is the finest and freshest tasting as proven by our loyal customers and great chefs of the south who use Abita Beer in their recipes. We're proud of our brewery and the beers we make. Try Abita Beer today and taste a bit of the South!","address":["PO Box 1510"],"geo":{"accuracy":"APPROXIMATE","lat":30.5049,"lon":-89.944}}

View File

@ -0,0 +1 @@
{"name":"Adnam's Suffolk Special Bitter","abv":4.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"adnams_co","updated":"2010-07-22 20:00:20","description":"Adnams Suffolk Special Bitter, at 4.5% alcohol by volume, is a dry, crisp, refreshing, and distinctly hoppy example of its style.\r\n\r\nThis beer is also available in some markets in tradtional cask form, as Adnams Bitter. At only 3.7% alcohol, Adnams Bitter is the classic English 'ordinary,' though we think you'll agree that there's full of flavor.\r\n\r\nDespite its rich heritage and enduring fame, Adnams is not a company willing to rest on its laurels. Its continued commitment to quality and innovative packaging designs has made it Britains fastest growing brewery over the last two years. To top it off, Adnams head brewer was recently chosen as Britains Brewer of the Year by a panel of his peers.","style":"Extra Special Bitter","category":"British Ale"}

View File

@ -0,0 +1 @@
{"name":"Adnams Explorer","abv":5.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"adnams_co","updated":"2010-07-22 20:00:20","description":"A golden bitter suffused with the aromas of a grapefruit grove. The massive citrus attack will burst on your palate allowing all the flavours of the imported New World hops to deliver their fruity bitterness.","style":"Ordinary Bitter","category":"British Ale"}

View File

@ -0,0 +1 @@
{"name":"Adnams Fisherman","abv":4.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"adnams_co","updated":"2010-07-22 20:00:20","description":"The Winter seasonal from Adnams, Fisherman is described as an old ale. If your local pub has the good sense to serve cask beer, look for Fisherman on draught from January through March.\r\n\r\nClean and refreshing yet dark and mysterious, Fisherman is a deep coppery red, conjuring roasted nuts and dark chocolate, with a lingering taste of liquorice and dried fruits.","style":"American-Style Amber/Red Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Adnams Tally Ho","abv":7.5,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"adnams_co","updated":"2010-07-22 20:00:20","description":"The original winter warmer, Tally-Ho is actually a barley wine brewed to a nineteenth century recipe. It's sweet; it's heart-warming; it's rich - but watch out, it's got a kick like a drayman's horse!","style":"American-Style Barley Wine Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Broadside Ale","abv":6.3,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"adnams_co","updated":"2010-07-22 20:00:20","description":"Rich fruitcake aromas almonds, zest and conserved fruit. A wonderful balance of malt and hop flavours. A beer to savour and rich in flavour.","style":"Extra Special Bitter","category":"British Ale"}

View File

@ -0,0 +1 @@
{"name":"Nut Brown Ale","abv":3.2,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"adnams_co","updated":"2010-07-22 20:00:20","description":"","style":"American-Style Brown Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Adnams & Co.","city":"Southwold","state":"Suffolk","code":"","country":"United Kingdom","phone":"44-(01502)-727200","website":"http://www.adnams.co.uk","type":"brewery","updated":"2010-07-22 20:00:20","description":"Adnams is one of England's oldest and most respected breweries, but despite its rich heritage and enduring fame, Adnams is not a company willing to rest on its laurels. Its continued commitment to quality and innovative packaging designs has made it Britain's fastest growing brewery over the last two years. To top it off, Adnams' head brewer was recently chosen as Britain's Brewer of the Year by a panel of his peers. The aggressive team at Adnams is raring to go and keen to make a mark in the U.S.","address":["IP18 6JW"],"geo":{"accuracy":"APPROXIMATE","lat":52.3277,"lon":1.6802}}

View File

@ -0,0 +1 @@
{"name":"Affligem Dubbel","abv":6.8,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"affligem_brouwerij","updated":"2010-07-22 20:00:20","description":"A reddish-brown abbey ale brewed with dark malts. The secondary fermentation gives a fruity aroma and a unique, spicy character with a distinctive aftertaste. Secondary fermentation in the bottle. Contains barley malt.","style":"Belgian-Style Dubbel","category":"Belgian and French Ale"}

View File

@ -0,0 +1 @@
{"name":"Affligem Brouwerij","city":"Opwijk","state":"","code":"","country":"Belgium","phone":"052/35 83 57","website":"http://www.affligembeer.be/","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":[],"geo":{"accuracy":"APPROXIMATE","lat":50.9699,"lon":4.1892}}

View File

@ -0,0 +1 @@
{"name":"Catfish Cream Ale","abv":5.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"agassiz_brewing","updated":"2010-07-22 20:00:20","description":"","style":"American-Style Lager","category":"North American Lager"}

View File

@ -0,0 +1 @@
{"name":"Agassiz Brewing","city":"Winnipeg","state":"Manitoba","code":"0","country":"Canada","phone":"1-204-233-4677","website":"","type":"brewery","updated":"2010-07-22 20:00:20","description":"","address":["PO Box 42008"],"geo":{"accuracy":"APPROXIMATE","lat":49.7652,"lon":-97.1539}}

View File

@ -0,0 +1 @@
{"name":"Bavarian Bock","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aksarben_brewing_bop","updated":"2010-07-22 20:00:20","description":"","style":"Traditional German-Style Bock","category":"German Lager"}

View File

@ -0,0 +1 @@
{"name":"Bourbon Imperial Stout","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aksarben_brewing_bop","updated":"2010-07-22 20:00:20","description":""}

View File

@ -0,0 +1 @@
{"name":"Brout","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aksarben_brewing_bop","updated":"2010-07-22 20:00:20","description":""}

View File

@ -0,0 +1 @@
{"name":"Harvest Brown","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aksarben_brewing_bop","updated":"2010-07-22 20:00:20","description":"","style":"American-Style Brown Ale","category":"North American Ale"}

View File

@ -0,0 +1 @@
{"name":"Heater","abv":0.0,"ibu":0.0,"srm":0.0,"upc":0,"type":"beer","brewery_id":"aksarben_brewing_bop","updated":"2010-07-22 20:00:20","description":"","style":"American-Style Amber/Red Ale","category":"North American Ale"}

Some files were not shown because too many files have changed in this diff Show More