Index: lucene/src/test/org/apache/lucene/search/TestNoFieldValueFilter.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestNoFieldValueFilter.java (revision 0) +++ lucene/src/test/org/apache/lucene/search/TestNoFieldValueFilter.java (revision 0) @@ -0,0 +1,103 @@ +package org.apache.lucene.search; + +/** + * 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. + */ +import java.io.IOException; + +import org.apache.lucene.analysis.MockAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.TextField; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.LuceneTestCase; + +/** + * + */ +public class TestNoFieldValueFilter extends LuceneTestCase { + + public void testNoFieldValueFilter() throws IOException { + Directory directory = newDirectory(); + RandomIndexWriter writer = new RandomIndexWriter(random, directory, + newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)) + .setMergePolicy(newLogMergePolicy())); + int docs = atLeast(10); + int docsWithValue = 0; + for (int i = 0; i < docs; i++) { + Document doc = new Document(); + if (random.nextBoolean()) { + docsWithValue++; + doc.add(newField("some", "value", TextField.TYPE_STORED)); + } + doc.add(newField("all", "test", TextField.TYPE_UNSTORED)); + writer.addDocument(doc); + } + writer.close(); + + IndexReader reader = IndexReader.open(directory); + IndexSearcher searcher = new IndexSearcher(reader); + TopDocs search = searcher.search(new TermQuery(new Term("all", "test")), + new NoFieldValueFilter("some", false), docs); + assertEquals(search.totalHits, docs - docsWithValue); + + ScoreDoc[] scoreDocs = search.scoreDocs; + for (ScoreDoc scoreDoc : scoreDocs) { + assertNull(reader.document(scoreDoc.doc).get("some")); + } + + reader.close(); + searcher.close(); + directory.close(); + } + + public void testNoFieldValueFilterInverted() throws IOException { + Directory directory = newDirectory(); + RandomIndexWriter writer = new RandomIndexWriter(random, directory, + newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random)) + .setMergePolicy(newLogMergePolicy())); + int docs = atLeast(10); + int docsWithValue = 0; + for (int i = 0; i < docs; i++) { + Document doc = new Document(); + if (random.nextBoolean()) { + docsWithValue++; + doc.add(newField("some", "value", TextField.TYPE_STORED)); + } + doc.add(newField("all", "test", TextField.TYPE_UNSTORED)); + writer.addDocument(doc); + } + writer.close(); + + IndexReader reader = IndexReader.open(directory); + IndexSearcher searcher = new IndexSearcher(reader); + TopDocs search = searcher.search(new TermQuery(new Term("all", "test")), + new NoFieldValueFilter("some", true), docs); + assertEquals(search.totalHits, docsWithValue); + + ScoreDoc[] scoreDocs = search.scoreDocs; + for (ScoreDoc scoreDoc : scoreDocs) { + assertEquals("value", reader.document(scoreDoc.doc).get("some")); + } + + reader.close(); + searcher.close(); + directory.close(); + } + +} Property changes on: lucene/src/test/org/apache/lucene/search/TestNoFieldValueFilter.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: lucene/src/java/org/apache/lucene/search/NoFieldValueFilter.java =================================================================== --- lucene/src/java/org/apache/lucene/search/NoFieldValueFilter.java (revision 0) +++ lucene/src/java/org/apache/lucene/search/NoFieldValueFilter.java (revision 0) @@ -0,0 +1,98 @@ +package org.apache.lucene.search; +/** + * 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. + */ +import java.io.IOException; + +import org.apache.lucene.index.IndexReader.AtomicReaderContext; +import org.apache.lucene.search.FieldCacheRangeFilter.FieldCacheDocIdSet; +import org.apache.lucene.util.Bits; + +/** + * A {@link Filter} that accepts all documents that don't have a value + * in a given field. This {@link Filter} request {@link Bits} from the + * {@link FieldCache} and build the bits if not present. + */ +public class NoFieldValueFilter extends Filter { + private final String field; + private final boolean negate; + + /** + * Creates a new {@link NoFieldValueFilter} + * @param field the field to filter + * @param negate TODO javadoc + */ + public NoFieldValueFilter(String field, boolean negate) { + this.field = field; + this.negate = negate; // nocommit - is there a better way to do that? + } + + @Override + public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) + throws IOException { + final Bits docsWithField = FieldCache.DEFAULT.getDocsWithField( + context.reader, field); + if (negate) { + return new FieldCacheDocIdSet(context.reader.maxDoc(), acceptDocs) { + @Override + final boolean matchDoc(int doc) { + return docsWithField.get(doc); + } + }; + } else { + return new FieldCacheDocIdSet(context.reader.maxDoc(), acceptDocs) { + @Override + final boolean matchDoc(int doc) { + return !docsWithField.get(doc); + } + }; + } + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((field == null) ? 0 : field.hashCode()); + result = prime * result + (negate ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NoFieldValueFilter other = (NoFieldValueFilter) obj; + if (field == null) { + if (other.field != null) + return false; + } else if (!field.equals(other.field)) + return false; + if (negate != other.negate) + return false; + return true; + } + + @Override + public String toString() { + return "NoFieldValueFilter [field=" + field + ", negate=" + negate + "]"; + } + +} Property changes on: lucene/src/java/org/apache/lucene/search/NoFieldValueFilter.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native