package org.apache.solr.schema; /* * 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 org.apache.lucene.index.IndexableField; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.core.SolrCore; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import java.util.Random; import java.util.Set; /** * Tests currency field type. */ public class CurrencyFieldTest extends SolrTestCaseJ4 { @BeforeClass public static void beforeClass() throws Exception { initCore("solrconfig.xml", "schema.xml"); } @Test public void testCurrencySchema() throws Exception { IndexSchema schema = h.getCore().getSchema(); SchemaField amount = schema.getField("amount"); assertNotNull(amount); assertTrue(amount.isPolyField()); SchemaField[] dynFields = schema.getDynamicFieldPrototypes(); boolean seenCurrency = false; boolean seenAmount = false; for (SchemaField dynField : dynFields) { if (dynField.getName().equals("*" + FieldType.POLY_FIELD_SEPARATOR + CurrencyField.FIELD_SUFFIX_CURRENCY)) { seenCurrency = true; } if (dynField.getName().equals("*" + FieldType.POLY_FIELD_SEPARATOR + CurrencyField.FIELD_SUFFIX_AMOUNT_RAW)) { seenAmount = true; } } assertTrue("Didn't find the expected currency code dynamic field", seenCurrency); assertTrue("Didn't find the expected value dynamic field", seenAmount); } @Test public void testCurrencyFieldType() throws Exception { SolrCore core = h.getCore(); IndexSchema schema = core.getSchema(); SchemaField amount = schema.getField("amount"); assertNotNull(amount); assertTrue("amount is not a poly field", amount.isPolyField()); FieldType tmp = amount.getType(); assertTrue(tmp instanceof CurrencyField); String currencyValue = "1.50,EUR"; IndexableField[] fields = amount.createFields(currencyValue, 2); assertEquals(fields.length, 3); // First field is currency code, second is value, third is stored. for (int i = 0; i < 3; i++) { boolean hasValue = fields[i].readerValue() != null || fields[i].numericValue() != null || fields[i].stringValue() != null; assertTrue("Doesn't have a value: " + fields[i], hasValue); } assertEquals(schema.getFieldTypeByName("string").toExternal(fields[2]), "1.50,EUR"); // A few tests on the provider directly ExchangeRateProvider p = ((CurrencyField) tmp).getProvider(); Set<String> availableCurrencies = p.listAvailableCurrencies(); assert(availableCurrencies.size() == 4); assert(p.reload() == true); assert(p.getExchangeRate("USD", "EUR") == 2.5); } @Test public void testMockExchangeRateProvider() throws Exception { SolrCore core = h.getCore(); IndexSchema schema = core.getSchema(); SchemaField amount = schema.getField("mock_amount"); // A few tests on the provider directly ExchangeRateProvider p = ((CurrencyField)amount.getType()).getProvider(); Set<String> availableCurrencies = p.listAvailableCurrencies(); assert(availableCurrencies.size() == 3); assert(p.reload() == true); assert(p.getExchangeRate("USD", "EUR") == 0.8); } @Test public void testCurrencyRangeSearch() throws Exception { for (int i = 1; i <= 10; i++) { assertU(adoc("id", "" + i, "amount", i + ",USD")); } assertU(commit()); assertQ(req("fl", "*,score", "q", "amount:[2.00,USD TO 5.00,USD]"), "//*[@numFound='4']"); assertQ(req("fl", "*,score", "q", "amount:[0.50,USD TO 1.00,USD]"), "//*[@numFound='1']"); assertQ(req("fl", "*,score", "q", "amount:[24.00,USD TO 25.00,USD]"), "//*[@numFound='0']"); // "GBP" currency code is 1/2 of a USD dollar, for testing. assertQ(req("fl", "*,score", "q", "amount:[0.50,GBP TO 1.00,GBP]"), "//*[@numFound='2']"); // "EUR" currency code is 2.5X of a USD dollar, for testing. assertQ(req("fl", "*,score", "q", "amount:[24.00,EUR TO 25.00,EUR]"), "//*[@numFound='1']"); // Slight asymmetric rate should work. assertQ(req("fl", "*,score", "q", "amount:[24.99,EUR TO 25.01,EUR]"), "//*[@numFound='1']"); // Open ended ranges without currency assertQ(req("fl", "*,score", "q", "amount:[* TO *]"), "//*[@numFound='10']"); // Open ended ranges with currency assertQ(req("fl", "*,score", "q", "amount:[*,EUR TO *,EUR]"), "//*[@numFound='10']"); // Open ended start range without currency assertQ(req("fl", "*,score", "q", "amount:[* TO 5,USD]"), "//*[@numFound='5']"); // Open ended start range with currency (currency for the * won't matter) assertQ(req("fl", "*,score", "q", "amount:[*,USD TO 5,USD]"), "//*[@numFound='5']"); // Open ended end range assertQ(req("fl", "*,score", "q", "amount:[3 TO *]"), "//*[@numFound='8']"); } @Test public void testCurrencyPointQuery() throws Exception { assertU(adoc("id", "" + 1, "amount", "10.00,USD")); assertU(adoc("id", "" + 2, "amount", "15.00,EUR")); assertU(commit()); assertQ(req("fl", "*,score", "q", "amount:10.00,USD"), "//int[@name='id']='1'"); assertQ(req("fl", "*,score", "q", "amount:9.99,USD"), "//*[@numFound='0']"); assertQ(req("fl", "*,score", "q", "amount:10.01,USD"), "//*[@numFound='0']"); assertQ(req("fl", "*,score", "q", "amount:15.00,EUR"), "//int[@name='id']='2'"); assertQ(req("fl", "*,score", "q", "amount:7.50,USD"), "//int[@name='id']='2'"); assertQ(req("fl", "*,score", "q", "amount:7.49,USD"), "//*[@numFound='0']"); assertQ(req("fl", "*,score", "q", "amount:7.51,USD"), "//*[@numFound='0']"); } @Ignore public void testPerformance() throws Exception { Random r = random(); int initDocs = 200000; for (int i = 1; i <= initDocs; i++) { assertU(adoc("id", "" + i, "amount", (r.nextInt(10) + 1.00) + ",USD")); if (i % 1000 == 0) System.out.println(i); } assertU(commit()); for (int i = 0; i < 1000; i++) { double lower = r.nextInt(10) + 1.00; assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",USD TO " + (lower + 10.00) + ",USD]"), "//*"); assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",EUR TO " + (lower + 10.00) + ",EUR]"), "//*"); } for (int j = 0; j < 3; j++) { long t1 = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { double lower = r.nextInt(10) + 1.00; assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",USD TO " + (lower + (9.99 - (j * 0.01))) + ",USD]"), "//*"); } System.out.println(System.currentTimeMillis() - t1); } System.out.println("---"); for (int j = 0; j < 3; j++) { long t1 = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { double lower = r.nextInt(10) + 1.00; assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",EUR TO " + (lower + (9.99 - (j * 0.01))) + ",EUR]"), "//*"); } System.out.println(System.currentTimeMillis() - t1); } } @Test public void testCurrencySort() throws Exception { assertU(adoc("id", "" + 1, "amount", "10.00,USD")); assertU(adoc("id", "" + 2, "amount", "15.00,EUR")); assertU(adoc("id", "" + 3, "amount", "7.00,EUR")); assertU(adoc("id", "" + 4, "amount", "6.00,GBP")); assertU(adoc("id", "" + 5, "amount", "2.00,GBP")); assertU(commit()); assertQ(req("fl", "*,score", "q", "*:*", "sort", "amount desc", "limit", "1"), "//int[@name='id']='4'"); assertQ(req("fl", "*,score", "q", "*:*", "sort", "amount asc", "limit", "1"), "//int[@name='id']='3'"); } @Test public void testMockFieldType() throws Exception { assertU(adoc("id", "1", "mock_amount", "1.00,USD")); assertU(adoc("id", "2", "mock_amount", "1.00,EUR")); assertU(adoc("id", "3", "mock_amount", "1.00,NOK")); assertU(commit()); assertQ(req("fl", "*,score", "q", "mock_amount:5.0,NOK"), "//*[@numFound='1']", "//int[@name='id']='1'"); assertQ(req("fl", "*,score", "q", "mock_amount:1.2,USD"), "//*[@numFound='1']", "//int[@name='id']='2'"); assertQ(req("fl", "*,score", "q", "mock_amount:0.2,USD"), "//*[@numFound='1']", "//int[@name='id']='3'"); assertQ(req("fl", "*,score", "q", "mock_amount:99,USD"), "//*[@numFound='0']"); } }