| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.lucene.index; |
| |
| import java.io.IOException; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.concurrent.BrokenBarrierException; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.CyclicBarrier; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.locks.ReentrantLock; |
| |
| import org.apache.lucene.analysis.MockAnalyzer; |
| import org.apache.lucene.document.BinaryDocValuesField; |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.document.Field.Store; |
| import org.apache.lucene.document.NumericDocValuesField; |
| import org.apache.lucene.document.StringField; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.TermQuery; |
| import org.apache.lucene.search.DocIdSetIterator; |
| import org.apache.lucene.search.DocValuesFieldExistsQuery; |
| import org.apache.lucene.search.ScoreDoc; |
| import org.apache.lucene.search.TopDocs; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.util.Bits; |
| import org.apache.lucene.util.BytesRef; |
| import org.apache.lucene.util.IOUtils; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.apache.lucene.util.TestUtil; |
| |
| import com.carrotsearch.randomizedtesting.generators.RandomPicks; |
| |
| |
| public class TestMixedDocValuesUpdates extends LuceneTestCase { |
| |
| public void testManyReopensAndFields() throws Exception { |
| Directory dir = newDirectory(); |
| final Random random = random(); |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random)); |
| LogMergePolicy lmp = newLogMergePolicy(); |
| lmp.setMergeFactor(3); // merge often |
| conf.setMergePolicy(lmp); |
| IndexWriter writer = new IndexWriter(dir, conf); |
| |
| final boolean isNRT = random.nextBoolean(); |
| DirectoryReader reader; |
| if (isNRT) { |
| reader = DirectoryReader.open(writer); |
| } else { |
| writer.commit(); |
| reader = DirectoryReader.open(dir); |
| } |
| |
| final int numFields = random.nextInt(4) + 3; // 3-7 |
| final int numNDVFields = random.nextInt(numFields/2) + 1; // 1-3 |
| final long[] fieldValues = new long[numFields]; |
| for (int i = 0; i < fieldValues.length; i++) { |
| fieldValues[i] = 1; |
| } |
| |
| int numRounds = atLeast(15); |
| int docID = 0; |
| for (int i = 0; i < numRounds; i++) { |
| int numDocs = atLeast(5); |
| // System.out.println("TEST: round=" + i + ", numDocs=" + numDocs); |
| for (int j = 0; j < numDocs; j++) { |
| Document doc = new Document(); |
| doc.add(new StringField("id", "doc-" + docID, Store.NO)); |
| doc.add(new StringField("key", "all", Store.NO)); // update key |
| // add all fields with their current value |
| for (int f = 0; f < fieldValues.length; f++) { |
| if (f < numNDVFields) { |
| doc.add(new NumericDocValuesField("f" + f, fieldValues[f])); |
| } else { |
| doc.add(new BinaryDocValuesField("f" + f, TestBinaryDocValuesUpdates.toBytes(fieldValues[f]))); |
| } |
| } |
| writer.addDocument(doc); |
| ++docID; |
| } |
| |
| int fieldIdx = random.nextInt(fieldValues.length); |
| String updateField = "f" + fieldIdx; |
| if (fieldIdx < numNDVFields) { |
| writer.updateNumericDocValue(new Term("key", "all"), updateField, ++fieldValues[fieldIdx]); |
| } else { |
| writer.updateBinaryDocValue(new Term("key", "all"), updateField, TestBinaryDocValuesUpdates.toBytes(++fieldValues[fieldIdx])); |
| } |
| //System.out.println("TEST: updated field '" + updateField + "' to value " + fieldValues[fieldIdx]); |
| |
| if (random.nextDouble() < 0.2) { |
| int deleteDoc = random.nextInt(docID); // might also delete an already deleted document, ok! |
| writer.deleteDocuments(new Term("id", "doc-" + deleteDoc)); |
| // System.out.println("[" + Thread.currentThread().getName() + "]: deleted document: doc-" + deleteDoc); |
| } |
| |
| // verify reader |
| if (!isNRT) { |
| writer.commit(); |
| } |
| |
| // System.out.println("[" + Thread.currentThread().getName() + "]: reopen reader: " + reader); |
| DirectoryReader newReader = DirectoryReader.openIfChanged(reader); |
| assertNotNull(newReader); |
| reader.close(); |
| reader = newReader; |
| // System.out.println("[" + Thread.currentThread().getName() + "]: reopened reader: " + reader); |
| assertTrue(reader.numDocs() > 0); // we delete at most one document per round |
| for (LeafReaderContext context : reader.leaves()) { |
| LeafReader r = context.reader(); |
| // System.out.println(((SegmentReader) r).getSegmentName()); |
| Bits liveDocs = r.getLiveDocs(); |
| for (int field = 0; field < fieldValues.length; field++) { |
| String f = "f" + field; |
| BinaryDocValues bdv = r.getBinaryDocValues(f); |
| NumericDocValues ndv = r.getNumericDocValues(f); |
| if (field < numNDVFields) { |
| assertNotNull(ndv); |
| assertNull(bdv); |
| } else { |
| assertNull(ndv); |
| assertNotNull(bdv); |
| } |
| int maxDoc = r.maxDoc(); |
| for (int doc = 0; doc < maxDoc; doc++) { |
| if (liveDocs == null || liveDocs.get(doc)) { |
| // System.out.println("doc=" + (doc + context.docBase) + " f='" + f + "' vslue=" + getValue(bdv, doc, scratch)); |
| if (field < numNDVFields) { |
| assertEquals(doc, ndv.advance(doc)); |
| assertEquals("invalid numeric value for doc=" + doc + ", field=" + f + ", reader=" + r, fieldValues[field], ndv.longValue()); |
| } else { |
| assertEquals(doc, bdv.advance(doc)); |
| assertEquals("invalid binary value for doc=" + doc + ", field=" + f + ", reader=" + r, fieldValues[field], TestBinaryDocValuesUpdates.getValue(bdv)); |
| } |
| } |
| } |
| } |
| } |
| // System.out.println(); |
| } |
| |
| writer.close(); |
| IOUtils.close(reader, dir); |
| } |
| |
| public void testStressMultiThreading() throws Exception { |
| final Directory dir = newDirectory(); |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())); |
| final IndexWriter writer = new IndexWriter(dir, conf); |
| |
| // create index |
| final int numFields = TestUtil.nextInt(random(), 2, 4); |
| final int numThreads = TestUtil.nextInt(random(), 3, 6); |
| final int numDocs = atLeast(2000); |
| for (int i = 0; i < numDocs; i++) { |
| Document doc = new Document(); |
| doc.add(new StringField("id", "doc" + i, Store.NO)); |
| double group = random().nextDouble(); |
| String g; |
| if (group < 0.1) g = "g0"; |
| else if (group < 0.5) g = "g1"; |
| else if (group < 0.8) g = "g2"; |
| else g = "g3"; |
| doc.add(new StringField("updKey", g, Store.NO)); |
| for (int j = 0; j < numFields; j++) { |
| long value = random().nextInt(); |
| doc.add(new BinaryDocValuesField("f" + j, TestBinaryDocValuesUpdates.toBytes(value))); |
| doc.add(new NumericDocValuesField("cf" + j, value * 2)); // control, always updated to f * 2 |
| } |
| writer.addDocument(doc); |
| } |
| |
| final CountDownLatch done = new CountDownLatch(numThreads); |
| final AtomicInteger numUpdates = new AtomicInteger(atLeast(100)); |
| |
| // same thread updates a field as well as reopens |
| Thread[] threads = new Thread[numThreads]; |
| for (int i = 0; i < threads.length; i++) { |
| threads[i] = new Thread("UpdateThread-" + i) { |
| @Override |
| public void run() { |
| DirectoryReader reader = null; |
| boolean success = false; |
| try { |
| Random random = random(); |
| while (numUpdates.getAndDecrement() > 0) { |
| double group = random.nextDouble(); |
| Term t; |
| if (group < 0.1) t = new Term("updKey", "g0"); |
| else if (group < 0.5) t = new Term("updKey", "g1"); |
| else if (group < 0.8) t = new Term("updKey", "g2"); |
| else t = new Term("updKey", "g3"); |
| // System.out.println("[" + Thread.currentThread().getName() + "] numUpdates=" + numUpdates + " updateTerm=" + t); |
| int field = random().nextInt(numFields); |
| final String f = "f" + field; |
| final String cf = "cf" + field; |
| long updValue = random.nextInt(); |
| // System.err.println("[" + Thread.currentThread().getName() + "] t=" + t + ", f=" + f + ", updValue=" + updValue); |
| writer.updateDocValues(t, new BinaryDocValuesField(f, TestBinaryDocValuesUpdates.toBytes(updValue)), |
| new NumericDocValuesField(cf, updValue*2)); |
| |
| if (random.nextDouble() < 0.2) { |
| // delete a random document |
| int doc = random.nextInt(numDocs); |
| // System.out.println("[" + Thread.currentThread().getName() + "] deleteDoc=doc" + doc); |
| writer.deleteDocuments(new Term("id", "doc" + doc)); |
| } |
| |
| if (random.nextDouble() < 0.05) { // commit every 20 updates on average |
| // System.out.println("[" + Thread.currentThread().getName() + "] commit"); |
| writer.commit(); |
| } |
| |
| if (random.nextDouble() < 0.1) { // reopen NRT reader (apply updates), on average once every 10 updates |
| if (reader == null) { |
| // System.out.println("[" + Thread.currentThread().getName() + "] open NRT"); |
| reader = DirectoryReader.open(writer); |
| } else { |
| // System.out.println("[" + Thread.currentThread().getName() + "] reopen NRT"); |
| DirectoryReader r2 = DirectoryReader.openIfChanged(reader, writer); |
| if (r2 != null) { |
| reader.close(); |
| reader = r2; |
| } |
| } |
| } |
| } |
| // System.out.println("[" + Thread.currentThread().getName() + "] DONE"); |
| success = true; |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } finally { |
| if (reader != null) { |
| try { |
| reader.close(); |
| } catch (IOException e) { |
| if (success) { // suppress this exception only if there was another exception |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| done.countDown(); |
| } |
| } |
| }; |
| } |
| |
| for (Thread t : threads) t.start(); |
| done.await(); |
| writer.close(); |
| |
| DirectoryReader reader = DirectoryReader.open(dir); |
| for (LeafReaderContext context : reader.leaves()) { |
| LeafReader r = context.reader(); |
| for (int i = 0; i < numFields; i++) { |
| BinaryDocValues bdv = r.getBinaryDocValues("f" + i); |
| NumericDocValues control = r.getNumericDocValues("cf" + i); |
| Bits liveDocs = r.getLiveDocs(); |
| for (int j = 0; j < r.maxDoc(); j++) { |
| if (liveDocs == null || liveDocs.get(j)) { |
| assertEquals(j, control.advance(j)); |
| long ctrlValue = control.longValue(); |
| assertEquals(j, bdv.advance(j)); |
| long bdvValue = TestBinaryDocValuesUpdates.getValue(bdv) * 2; |
| // if (ctrlValue != bdvValue) { |
| // System.out.println("seg=" + r + ", f=f" + i + ", doc=" + j + ", group=" + r.document(j).get("updKey") + ", ctrlValue=" + ctrlValue + ", bdvBytes=" + scratch); |
| // } |
| assertEquals(ctrlValue, bdvValue); |
| } |
| } |
| } |
| } |
| reader.close(); |
| |
| dir.close(); |
| } |
| |
| public void testUpdateDifferentDocsInDifferentGens() throws Exception { |
| // update same document multiple times across generations |
| Directory dir = newDirectory(); |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())); |
| conf.setMaxBufferedDocs(4); |
| IndexWriter writer = new IndexWriter(dir, conf); |
| final int numDocs = atLeast(10); |
| for (int i = 0; i < numDocs; i++) { |
| Document doc = new Document(); |
| doc.add(new StringField("id", "doc" + i, Store.NO)); |
| long value = random().nextInt(); |
| doc.add(new BinaryDocValuesField("f", TestBinaryDocValuesUpdates.toBytes(value))); |
| doc.add(new NumericDocValuesField("cf", value * 2)); |
| writer.addDocument(doc); |
| } |
| |
| int numGens = atLeast(5); |
| for (int i = 0; i < numGens; i++) { |
| int doc = random().nextInt(numDocs); |
| Term t = new Term("id", "doc" + doc); |
| long value = random().nextLong(); |
| if (random().nextBoolean()) { |
| doUpdate(t, writer, new BinaryDocValuesField("f", TestBinaryDocValuesUpdates.toBytes(value)), |
| new NumericDocValuesField("cf", value*2)); |
| } else { |
| writer.updateDocValues(t, new BinaryDocValuesField("f", TestBinaryDocValuesUpdates.toBytes(value)), |
| new NumericDocValuesField("cf", value*2)); |
| } |
| |
| DirectoryReader reader = DirectoryReader.open(writer); |
| for (LeafReaderContext context : reader.leaves()) { |
| LeafReader r = context.reader(); |
| BinaryDocValues fbdv = r.getBinaryDocValues("f"); |
| NumericDocValues cfndv = r.getNumericDocValues("cf"); |
| for (int j = 0; j < r.maxDoc(); j++) { |
| assertEquals(j, cfndv.nextDoc()); |
| assertEquals(j, fbdv.nextDoc()); |
| assertEquals(cfndv.longValue(), TestBinaryDocValuesUpdates.getValue(fbdv) * 2); |
| } |
| } |
| reader.close(); |
| } |
| writer.close(); |
| dir.close(); |
| } |
| |
| @Nightly |
| public void testTonsOfUpdates() throws Exception { |
| // LUCENE-5248: make sure that when there are many updates, we don't use too much RAM |
| Directory dir = newDirectory(); |
| final Random random = random(); |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random)); |
| conf.setRAMBufferSizeMB(IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB); |
| conf.setMaxBufferedDocs(IndexWriterConfig.DISABLE_AUTO_FLUSH); // don't flush by doc |
| IndexWriter writer = new IndexWriter(dir, conf); |
| |
| // test data: lots of documents (few 10Ks) and lots of update terms (few hundreds) |
| final int numDocs = atLeast(20000); |
| final int numBinaryFields = atLeast(5); |
| final int numTerms = TestUtil.nextInt(random, 10, 100); // terms should affect many docs |
| Set<String> updateTerms = new HashSet<>(); |
| while (updateTerms.size() < numTerms) { |
| updateTerms.add(TestUtil.randomSimpleString(random)); |
| } |
| |
| // System.out.println("numDocs=" + numDocs + " numBinaryFields=" + numBinaryFields + " numTerms=" + numTerms); |
| |
| // build a large index with many BDV fields and update terms |
| for (int i = 0; i < numDocs; i++) { |
| Document doc = new Document(); |
| int numUpdateTerms = TestUtil.nextInt(random, 1, numTerms / 10); |
| for (int j = 0; j < numUpdateTerms; j++) { |
| doc.add(new StringField("upd", RandomPicks.randomFrom(random, updateTerms), Store.NO)); |
| } |
| for (int j = 0; j < numBinaryFields; j++) { |
| long val = random.nextInt(); |
| doc.add(new BinaryDocValuesField("f" + j, TestBinaryDocValuesUpdates.toBytes(val))); |
| doc.add(new NumericDocValuesField("cf" + j, val * 2)); |
| } |
| writer.addDocument(doc); |
| } |
| |
| writer.commit(); // commit so there's something to apply to |
| |
| // set to flush every 2048 bytes (approximately every 12 updates), so we get |
| // many flushes during binary updates |
| writer.getConfig().setRAMBufferSizeMB(2048.0 / 1024 / 1024); |
| final int numUpdates = atLeast(100); |
| // System.out.println("numUpdates=" + numUpdates); |
| for (int i = 0; i < numUpdates; i++) { |
| int field = random.nextInt(numBinaryFields); |
| Term updateTerm = new Term("upd", RandomPicks.randomFrom(random, updateTerms)); |
| long value = random.nextInt(); |
| writer.updateDocValues(updateTerm, new BinaryDocValuesField("f"+field, TestBinaryDocValuesUpdates.toBytes(value)), |
| new NumericDocValuesField("cf"+field, value*2)); |
| } |
| |
| writer.close(); |
| |
| DirectoryReader reader = DirectoryReader.open(dir); |
| for (LeafReaderContext context : reader.leaves()) { |
| for (int i = 0; i < numBinaryFields; i++) { |
| LeafReader r = context.reader(); |
| BinaryDocValues f = r.getBinaryDocValues("f" + i); |
| NumericDocValues cf = r.getNumericDocValues("cf" + i); |
| for (int j = 0; j < r.maxDoc(); j++) { |
| assertEquals(j, cf.nextDoc()); |
| assertEquals(j, f.nextDoc()); |
| assertEquals("reader=" + r + ", field=f" + i + ", doc=" + j, cf.longValue(), TestBinaryDocValuesUpdates.getValue(f) * 2); |
| } |
| } |
| } |
| reader.close(); |
| |
| dir.close(); |
| } |
| |
| public void testTryUpdateDocValues() throws IOException { |
| Directory dir = newDirectory(); |
| IndexWriterConfig conf = newIndexWriterConfig(); |
| IndexWriter writer = new IndexWriter(dir, conf); |
| int numDocs = 1 + random().nextInt(128); |
| for (int i = 0; i < numDocs; i++) { |
| Document doc = new Document(); |
| doc.add(new StringField("id", "" + i, Store.YES)); |
| doc.add(new NumericDocValuesField("id", i)); |
| doc.add(new BinaryDocValuesField("binaryId", new BytesRef(new byte[] {(byte)i}))); |
| writer.addDocument(doc); |
| if (random().nextBoolean()) { |
| writer.flush(); |
| } |
| } |
| int doc = random().nextInt(numDocs); |
| doUpdate(new Term("id", "" + doc), writer, new NumericDocValuesField("id", doc + 1), |
| new BinaryDocValuesField("binaryId", new BytesRef(new byte[]{(byte) (doc + 1)}))); |
| IndexReader reader = writer.getReader(); |
| NumericDocValues idValues = null; |
| BinaryDocValues binaryIdValues = null; |
| for (LeafReaderContext c : reader.leaves()) { |
| TopDocs topDocs = new IndexSearcher(c.reader()).search(new TermQuery(new Term("id", "" + doc)), 10); |
| if (topDocs.totalHits.value == 1) { |
| assertNull(idValues); |
| assertNull(binaryIdValues); |
| idValues = c.reader().getNumericDocValues("id"); |
| assertEquals(topDocs.scoreDocs[0].doc, idValues.advance(topDocs.scoreDocs[0].doc)); |
| binaryIdValues = c.reader().getBinaryDocValues("binaryId"); |
| assertEquals(topDocs.scoreDocs[0].doc, binaryIdValues.advance(topDocs.scoreDocs[0].doc)); |
| } else { |
| assertEquals(0, topDocs.totalHits.value); |
| } |
| } |
| |
| assertNotNull(idValues); |
| assertNotNull(binaryIdValues); |
| |
| assertEquals(doc+1, idValues.longValue()); |
| assertEquals(new BytesRef(new byte[] {(byte)(doc+1)}), binaryIdValues.binaryValue()); |
| IOUtils.close(reader, writer, dir); |
| } |
| |
| public void testTryUpdateMultiThreaded() throws IOException, BrokenBarrierException, InterruptedException { |
| Directory dir = newDirectory(); |
| IndexWriterConfig conf = newIndexWriterConfig(); |
| IndexWriter writer = new IndexWriter(dir, conf); |
| ReentrantLock[] locks = new ReentrantLock[25 + random().nextInt(50)]; |
| Long[] values = new Long[locks.length]; |
| |
| for (int i = 0; i < locks.length; i++) { |
| locks[i] = new ReentrantLock(); |
| Document doc = new Document(); |
| values[i] = random().nextLong(); |
| doc.add(new StringField("id", Integer.toString(i), Store.NO)); |
| doc.add(new NumericDocValuesField("value", values[i])); |
| writer.addDocument(doc); |
| } |
| |
| int numThreads = TEST_NIGHTLY ? 2 + random().nextInt(3) : 2; |
| Thread[] threads = new Thread[numThreads]; |
| CyclicBarrier barrier = new CyclicBarrier(threads.length + 1); |
| for (int i = 0; i < threads.length; i++) { |
| threads[i] = new Thread(() -> { |
| try { |
| barrier.await(); |
| for (int doc = 0; doc < 1000; doc++) { |
| int docId = random().nextInt(locks.length); |
| locks[docId].lock(); |
| try { |
| Long value = rarely() ? null : random().nextLong(); // sometimes reset it |
| if (random().nextBoolean()) { |
| writer.updateDocValues(new Term("id", docId + ""), new NumericDocValuesField("value", value)); |
| } else { |
| doUpdate(new Term("id", docId + ""), writer, new NumericDocValuesField("value", value)); |
| } |
| values[docId] = value; |
| } catch (IOException e) { |
| throw new AssertionError(e); |
| } finally { |
| locks[docId].unlock(); |
| } |
| if (rarely()) { |
| writer.flush(); |
| } |
| } |
| } catch (Exception e) { |
| throw new AssertionError(e); |
| } |
| }); |
| threads[i].start(); |
| } |
| |
| barrier.await(); |
| for (Thread t : threads) { |
| t.join(); |
| } |
| try (DirectoryReader reader = writer.getReader()) { |
| for (int i = 0; i < locks.length; i++) { |
| locks[i].lock(); |
| try { |
| Long value = values[i]; |
| TopDocs topDocs = new IndexSearcher(reader).search(new TermQuery(new Term("id", "" + i)), 10); |
| assertEquals(topDocs.totalHits.value, 1); |
| int docID = topDocs.scoreDocs[0].doc; |
| List<LeafReaderContext> leaves = reader.leaves(); |
| int subIndex = ReaderUtil.subIndex(docID, leaves); |
| LeafReader leafReader = leaves.get(subIndex).reader(); |
| docID -= leaves.get(subIndex).docBase; |
| NumericDocValues numericDocValues = leafReader.getNumericDocValues("value"); |
| if (value == null) { |
| assertFalse("docID: " + docID, numericDocValues.advanceExact(docID)); |
| } else { |
| assertTrue("docID: " + docID, numericDocValues.advanceExact(docID)); |
| assertEquals(numericDocValues.longValue(), value.longValue()); |
| } |
| } finally { |
| locks[i].unlock(); |
| } |
| } |
| } |
| |
| IOUtils.close(writer, dir); |
| } |
| |
| static void doUpdate(Term doc, IndexWriter writer, Field... fields) throws IOException { |
| long seqId = -1; |
| do { // retry if we just committing a merge |
| try (DirectoryReader reader = writer.getReader()) { |
| TopDocs topDocs = new IndexSearcher(reader).search(new TermQuery(doc), 10); |
| assertEquals(1, topDocs.totalHits.value); |
| int theDoc = topDocs.scoreDocs[0].doc; |
| seqId = writer.tryUpdateDocValue(reader, theDoc, fields); |
| } |
| } while (seqId == -1); |
| } |
| |
| public void testResetValue() throws Exception { |
| Directory dir = newDirectory(); |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())); |
| IndexWriter writer = new IndexWriter(dir, conf); |
| Document doc = new Document(); |
| doc.add(new StringField("id", "0", Store.NO)); |
| doc.add(new NumericDocValuesField("val", 5)); |
| doc.add(new BinaryDocValuesField("val-bin", new BytesRef(new byte[] {(byte)5}))); |
| writer.addDocument(doc); |
| |
| if (random().nextBoolean()) { |
| writer.commit(); |
| } |
| try(DirectoryReader reader = writer.getReader()) { |
| assertEquals(1, reader.leaves().size()); |
| LeafReader r = reader.leaves().get(0).reader(); |
| NumericDocValues ndv = r.getNumericDocValues("val"); |
| assertEquals(0, ndv.nextDoc()); |
| assertEquals(5, ndv.longValue()); |
| assertEquals(DocIdSetIterator.NO_MORE_DOCS, ndv.nextDoc()); |
| |
| BinaryDocValues bdv = r.getBinaryDocValues("val-bin"); |
| assertEquals(0, bdv.nextDoc()); |
| assertEquals(new BytesRef(new byte[]{(byte) 5}), bdv.binaryValue()); |
| assertEquals(DocIdSetIterator.NO_MORE_DOCS, bdv.nextDoc()); |
| } |
| |
| writer.updateDocValues(new Term("id", "0"), new BinaryDocValuesField("val-bin", null)); |
| try(DirectoryReader reader = writer.getReader()) { |
| assertEquals(1, reader.leaves().size()); |
| LeafReader r = reader.leaves().get(0).reader(); |
| NumericDocValues ndv = r.getNumericDocValues("val"); |
| assertEquals(0, ndv.nextDoc()); |
| assertEquals(5, ndv.longValue()); |
| assertEquals(DocIdSetIterator.NO_MORE_DOCS, ndv.nextDoc()); |
| |
| BinaryDocValues bdv = r.getBinaryDocValues("val-bin"); |
| assertEquals(DocIdSetIterator.NO_MORE_DOCS, bdv.nextDoc()); |
| } |
| IOUtils.close(writer, dir); |
| } |
| |
| public void testResetValueMultipleDocs() throws Exception { |
| Directory dir = newDirectory(); |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())); |
| IndexWriter writer = new IndexWriter(dir, conf); |
| int numDocs = 10 + random().nextInt(50); |
| int currentSeqId = 0; |
| int[] seqId = new int[] {-1, -1, -1, -1, -1}; |
| for (int i = 0; i < numDocs; i++) { |
| Document doc = new Document(); |
| int id = random().nextInt(5); |
| seqId[id] = currentSeqId; |
| doc.add(new StringField("id", "" + id, Store.YES)); |
| doc.add(new NumericDocValuesField("seqID", currentSeqId++)); |
| doc.add(new NumericDocValuesField("is_live", 1)); |
| if (i > 0) { |
| writer.updateDocValues(new Term("id", "" + id), new NumericDocValuesField("is_live", null)); |
| } |
| writer.addDocument(doc); |
| if (random().nextBoolean()) { |
| writer.flush(); |
| } |
| } |
| |
| if (random().nextBoolean()) { |
| writer.commit(); |
| } |
| int numHits = 0; // check if every doc has been selected at least once |
| for (int i : seqId) { |
| if (i > -1) { |
| numHits++; |
| } |
| } |
| try(DirectoryReader reader = writer.getReader()) { |
| IndexSearcher searcher = new IndexSearcher(reader); |
| |
| TopDocs is_live = searcher.search(new DocValuesFieldExistsQuery("is_live"), 5); |
| assertEquals(numHits, is_live.totalHits.value); |
| for (ScoreDoc doc : is_live.scoreDocs) { |
| int id = Integer.parseInt(reader.document(doc.doc).get("id")); |
| int i = ReaderUtil.subIndex(doc.doc, reader.leaves()); |
| assertTrue(i >= 0); |
| LeafReaderContext leafReaderContext = reader.leaves().get(i); |
| NumericDocValues seqID = leafReaderContext.reader().getNumericDocValues("seqID"); |
| assertNotNull(seqID); |
| assertTrue(seqID.advanceExact(doc.doc - leafReaderContext.docBase)); |
| assertEquals(seqId[id], seqID.longValue()); |
| } |
| } |
| IOUtils.close(writer, dir); |
| } |
| |
| public void testUpdateNotExistingFieldDV() throws IOException { |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())); |
| try (Directory dir = newDirectory(); IndexWriter writer = new IndexWriter(dir, conf)) { |
| Document doc = new Document(); |
| doc.add(new StringField("id", "1", Store.YES)); |
| doc.add(new NumericDocValuesField("test", 1)); |
| writer.addDocument(doc); |
| if (random().nextBoolean()) { |
| writer.commit(); |
| } |
| writer.updateDocValues(new Term("id", "1"), new NumericDocValuesField("not_existing", 1)); |
| |
| Document doc1 = new Document(); |
| doc1.add(new StringField("id", "2", Store.YES)); |
| doc1.add(new BinaryDocValuesField("not_existing", new BytesRef())); |
| IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> |
| writer.addDocument(doc1) |
| ); |
| assertEquals("cannot change DocValues type from NUMERIC to BINARY for field \"not_existing\"", iae.getMessage()); |
| |
| iae = expectThrows(IllegalArgumentException.class, () -> |
| writer.updateDocValues(new Term("id", "1"), new BinaryDocValuesField("not_existing", new BytesRef())) |
| ); |
| assertEquals("cannot change DocValues type from NUMERIC to BINARY for field \"not_existing\"", iae.getMessage()); |
| } |
| } |
| |
| public void testUpdateFieldWithNoPreviousDocValues() throws IOException { |
| IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random())); |
| try (Directory dir = newDirectory(); IndexWriter writer = new IndexWriter(dir, conf)) { |
| Document doc = new Document(); |
| doc.add(new StringField("id", "1", Store.YES)); |
| writer.addDocument(doc); |
| if (random().nextBoolean()) { |
| try (DirectoryReader reader = writer.getReader()) { |
| NumericDocValues id = reader.leaves().get(0).reader().getNumericDocValues("id"); |
| assertNull(id); |
| } |
| } else if (random().nextBoolean()) { |
| writer.commit(); |
| } |
| writer.updateDocValues(new Term("id", "1"), new NumericDocValuesField("id", 1)); |
| try (DirectoryReader reader = writer.getReader()) { |
| NumericDocValues id = reader.leaves().get(0).reader().getNumericDocValues("id"); |
| assertNotNull(id); |
| assertTrue(id.advanceExact(0)); |
| assertEquals(1, id.longValue()); |
| } |
| } |
| } |
| } |
| |