/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.index.mapper.string;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.test.ESSingleNodeTestCase;
import java.io.IOException;
import java.util.Arrays;
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
import static org.hamcrest.Matchers.containsString;
/**
* Tests that position_increment_gap is read from the mapper and applies as
* expected in queries.
*/
public class StringFieldMapperPositionIncrementGapTests extends ESSingleNodeTestCase {
/**
* The default position_increment_gap should be large enough that most
* "sensible" queries phrase slops won't match across values.
*/
public void testDefault() throws IOException {
assertGapIsOneHundred(client(), "test", "test");
}
/**
* Asserts that the post-2.0 default is being applied.
*/
public static void assertGapIsOneHundred(Client client, String indexName, String type) throws IOException {
testGap(client, indexName, type, 100);
// No match across gap using default slop with default positionIncrementGap
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two")).get(), 0);
// Nor with small-ish values
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two").slop(5)).get(), 0);
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two").slop(50)).get(), 0);
// But huge-ish values still match
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two").slop(500)).get(), 1);
}
public void testZero() throws IOException {
setupGapInMapping(0);
assertGapIsZero(client(), "test", "test");
}
/**
* Asserts that the pre-2.0 default has been applied or explicitly
* configured.
*/
public static void assertGapIsZero(Client client, String indexName, String type) throws IOException {
testGap(client, indexName, type, 0);
/*
* Phrases match across different values using default slop with pre-2.0 default
* position_increment_gap.
*/
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two")).get(), 1);
}
public void testLargerThanDefault() throws IOException {
setupGapInMapping(10000);
testGap(client(), "test", "test", 10000);
}
public void testSmallerThanDefault() throws IOException {
setupGapInMapping(2);
testGap(client(), "test", "test", 2);
}
public void testNegativeIsError() throws IOException {
try {
setupGapInMapping(-1);
fail("Expected an error");
} catch (MapperParsingException e) {
assertThat(ExceptionsHelper.detailedMessage(e), containsString("positions_increment_gap less than 0 aren't allowed"));
}
}
/**
* Tests that the default actually defaults to the position_increment_gap
* configured in the analyzer. This behavior is very old and a little
* strange but not worth breaking some thought.
*/
public void testDefaultDefaultsToAnalyzer() throws IOException {
XContentBuilder settings = XContentFactory.jsonBuilder().startObject().startObject("analysis").startObject("analyzer")
.startObject("gappy");
settings.field("type", "custom");
settings.field("tokenizer", "standard");
settings.field("position_increment_gap", 2);
setupAnalyzer(settings, "gappy");
testGap(client(), "test", "test", 2);
}
/**
* Build an index named "test" with a field named "string" with the provided
* positionIncrementGap that uses the standard analyzer.
*/
private void setupGapInMapping(int positionIncrementGap) throws IOException {
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").startObject("string");
mapping.field("type", "string");
mapping.field("position_increment_gap", positionIncrementGap);
client().admin().indices().prepareCreate("test").addMapping("test", mapping).get();
}
/**
* Build an index named "test" with the provided settings and and a field
* named "string" that uses the specified analyzer and default
* position_increment_gap.
*/
private void setupAnalyzer(XContentBuilder settings, String analyzer) throws IOException {
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("properties").startObject("string");
mapping.field("type", "string");
mapping.field("analyzer", analyzer);
client().admin().indices().prepareCreate("test").addMapping("test", mapping).setSettings(settings).get();
}
private static void testGap(Client client, String indexName, String type, int positionIncrementGap) throws IOException {
client.prepareIndex(indexName, type, "position_gap_test").setSource("string", Arrays.asList("one", "two three")).setRefresh(true).get();
// Baseline - phrase query finds matches in the same field value
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "two three")).get(), 1);
if (positionIncrementGap > 0) {
// No match across gaps when slop < position gap
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two").slop(positionIncrementGap - 1)).get(),
0);
}
// Match across gaps when slop >= position gap
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two").slop(positionIncrementGap)).get(), 1);
assertHitCount(client.prepareSearch(indexName).setQuery(matchPhraseQuery("string", "one two").slop(positionIncrementGap + 1)).get(), 1);
}
}