github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/core/index/termsHashConsumerPerField.go (about)

     1  package index
     2  
     3  import (
     4  	// "fmt"
     5  	. "github.com/balzaczyy/golucene/core/analysis/tokenattributes"
     6  	"github.com/balzaczyy/golucene/core/codec"
     7  	. "github.com/balzaczyy/golucene/core/codec/spi"
     8  	. "github.com/balzaczyy/golucene/core/index/model"
     9  	"github.com/balzaczyy/golucene/core/util"
    10  )
    11  
    12  // type TermsHashConsumerPerField interface {
    13  // 	start([]IndexableField, int) (bool, error)
    14  // 	finish() error
    15  // 	startField(IndexableField) error
    16  // 	newTerm(int) error
    17  // 	streamCount() int
    18  // 	createPostingsArray(int) *ParallelPostingsArray
    19  // }
    20  
    21  // index/TermVectorsConsumerPerField.java
    22  
    23  type TermVectorsConsumerPerField struct {
    24  	*TermsHashPerFieldImpl
    25  
    26  	termVectorsPostingsArray *TermVectorsPostingArray
    27  
    28  	termsWriter *TermVectorsConsumer
    29  
    30  	doVectors, doVectorPositions, doVectorOffsets, doVectorPayloads bool
    31  
    32  	payloadAttribute PayloadAttribute
    33  	offsetAttribute  OffsetAttribute
    34  	hasPayloads      bool // if enabled, and we actually saw any for this field
    35  }
    36  
    37  func newTermVectorsConsumerPerField(invertState *FieldInvertState,
    38  	termsWriter *TermVectorsConsumer,
    39  	fieldInfo *FieldInfo) *TermVectorsConsumerPerField {
    40  
    41  	ans := &TermVectorsConsumerPerField{
    42  		termsWriter: termsWriter,
    43  	}
    44  	ans.TermsHashPerFieldImpl = new(TermsHashPerFieldImpl)
    45  	ans.TermsHashPerFieldImpl._constructor(
    46  		ans, 2, invertState, termsWriter, nil, fieldInfo)
    47  	return ans
    48  }
    49  
    50  func (c *TermVectorsConsumerPerField) start(field IndexableField, first bool) bool {
    51  	t := field.FieldType()
    52  	assert(t.Indexed())
    53  
    54  	if first {
    55  
    56  		if c.bytesHash.Size() != 0 {
    57  			// only necessary if previous doc hit a non-aborting error
    58  			// while writing vectors in this field:
    59  			c.reset()
    60  		}
    61  
    62  		c.bytesHash.Reinit()
    63  
    64  		c.hasPayloads = false
    65  
    66  		if c.doVectors = t.StoreTermVectors(); c.doVectors {
    67  			panic("not implemented yet")
    68  		} else {
    69  			assert2(!t.StoreTermVectorOffsets(),
    70  				"cannot index term vector offsets when term vectors are not indexed (field='%v')",
    71  				field.Name())
    72  			assert2(!t.StoreTermVectorPositions(),
    73  				"cannot index term vector positions when term vectors are not indexed (field='%v')",
    74  				field.Name())
    75  			assert2(!t.StoreTermVectorPayloads(),
    76  				"cannot index term vector payloads when term vectors are not indexed (field='%v')",
    77  				field.Name())
    78  		}
    79  	} else {
    80  		panic("not implemented yet")
    81  	}
    82  
    83  	if c.doVectors {
    84  		panic("not implemented yet")
    85  	}
    86  
    87  	return c.doVectors
    88  }
    89  
    90  // /*
    91  // Called once per field per document if term vectors are enabled, to
    92  // write the vectors to RAMOutputStream, which is then quickly flushed
    93  // to the real term vectors files in the Directory.
    94  // */
    95  // func (c *TermVectorsConsumerPerField) finish() error {
    96  // 	if !c.doVectors || c.termsHashPerField.bytesHash.Size() == 0 {
    97  // 		return nil
    98  // 	}
    99  // 	c.termsWriter.addFieldToFlush(c)
   100  // 	return nil
   101  // }
   102  
   103  func (c *TermVectorsConsumerPerField) finishDocument() error {
   104  	panic("not implemented yet")
   105  }
   106  
   107  // func (c *TermVectorsConsumerPerField) shrinkHash() {
   108  // 	c.termsHashPerField.shrinkHash(c.maxNumPostings)
   109  // 	c.maxNumPostings = 0
   110  // }
   111  
   112  // func (c *TermVectorsConsumerPerField) startField(f IndexableField) error {
   113  // 	atts := c.fieldState.attributeSource
   114  // 	if c.doVectorOffsets {
   115  // 		c.offsetAttribute = atts.Add("OffsetAttribute").(OffsetAttribute)
   116  // 	} else {
   117  // 		c.offsetAttribute = nil
   118  // 	}
   119  // 	if c.doVectorPayloads && atts.Has("PayloadAttribute") {
   120  // 		c.payloadAttribute = atts.Get("PayloadAttribute").(PayloadAttribute)
   121  // 	} else {
   122  // 		c.payloadAttribute = nil
   123  // 	}
   124  // 	return nil
   125  // }
   126  
   127  func (c *TermVectorsConsumerPerField) newTerm(termId int) {
   128  	panic("not implemented yet")
   129  }
   130  
   131  func (c *TermVectorsConsumerPerField) addTerm(termid int) {
   132  	panic("not implemented yet")
   133  }
   134  
   135  func (c *TermVectorsConsumerPerField) newPostingsArray() {
   136  	if c.postingsArray != nil {
   137  		c.termVectorsPostingsArray = c.postingsArray.PostingsArray.(*TermVectorsPostingArray)
   138  	} else {
   139  		c.termVectorsPostingsArray = nil
   140  	}
   141  }
   142  
   143  func (c *TermVectorsConsumerPerField) createPostingsArray(size int) *ParallelPostingsArray {
   144  	return newTermVectorsPostingArray(size)
   145  }
   146  
   147  type TermVectorsPostingArray struct {
   148  	freqs         []int // How many times this term occurred in the current doc
   149  	lastOffsets   []int // Last offset we saw
   150  	lastPositions []int //Last position where this term occurred
   151  }
   152  
   153  func newTermVectorsPostingArray(size int) *ParallelPostingsArray {
   154  	ans := new(TermVectorsPostingArray)
   155  	return newParallelPostingsArray(ans, size)
   156  }
   157  
   158  func (arr *TermVectorsPostingArray) newInstance(size int) PostingsArray {
   159  	return newTermVectorsPostingArray(size)
   160  }
   161  
   162  func (arr *TermVectorsPostingArray) copyTo(toArray PostingsArray, numToCopy int) {
   163  	panic("not implemented yet")
   164  }
   165  
   166  func (arr *TermVectorsPostingArray) bytesPerPosting() int {
   167  	return BYTES_PER_POSTING + 3*util.NUM_BYTES_INT
   168  }
   169  
   170  // TODO: break into separate freq and prox writers as codes; make
   171  // separate container (tii/tis/skip/*) that can be configured as any
   172  // number of files 1..N
   173  type FreqProxTermsWriterPerField struct {
   174  	*TermsHashPerFieldImpl
   175  
   176  	freqProxPostingsArray *FreqProxPostingsArray
   177  
   178  	// parent            *FreqProxTermsWriter
   179  	// termsHashPerField *TermsHashPerField
   180  	// fieldInfo         *FieldInfo
   181  	// docState          *docState
   182  	// fieldState        *FieldInvertState
   183  
   184  	hasFreq          bool
   185  	hasProx          bool
   186  	hasOffsets       bool
   187  	hasPayloads      bool
   188  	payloadAttribute PayloadAttribute
   189  	offsetAttribute  OffsetAttribute
   190  
   191  	sawPayloads bool // true if any token had a payload in the current segment
   192  }
   193  
   194  func newFreqProxTermsWriterPerField(invertState *FieldInvertState,
   195  	termsHash TermsHash, fieldInfo *FieldInfo,
   196  	nextPerField TermsHashPerField) *FreqProxTermsWriterPerField {
   197  
   198  	indexOptions := fieldInfo.IndexOptions()
   199  	assert(int(indexOptions) != 0)
   200  	hasProx := indexOptions >= INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS
   201  	ans := &FreqProxTermsWriterPerField{
   202  		hasFreq:    indexOptions >= INDEX_OPT_DOCS_AND_FREQS,
   203  		hasProx:    hasProx,
   204  		hasOffsets: indexOptions >= INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS,
   205  	}
   206  	streamCount := map[bool]int{true: 2, false: 1}[hasProx]
   207  	ans.TermsHashPerFieldImpl = new(TermsHashPerFieldImpl)
   208  	ans.TermsHashPerFieldImpl._constructor(
   209  		ans, streamCount, invertState,
   210  		termsHash, nextPerField, fieldInfo,
   211  	)
   212  	return ans
   213  }
   214  
   215  // func (w *FreqProxTermsWriterPerField) streamCount() int {
   216  // 	if !w.hasProx {
   217  // 		return 1
   218  // 	}
   219  // 	return 2
   220  // }
   221  
   222  func (w *FreqProxTermsWriterPerField) finish() error {
   223  	err := w.TermsHashPerFieldImpl.finish()
   224  	if err == nil && w.sawPayloads {
   225  		panic("not implemented yet")
   226  		// w.fieldInfo.SetStorePayloads()
   227  	}
   228  	return err
   229  }
   230  
   231  /* Called after flush */
   232  // func (w *FreqProxTermsWriterPerField) reset() {
   233  // 	// record, up front, whether our in-RAM format will be
   234  // 	// with or without term freqs:
   235  // 	w.setIndexOptions(w.fieldInfo.IndexOptions())
   236  // 	w.payloadAttribute = nil
   237  // }
   238  
   239  // func (w *FreqProxTermsWriterPerField) setIndexOptions(indexOptions IndexOptions) {
   240  // 	if n := int(indexOptions); n > 0 {
   241  // 		w.hasFreq = n >= int(INDEX_OPT_DOCS_AND_FREQS)
   242  // 		w.hasProx = n >= int(INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS)
   243  // 		w.hasOffsets = n >= int(INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS)
   244  // 	} else {
   245  // 		// field could later be updated with indexed=true, so set everything on
   246  // 		w.hasFreq = true
   247  // 		w.hasProx = true
   248  // 		w.hasOffsets = true
   249  // 	}
   250  // }
   251  
   252  // func (w *FreqProxTermsWriterPerField) start(fields []IndexableField, count int) (bool, error) {
   253  // 	for _, field := range fields[:count] {
   254  // 		if field.FieldType().Indexed() {
   255  // 			return true, nil
   256  // 		}
   257  // 	}
   258  // 	return false, nil
   259  // }
   260  
   261  func (w *FreqProxTermsWriterPerField) start(f IndexableField, first bool) bool {
   262  	w.TermsHashPerFieldImpl.start(f, first)
   263  	w.payloadAttribute = w.fieldState.payloadAttribute
   264  	w.offsetAttribute = w.fieldState.offsetAttribute
   265  	return true
   266  }
   267  
   268  func (w *FreqProxTermsWriterPerField) writeProx(termId, proxCode int) {
   269  	if w.payloadAttribute == nil {
   270  		w.writeVInt(1, proxCode<<1)
   271  	} else {
   272  		payload := w.payloadAttribute.Payload()
   273  		if len(payload) > 0 {
   274  			panic("not implemented yet")
   275  		} else {
   276  			w.writeVInt(1, proxCode<<1)
   277  		}
   278  	}
   279  
   280  	assert(w.postingsArray.PostingsArray == w.freqProxPostingsArray)
   281  	w.freqProxPostingsArray.lastPositions[termId] = w.fieldState.position
   282  }
   283  
   284  func (w *FreqProxTermsWriterPerField) writeOffsets(termId, offsetAccum int) {
   285  	panic("not implemented yet")
   286  }
   287  
   288  func (w *FreqProxTermsWriterPerField) newTerm(termId int) {
   289  	// First time we're seeing this term since the last flush
   290  	w.docState.testPoint("FreqProxTermsWriterPerField.newTerm start")
   291  
   292  	postings := w.freqProxPostingsArray
   293  	assert(postings != nil)
   294  
   295  	postings.lastDocIDs[termId] = w.docState.docID
   296  	if !w.hasFreq {
   297  		assert(postings.termFreqs == nil)
   298  		postings.lastDocCodes[termId] = w.docState.docID
   299  	} else {
   300  		postings.lastDocCodes[termId] = w.docState.docID << 1
   301  		postings.termFreqs[termId] = 1
   302  		if w.hasProx {
   303  			w.writeProx(termId, w.fieldState.position)
   304  			if w.hasOffsets {
   305  				w.writeOffsets(termId, w.fieldState.offset)
   306  			}
   307  		} else {
   308  			assert(!w.hasOffsets)
   309  		}
   310  	}
   311  	if 1 > w.fieldState.maxTermFrequency {
   312  		w.fieldState.maxTermFrequency = 1
   313  	}
   314  	w.fieldState.uniqueTermCount++
   315  }
   316  
   317  func (w *FreqProxTermsWriterPerField) addTerm(termId int) {
   318  	w.docState.testPoint("FreqProxTermsWriterPerField.addTerm start")
   319  
   320  	postings := w.freqProxPostingsArray
   321  
   322  	assert(!w.hasFreq || postings.termFreqs[termId] > 0)
   323  
   324  	if !w.hasFreq {
   325  		panic("not implemented yet")
   326  	} else if w.docState.docID != postings.lastDocIDs[termId] {
   327  		assert2(w.docState.docID > postings.lastDocIDs[termId],
   328  			"id: %v postings ID: %v termID: %v",
   329  			w.docState.docID, postings.lastDocIDs[termId], termId)
   330  		// Term not yet seen in the current doc but previously seen in
   331  		// other doc(s) since the last flush
   332  
   333  		// Now that we know doc freq for previous doc, write it & lastDocCode
   334  		if 1 == postings.termFreqs[termId] {
   335  			w.writeVInt(0, postings.lastDocCodes[termId]|1)
   336  		} else {
   337  			w.writeVInt(0, postings.lastDocCodes[termId])
   338  			w.writeVInt(0, postings.termFreqs[termId])
   339  		}
   340  
   341  		// Init freq for the current document
   342  		postings.termFreqs[termId] = 1
   343  		if w.fieldState.maxTermFrequency < 1 {
   344  			w.fieldState.maxTermFrequency = 1
   345  		}
   346  		postings.lastDocCodes[termId] = (w.docState.docID - postings.lastDocIDs[termId]) << 1
   347  		postings.lastDocIDs[termId] = w.docState.docID
   348  		if w.hasProx {
   349  			w.writeProx(termId, w.fieldState.position)
   350  			if w.hasOffsets {
   351  				panic("niy")
   352  			}
   353  		} else {
   354  			assert(!w.hasOffsets)
   355  		}
   356  		w.fieldState.uniqueTermCount++
   357  	} else {
   358  		postings.termFreqs[termId]++
   359  		if n := postings.termFreqs[termId]; n > w.fieldState.maxTermFrequency {
   360  			w.fieldState.maxTermFrequency = n
   361  		}
   362  		if w.hasProx {
   363  			w.writeProx(termId, w.fieldState.position-postings.lastPositions[termId])
   364  			if w.hasOffsets {
   365  				w.writeOffsets(termId, w.fieldState.offset)
   366  			}
   367  		}
   368  	}
   369  }
   370  
   371  func (w *FreqProxTermsWriterPerField) newPostingsArray() {
   372  	if arr := w.postingsArray; arr != nil {
   373  		w.freqProxPostingsArray = arr.PostingsArray.(*FreqProxPostingsArray)
   374  	} else {
   375  		w.freqProxPostingsArray = nil
   376  	}
   377  }
   378  
   379  func (w *FreqProxTermsWriterPerField) createPostingsArray(size int) *ParallelPostingsArray {
   380  	indexOptions := w.fieldInfo.IndexOptions()
   381  	assert(indexOptions != 0)
   382  	hasFreq := indexOptions >= INDEX_OPT_DOCS_AND_FREQS
   383  	hasProx := indexOptions >= INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS
   384  	hasOffsets := indexOptions >= INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS
   385  	return newFreqProxPostingsArray(size, hasFreq, hasProx, hasOffsets)
   386  }
   387  
   388  type FreqProxPostingsArray struct {
   389  	*ParallelPostingsArray
   390  	termFreqs     []int // # times this term occurs in the current doc
   391  	lastDocIDs    []int // Last docID where this term occurred
   392  	lastDocCodes  []int // Code for prior doc
   393  	lastPositions []int //Last position where this term occurred
   394  	lastOffsets   []int // Last endOffsets where this term occurred
   395  }
   396  
   397  func newFreqProxPostingsArray(size int, writeFreqs, writeProx, writeOffsets bool) *ParallelPostingsArray {
   398  	ans := new(FreqProxPostingsArray)
   399  	ans.ParallelPostingsArray = newParallelPostingsArray(ans, size)
   400  	if writeFreqs {
   401  		ans.termFreqs = make([]int, size)
   402  	}
   403  	ans.lastDocIDs = make([]int, size)
   404  	ans.lastDocCodes = make([]int, size)
   405  	if writeProx {
   406  		ans.lastPositions = make([]int, size)
   407  		if writeOffsets {
   408  			ans.lastOffsets = make([]int, size)
   409  		}
   410  	} else {
   411  		assert(!writeOffsets)
   412  	}
   413  	// fmt.Printf("PA init freqs=%v pos=%v offs=%v\n", writeFreqs, writeProx, writeOffsets)
   414  	return ans.ParallelPostingsArray
   415  }
   416  
   417  func (arr *FreqProxPostingsArray) newInstance(size int) PostingsArray {
   418  	return newFreqProxPostingsArray(size, arr.termFreqs != nil,
   419  		arr.lastPositions != nil, arr.lastOffsets != nil)
   420  }
   421  
   422  func (arr *FreqProxPostingsArray) copyTo(toArray PostingsArray, numToCopy int) {
   423  	to, ok := toArray.(*ParallelPostingsArray).PostingsArray.(*FreqProxPostingsArray)
   424  	assert(ok)
   425  
   426  	arr.ParallelPostingsArray.copyTo(toArray, numToCopy)
   427  
   428  	copy(to.lastDocIDs[:numToCopy], arr.lastDocIDs[:numToCopy])
   429  	copy(to.lastDocCodes[:numToCopy], arr.lastDocCodes[:numToCopy])
   430  	if arr.lastPositions != nil {
   431  		assert(to.lastPositions != nil)
   432  		copy(to.lastPositions[:numToCopy], arr.lastPositions[:numToCopy])
   433  	}
   434  	if arr.lastOffsets != nil {
   435  		assert(to.lastOffsets != nil)
   436  		copy(to.lastOffsets[:numToCopy], arr.lastOffsets[:numToCopy])
   437  	}
   438  	if arr.termFreqs != nil {
   439  		assert(to.termFreqs != nil)
   440  		copy(to.termFreqs[:numToCopy], arr.termFreqs[:numToCopy])
   441  	}
   442  }
   443  
   444  func (arr *FreqProxPostingsArray) bytesPerPosting() int {
   445  	bytes := BYTES_PER_POSTING + 2*util.NUM_BYTES_INT
   446  	if arr.lastPositions != nil {
   447  		bytes += util.NUM_BYTES_INT
   448  	}
   449  	if arr.lastOffsets != nil {
   450  		bytes += util.NUM_BYTES_INT
   451  	}
   452  	if arr.termFreqs != nil {
   453  		bytes += util.NUM_BYTES_INT
   454  	}
   455  	return bytes
   456  }
   457  
   458  /*
   459  Walk through all unique text tokens (Posting instances) found in this
   460  field and serialie them into a single RAM segment.
   461  */
   462  func (w *FreqProxTermsWriterPerField) flush(fieldName string,
   463  	consumer FieldsConsumer, state *SegmentWriteState) error {
   464  	if !w.fieldInfo.IsIndexed() {
   465  		return nil // nothing to flush, don't bother the codc with the unindexed field
   466  	}
   467  
   468  	termsConsumer, err := consumer.AddField(w.fieldInfo)
   469  	if err != nil {
   470  		return err
   471  	}
   472  	termComp := termsConsumer.Comparator()
   473  
   474  	// CONFUSING: this.indexOptions holds the index options that were
   475  	// current when we first saw this field. But it's posible this has
   476  	// changed, e.g. when other documents are indexed that cause a
   477  	// "downgrade" of the IndexOptions. So we must decode the in-RAM
   478  	// buffer according to this.indexOptions, but then write the new
   479  	// segment to the directory according to currentFieldIndexOptions:
   480  	currentFieldIndexOptions := w.fieldInfo.IndexOptions()
   481  	assert(int(currentFieldIndexOptions) != 0)
   482  
   483  	writeTermFreq := int(currentFieldIndexOptions) >= int(INDEX_OPT_DOCS_AND_FREQS)
   484  	writePositions := int(currentFieldIndexOptions) >= int(INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS)
   485  	writeOffsets := int(currentFieldIndexOptions) >= int(INDEX_OPT_DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS)
   486  
   487  	readTermFreq := w.hasFreq
   488  	readPositions := w.hasProx
   489  	readOffsets := w.hasOffsets
   490  
   491  	// fmt.Printf("flush readTF=%v readPos=%v readOffs=%v\n",
   492  	// 	readTermFreq, readPositions, readOffsets)
   493  
   494  	// Make sure FieldInfo.update is working correctly
   495  	assert(!writeTermFreq || readTermFreq)
   496  	assert(!writePositions || readPositions)
   497  	assert(!writeOffsets || readOffsets)
   498  
   499  	assert(!writeOffsets || writePositions)
   500  
   501  	var segUpdates map[*Term]int
   502  	if state.SegUpdates != nil && len(state.SegUpdates.(*BufferedUpdates).terms) > 0 {
   503  		segUpdates = state.SegUpdates.(*BufferedUpdates).terms
   504  	}
   505  
   506  	termIDs := w.sortPostings(termComp)
   507  	numTerms := w.bytesHash.Size()
   508  	text := new(util.BytesRef)
   509  	postings := w.freqProxPostingsArray
   510  	freq := newByteSliceReader()
   511  	prox := newByteSliceReader()
   512  
   513  	visitedDocs := util.NewFixedBitSetOf(state.SegmentInfo.DocCount())
   514  	sumTotalTermFreq := int64(0)
   515  	sumDocFreq := int64(0)
   516  
   517  	protoTerm := NewEmptyTerm(fieldName)
   518  	for i := 0; i < numTerms; i++ {
   519  		termId := termIDs[i]
   520  		// fmt.Printf("term=%v\n", termId)
   521  		// Get BytesRef
   522  		textStart := postings.textStarts[termId]
   523  		w.bytePool.SetBytesRef(text, textStart)
   524  
   525  		w.initReader(freq, termId, 0)
   526  		if readPositions || readOffsets {
   527  			w.initReader(prox, termId, 1)
   528  		}
   529  
   530  		// TODO: really TermsHashPerField shold take over most of this
   531  		// loop, including merge sort of terms from multiple threads and
   532  		// interacting with the TermsConsumer, only calling out to us
   533  		// (passing us the DocConsumer) to handle delivery of docs/positions
   534  
   535  		postingsConsumer, err := termsConsumer.StartTerm(text.ToBytes())
   536  		if err != nil {
   537  			return err
   538  		}
   539  
   540  		delDocLimit := 0
   541  		if segUpdates != nil {
   542  			protoTerm.Bytes = text.ToBytes()
   543  			if docIDUpto, ok := segUpdates[protoTerm]; ok {
   544  				delDocLimit = docIDUpto
   545  			}
   546  		}
   547  
   548  		// Now termStates has numToMerge FieldMergeStates which call
   549  		// share the same term. Now we must interleave the docID streams.
   550  		docFreq := 0
   551  		totalTermFreq := int64(0)
   552  		docId := 0
   553  
   554  		for {
   555  			// fmt.Println("  cycle")
   556  			var termFreq int
   557  			if freq.eof() {
   558  				if postings.lastDocCodes[termId] != -1 {
   559  					// return last doc
   560  					docId = postings.lastDocIDs[termId]
   561  					if readTermFreq {
   562  						termFreq = postings.termFreqs[termId]
   563  					} else {
   564  						termFreq = -1
   565  					}
   566  					postings.lastDocCodes[termId] = -1
   567  				} else {
   568  					// EOF
   569  					break
   570  				}
   571  			} else {
   572  				code, err := freq.ReadVInt()
   573  				if err != nil {
   574  					return err
   575  				}
   576  				if !readTermFreq {
   577  					docId += int(code)
   578  					termFreq = -1
   579  				} else {
   580  					docId += int(uint(code) >> 1)
   581  					if (code & 1) != 0 {
   582  						termFreq = 1
   583  					} else {
   584  						n, err := freq.ReadVInt()
   585  						if err != nil {
   586  							return err
   587  						}
   588  						termFreq = int(n)
   589  					}
   590  				}
   591  
   592  				assert(docId != postings.lastDocIDs[termId])
   593  			}
   594  
   595  			docFreq++
   596  			assert2(docId < state.SegmentInfo.DocCount(),
   597  				"doc=%v maxDoc=%v", docId, state.SegmentInfo.DocCount())
   598  
   599  			// NOTE: we could check here if the docID was deleted, and skip
   600  			// it. However, this is somewhat dangerous because it can yield
   601  			// non-deterministic behavior since we may see the docID before
   602  			// we see the term that caused it to be deleted. This would
   603  			// mean some (but not all) of its postings may make it into the
   604  			// index, which'd alter the docFreq for those terms. We could
   605  			// fix this by doing two passes, i.e. first sweep marks all del
   606  			// docs, and 2nd sweep does the real flush, but I suspect
   607  			// that'd add too much time to flush.
   608  			visitedDocs.Set(docId)
   609  			err := postingsConsumer.StartDoc(docId,
   610  				map[bool]int{true: termFreq, false: -1}[writeTermFreq])
   611  			if err != nil {
   612  				return err
   613  			}
   614  			if docId < delDocLimit {
   615  				panic("not implemented yet")
   616  			}
   617  
   618  			totalTermFreq += int64(termFreq)
   619  
   620  			// Carefully copy over the prox + payload info, changing the
   621  			// format to match Lucene's segment format.
   622  
   623  			if readPositions || readOffsets {
   624  				// we did record positions (& maybe payload) and/or offsets
   625  				position := 0
   626  				// offset := 0
   627  				for j := 0; j < termFreq; j++ {
   628  					var thisPayload []byte
   629  
   630  					if readPositions {
   631  						code, err := prox.ReadVInt()
   632  						if err != nil {
   633  							return err
   634  						}
   635  						position += int(uint(code) >> 1)
   636  
   637  						if (code & 1) != 0 {
   638  							panic("not implemented yet")
   639  						}
   640  
   641  						if readOffsets {
   642  							panic("not implemented yet")
   643  						} else if writePositions {
   644  							err = postingsConsumer.AddPosition(position, thisPayload, -1, -1)
   645  							if err != nil {
   646  								return err
   647  							}
   648  						}
   649  					}
   650  				}
   651  			}
   652  			err = postingsConsumer.FinishDoc()
   653  			if err != nil {
   654  				return err
   655  			}
   656  		}
   657  		err = termsConsumer.FinishTerm(text.ToBytes(), codec.NewTermStats(docFreq,
   658  			map[bool]int64{true: totalTermFreq, false: -1}[writeTermFreq]))
   659  		if err != nil {
   660  			return err
   661  		}
   662  		sumTotalTermFreq += int64(totalTermFreq)
   663  		sumDocFreq += int64(docFreq)
   664  	}
   665  
   666  	return termsConsumer.Finish(
   667  		map[bool]int64{true: sumTotalTermFreq, false: -1}[writeTermFreq],
   668  		sumDocFreq, visitedDocs.Cardinality())
   669  }