/*
* 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.solr.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import monty.solr.util.MontySolrAbstractTestCase;
import monty.solr.util.MontySolrSetup;
import org.apache.lucene.util.BitSet;
import org.apache.lucene.util.FixedBitSet;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequestBase;
import org.junit.BeforeClass;
import org.junit.Test;
public class TestBitSetQParserPlugin extends MontySolrAbstractTestCase {
@BeforeClass
public static void beforeClass() throws Exception {
makeResourcesVisible(Thread.currentThread().getContextClassLoader(), new String[] {
MontySolrSetup.getMontySolrHome() + "/contrib/adsabs/src/test-files/solr/collection1/conf",
MontySolrSetup.getSolrHome() + "/example/solr/collection1/conf"
});
System.setProperty("solr.allow.unsafe.resourceloading", "true");
schemaString = MontySolrSetup.getMontySolrHome() + "/contrib/adsabs/src/test-files/solr/collection1/conf/"
+ "schema-minimal.xml";
configString = MontySolrSetup.getMontySolrHome() + "/contrib/adsabs/src/test-files/solr/collection1/conf/"
+ "bitset-solrconfig.xml";
initCore(configString, schemaString, "./temp");
}
public void createIndex() {
assertU(adoc("id","1","recid","1", "text", "who"));
assertU(adoc("id","2","recid","2", "text", "is stopword"));
assertU(adoc("id","3","recid","3", "text", "able"));
assertU(adoc("id","4","recid","4", "text", "to stopword"));
assertU(adoc("id","5","recid","5", "text", "exchange"));
assertU(commit("waitSearcher", "true"));
assertU(adoc("id","16","recid","16", "text", "liberty"));
assertU(adoc("id","17","recid","17", "text", "for stopword"));
assertU(adoc("id","18","recid","18", "text", "safety"));
assertU(adoc("id","19","recid","19", "text", "deserves"));
assertU(adoc("id","20","recid","20", "text", "neither"));
assertU(commit("waitSearcher", "true"));
}
@Override
public void setUp() throws Exception {
super.setUp();
createIndex();
}
@Test
public void test() throws IOException, Exception {
BitSetQParserPlugin bqp = new BitSetQParserPlugin();
bqp.init(new NamedList(){});
assertQ(req("q","text:*", "indent","true")
,"//*[@numFound='10']"
);
BitSet data = convert(new int[]{5,16});
byte[] byteData = bqp.toByteArray(data);
String base64string = bqp.encodeBase64(byteData);
byte[] gzipData = bqp.doGZip(byteData);
String gzipBase64string = bqp.encodeBase64(gzipData);
assertEquals(base64string, "BACA");
assertArrayEquals(byteData, bqp.decodeBase64(base64string));
assertTrue(((FixedBitSet) data).equals((FixedBitSet) bqp.fromByteArray(bqp.decodeBase64(base64string))));
assertEquals(gzipBase64string, "H4sIAAAAAAAAAGNhaAAA7vLwFQMAAAA=");
assertArrayEquals(byteData, bqp.unGZip(gzipData));
assertArrayEquals(byteData, bqp.unGZip(bqp.decodeBase64(gzipBase64string)));
assertEquals(data, bqp.fromByteArray(bqp.unGZip(bqp.decodeBase64(gzipBase64string))));
// sending lucene doc-ids (just a test)
SolrQueryRequestBase req = (SolrQueryRequestBase) req("q","text:*",
"fq","{!bitset compression=none}");
List<ContentStream> streams = new ArrayList<ContentStream>(1);
ContentStreamBase cs = new ContentStreamBase.StringStream("id\n5\n16");
cs.setContentType("big-query/csv");
streams.add(cs);
req.setContentStreams(streams);
assertQ(req
,"//*[@numFound='2']",
"//doc/str[@name='id'][.='5']",
"//doc/str[@name='id'][.='16']"
);
// sending lucene doc-ids (these will not be translated)
assertQ(req("q","text:*", "fq",
"{!bitset compression=none} " + bqp.encodeBase64(bqp.toByteArray(convert(new int[]{4,5}))))
,"//*[@numFound='2']",
"//doc/str[@name='id'][.='5']",
"//doc/str[@name='id'][.='16']"
);
assertQ(req("q","text:*", "fq", "{!bitset field=id} " + base64string)
,"//*[@numFound='2']",
"//doc/str[@name='id'][.='5']",
"//doc/str[@name='id'][.='16']"
);
assertQ(req("q","text:*", "fq", "{!bitset compression=none field=id} " + base64string)
,"//*[@numFound='2']",
"//doc/str[@name='id'][.='5']",
"//doc/str[@name='id'][.='16']"
);
assertQ(req("q","text:*", "fq", "{!bitset compression=gzip field=id} " + gzipBase64string)
,"//*[@numFound='2']",
"//doc/str[@name='id'][.='5']",
"//doc/str[@name='id'][.='16']"
);
// this is similar to the previous query, but the field is of type int
// and will use a different cache
assertQ(req("q","text:*", "fq", "{!bitset field=recid} " + base64string)
,"//*[@numFound='2']",
"//doc/str[@name='id'][.='5']",
"//doc/str[@name='id'][.='16']"
);
// and finally non-sensical input
// sending lucene doc-ids (these will not be translated)
assertQ(req("q","text:*", "fq",
"{!bitset compression=none} " + bqp.encodeBase64(bqp.toByteArray(convert(new int[]{40,500}))))
,"//*[@numFound='0']"
);
assertQ(req("q","text:*", "fq",
"{!bitset field=id} " + bqp.encodeBase64(bqp.toByteArray(convert(new int[]{40,500}))))
,"//*[@numFound='0']"
);
}
private BitSet convert(int[] numbers) {
int m = 0;
for (int n: numbers) {
if (n > m)
m = n;
}
// fixedbitset has to be one bit larger; also we need to round up num of bits
int size = ((m + 8)/8) * 8;
BitSet bitSet = new FixedBitSet(size);
for (int i: numbers) {
bitSet.set(i);
}
return bitSet;
}
}