TextFieldMapperTests.java 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. /*
  2. * Licensed to Elasticsearch under one or more contributor
  3. * license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright
  5. * ownership. Elasticsearch licenses this file to you under
  6. * the Apache License, Version 2.0 (the "License"); you may
  7. * not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.index.mapper;
  20. import org.apache.lucene.analysis.MockSynonymAnalyzer;
  21. import org.apache.lucene.analysis.TokenStream;
  22. import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
  23. import org.apache.lucene.document.FieldType;
  24. import org.apache.lucene.index.DocValuesType;
  25. import org.apache.lucene.index.IndexOptions;
  26. import org.apache.lucene.index.IndexableField;
  27. import org.apache.lucene.index.IndexableFieldType;
  28. import org.apache.lucene.index.LeafReader;
  29. import org.apache.lucene.index.PostingsEnum;
  30. import org.apache.lucene.index.Term;
  31. import org.apache.lucene.index.TermsEnum;
  32. import org.apache.lucene.search.ConstantScoreQuery;
  33. import org.apache.lucene.search.MultiPhraseQuery;
  34. import org.apache.lucene.search.PhraseQuery;
  35. import org.apache.lucene.search.PrefixQuery;
  36. import org.apache.lucene.search.Query;
  37. import org.apache.lucene.search.TermQuery;
  38. import org.apache.lucene.util.BytesRef;
  39. import org.elasticsearch.Version;
  40. import org.elasticsearch.action.index.IndexRequest;
  41. import org.elasticsearch.common.Strings;
  42. import org.elasticsearch.common.bytes.BytesReference;
  43. import org.elasticsearch.common.compress.CompressedXContent;
  44. import org.elasticsearch.common.lucene.uid.Versions;
  45. import org.elasticsearch.common.settings.Settings;
  46. import org.elasticsearch.common.xcontent.ToXContent;
  47. import org.elasticsearch.common.xcontent.XContentBuilder;
  48. import org.elasticsearch.common.xcontent.XContentFactory;
  49. import org.elasticsearch.common.xcontent.XContentType;
  50. import org.elasticsearch.index.IndexService;
  51. import org.elasticsearch.index.VersionType;
  52. import org.elasticsearch.index.engine.Engine;
  53. import org.elasticsearch.index.mapper.MapperService.MergeReason;
  54. import org.elasticsearch.index.mapper.TextFieldMapper.TextFieldType;
  55. import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
  56. import org.elasticsearch.index.query.QueryShardContext;
  57. import org.elasticsearch.index.search.MatchQuery;
  58. import org.elasticsearch.index.seqno.SequenceNumbers;
  59. import org.elasticsearch.index.shard.IndexShard;
  60. import org.elasticsearch.plugins.Plugin;
  61. import org.elasticsearch.test.ESSingleNodeTestCase;
  62. import org.elasticsearch.test.InternalSettingsPlugin;
  63. import org.junit.Before;
  64. import java.io.IOException;
  65. import java.util.Arrays;
  66. import java.util.Collection;
  67. import java.util.Collections;
  68. import java.util.HashMap;
  69. import java.util.Map;
  70. import static org.apache.lucene.search.MultiTermQuery.CONSTANT_SCORE_REWRITE;
  71. import static org.hamcrest.Matchers.containsString;
  72. import static org.hamcrest.Matchers.equalTo;
  73. import static org.hamcrest.Matchers.instanceOf;
  74. import static org.hamcrest.core.Is.is;
  75. public class TextFieldMapperTests extends ESSingleNodeTestCase {
  76. IndexService indexService;
  77. DocumentMapperParser parser;
  78. @Before
  79. public void setup() {
  80. Settings settings = Settings.builder()
  81. // Stop filter remains in server as it is part of lucene-core
  82. .put("index.analysis.analyzer.my_stop_analyzer.tokenizer", "standard")
  83. .put("index.analysis.analyzer.my_stop_analyzer.filter", "stop")
  84. .build();
  85. indexService = createIndex("test", settings);
  86. parser = indexService.mapperService().documentMapperParser();
  87. }
  88. @Override
  89. protected Collection<Class<? extends Plugin>> getPlugins() {
  90. return pluginList(InternalSettingsPlugin.class);
  91. }
  92. public void testDefaults() throws IOException {
  93. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  94. .startObject("properties").startObject("field").field("type", "text").endObject().endObject()
  95. .endObject().endObject());
  96. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  97. assertEquals(mapping, mapper.mappingSource().toString());
  98. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  99. .bytes(XContentFactory.jsonBuilder()
  100. .startObject()
  101. .field("field", "1234")
  102. .endObject()),
  103. XContentType.JSON));
  104. IndexableField[] fields = doc.rootDoc().getFields("field");
  105. assertEquals(1, fields.length);
  106. assertEquals("1234", fields[0].stringValue());
  107. IndexableFieldType fieldType = fields[0].fieldType();
  108. assertThat(fieldType.omitNorms(), equalTo(false));
  109. assertTrue(fieldType.tokenized());
  110. assertFalse(fieldType.stored());
  111. assertThat(fieldType.indexOptions(), equalTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS));
  112. assertThat(fieldType.storeTermVectors(), equalTo(false));
  113. assertThat(fieldType.storeTermVectorOffsets(), equalTo(false));
  114. assertThat(fieldType.storeTermVectorPositions(), equalTo(false));
  115. assertThat(fieldType.storeTermVectorPayloads(), equalTo(false));
  116. assertEquals(DocValuesType.NONE, fieldType.docValuesType());
  117. }
  118. public void testEnableStore() throws IOException {
  119. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  120. .startObject("properties").startObject("field").field("type", "text").field("store", true).endObject().endObject()
  121. .endObject().endObject());
  122. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  123. assertEquals(mapping, mapper.mappingSource().toString());
  124. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  125. .bytes(XContentFactory.jsonBuilder()
  126. .startObject()
  127. .field("field", "1234")
  128. .endObject()),
  129. XContentType.JSON));
  130. IndexableField[] fields = doc.rootDoc().getFields("field");
  131. assertEquals(1, fields.length);
  132. assertTrue(fields[0].fieldType().stored());
  133. }
  134. public void testDisableIndex() throws IOException {
  135. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  136. .startObject("properties").startObject("field").field("type", "text").field("index", false).endObject().endObject()
  137. .endObject().endObject());
  138. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  139. assertEquals(mapping, mapper.mappingSource().toString());
  140. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  141. .bytes(XContentFactory.jsonBuilder()
  142. .startObject()
  143. .field("field", "1234")
  144. .endObject()),
  145. XContentType.JSON));
  146. IndexableField[] fields = doc.rootDoc().getFields("field");
  147. assertEquals(0, fields.length);
  148. }
  149. public void testDisableNorms() throws IOException {
  150. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  151. .startObject("properties").startObject("field")
  152. .field("type", "text")
  153. .field("norms", false)
  154. .endObject().endObject()
  155. .endObject().endObject());
  156. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  157. assertEquals(mapping, mapper.mappingSource().toString());
  158. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  159. .bytes(XContentFactory.jsonBuilder()
  160. .startObject()
  161. .field("field", "1234")
  162. .endObject()),
  163. XContentType.JSON));
  164. IndexableField[] fields = doc.rootDoc().getFields("field");
  165. assertEquals(1, fields.length);
  166. assertTrue(fields[0].fieldType().omitNorms());
  167. }
  168. public void testIndexOptions() throws IOException {
  169. Map<String, IndexOptions> supportedOptions = new HashMap<>();
  170. supportedOptions.put("docs", IndexOptions.DOCS);
  171. supportedOptions.put("freqs", IndexOptions.DOCS_AND_FREQS);
  172. supportedOptions.put("positions", IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
  173. supportedOptions.put("offsets", IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
  174. XContentBuilder mappingBuilder = XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties");
  175. for (String option : supportedOptions.keySet()) {
  176. mappingBuilder.startObject(option).field("type", "text").field("index_options", option).endObject();
  177. }
  178. String mapping = Strings.toString(mappingBuilder.endObject().endObject().endObject());
  179. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  180. XContentBuilder jsonDoc = XContentFactory.jsonBuilder().startObject();
  181. for (String option : supportedOptions.keySet()) {
  182. jsonDoc.field(option, "1234");
  183. }
  184. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference.bytes(jsonDoc.endObject()),
  185. XContentType.JSON));
  186. for (Map.Entry<String, IndexOptions> entry : supportedOptions.entrySet()) {
  187. String field = entry.getKey();
  188. IndexOptions options = entry.getValue();
  189. IndexableField[] fields = doc.rootDoc().getFields(field);
  190. assertEquals(1, fields.length);
  191. assertEquals(options, fields[0].fieldType().indexOptions());
  192. }
  193. }
  194. public void testDefaultPositionIncrementGap() throws IOException {
  195. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  196. .startObject("properties").startObject("field").field("type", "text").endObject().endObject()
  197. .endObject().endObject());
  198. DocumentMapper mapper = indexService.mapperService().merge("type",
  199. new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE);
  200. assertEquals(mapping, mapper.mappingSource().toString());
  201. SourceToParse sourceToParse = SourceToParse.source("test", "type", "1", BytesReference
  202. .bytes(XContentFactory.jsonBuilder()
  203. .startObject()
  204. .array("field", new String[] {"a", "b"})
  205. .endObject()),
  206. XContentType.JSON);
  207. ParsedDocument doc = mapper.parse(sourceToParse);
  208. IndexableField[] fields = doc.rootDoc().getFields("field");
  209. assertEquals(2, fields.length);
  210. assertEquals("a", fields[0].stringValue());
  211. assertEquals("b", fields[1].stringValue());
  212. IndexShard shard = indexService.getShard(0);
  213. shard.applyIndexOperationOnPrimary(Versions.MATCH_ANY, VersionType.INTERNAL,
  214. sourceToParse, SequenceNumbers.UNASSIGNED_SEQ_NO, 0, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, false);
  215. shard.refresh("test");
  216. try (Engine.Searcher searcher = shard.acquireSearcher("test")) {
  217. LeafReader leaf = searcher.getDirectoryReader().leaves().get(0).reader();
  218. TermsEnum terms = leaf.terms("field").iterator();
  219. assertTrue(terms.seekExact(new BytesRef("b")));
  220. PostingsEnum postings = terms.postings(null, PostingsEnum.POSITIONS);
  221. assertEquals(0, postings.nextDoc());
  222. assertEquals(TextFieldMapper.Defaults.POSITION_INCREMENT_GAP + 1, postings.nextPosition());
  223. }
  224. }
  225. public void testPositionIncrementGap() throws IOException {
  226. final int positionIncrementGap = randomIntBetween(1, 1000);
  227. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  228. .startObject("properties").startObject("field")
  229. .field("type", "text")
  230. .field("position_increment_gap", positionIncrementGap)
  231. .endObject().endObject()
  232. .endObject().endObject());
  233. DocumentMapper mapper = indexService.mapperService().merge("type",
  234. new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE);
  235. assertEquals(mapping, mapper.mappingSource().toString());
  236. SourceToParse sourceToParse = SourceToParse.source("test", "type", "1", BytesReference
  237. .bytes(XContentFactory.jsonBuilder()
  238. .startObject()
  239. .array("field", new String[]{"a", "b"})
  240. .endObject()),
  241. XContentType.JSON);
  242. ParsedDocument doc = mapper.parse(sourceToParse);
  243. IndexableField[] fields = doc.rootDoc().getFields("field");
  244. assertEquals(2, fields.length);
  245. assertEquals("a", fields[0].stringValue());
  246. assertEquals("b", fields[1].stringValue());
  247. IndexShard shard = indexService.getShard(0);
  248. shard.applyIndexOperationOnPrimary(Versions.MATCH_ANY, VersionType.INTERNAL,
  249. sourceToParse, SequenceNumbers.UNASSIGNED_SEQ_NO, 0, IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP, false);
  250. shard.refresh("test");
  251. try (Engine.Searcher searcher = shard.acquireSearcher("test")) {
  252. LeafReader leaf = searcher.getDirectoryReader().leaves().get(0).reader();
  253. TermsEnum terms = leaf.terms("field").iterator();
  254. assertTrue(terms.seekExact(new BytesRef("b")));
  255. PostingsEnum postings = terms.postings(null, PostingsEnum.POSITIONS);
  256. assertEquals(0, postings.nextDoc());
  257. assertEquals(positionIncrementGap + 1, postings.nextPosition());
  258. }
  259. }
  260. public void testSearchAnalyzerSerialization() throws IOException {
  261. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  262. .startObject("properties")
  263. .startObject("field")
  264. .field("type", "text")
  265. .field("analyzer", "standard")
  266. .field("search_analyzer", "keyword")
  267. .endObject()
  268. .endObject().endObject().endObject());
  269. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  270. assertEquals(mapping, mapper.mappingSource().toString());
  271. // special case: default index analyzer
  272. mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  273. .startObject("properties")
  274. .startObject("field")
  275. .field("type", "text")
  276. .field("analyzer", "default")
  277. .field("search_analyzer", "keyword")
  278. .endObject()
  279. .endObject().endObject().endObject());
  280. mapper = parser.parse("type", new CompressedXContent(mapping));
  281. assertEquals(mapping, mapper.mappingSource().toString());
  282. mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  283. .startObject("properties")
  284. .startObject("field")
  285. .field("type", "text")
  286. .field("analyzer", "keyword")
  287. .endObject()
  288. .endObject().endObject().endObject());
  289. mapper = parser.parse("type", new CompressedXContent(mapping));
  290. assertEquals(mapping, mapper.mappingSource().toString());
  291. // special case: default search analyzer
  292. mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  293. .startObject("properties")
  294. .startObject("field")
  295. .field("type", "text")
  296. .field("analyzer", "keyword")
  297. .field("search_analyzer", "default")
  298. .endObject()
  299. .endObject().endObject().endObject());
  300. mapper = parser.parse("type", new CompressedXContent(mapping));
  301. assertEquals(mapping, mapper.mappingSource().toString());
  302. mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  303. .startObject("properties")
  304. .startObject("field")
  305. .field("type", "text")
  306. .field("analyzer", "keyword")
  307. .endObject()
  308. .endObject().endObject().endObject());
  309. mapper = parser.parse("type", new CompressedXContent(mapping));
  310. XContentBuilder builder = XContentFactory.jsonBuilder();
  311. builder.startObject();
  312. mapper.toXContent(builder, new ToXContent.MapParams(Collections.singletonMap("include_defaults", "true")));
  313. builder.endObject();
  314. String mappingString = Strings.toString(builder);
  315. assertTrue(mappingString.contains("analyzer"));
  316. assertTrue(mappingString.contains("search_analyzer"));
  317. assertTrue(mappingString.contains("search_quote_analyzer"));
  318. }
  319. public void testSearchQuoteAnalyzerSerialization() throws IOException {
  320. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  321. .startObject("properties")
  322. .startObject("field")
  323. .field("type", "text")
  324. .field("analyzer", "standard")
  325. .field("search_analyzer", "standard")
  326. .field("search_quote_analyzer", "keyword")
  327. .endObject()
  328. .endObject().endObject().endObject());
  329. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  330. assertEquals(mapping, mapper.mappingSource().toString());
  331. // special case: default index/search analyzer
  332. mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  333. .startObject("properties")
  334. .startObject("field")
  335. .field("type", "text")
  336. .field("analyzer", "default")
  337. .field("search_analyzer", "default")
  338. .field("search_quote_analyzer", "keyword")
  339. .endObject()
  340. .endObject().endObject().endObject());
  341. mapper = parser.parse("type", new CompressedXContent(mapping));
  342. assertEquals(mapping, mapper.mappingSource().toString());
  343. }
  344. public void testTermVectors() throws IOException {
  345. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  346. .startObject("properties")
  347. .startObject("field1")
  348. .field("type", "text")
  349. .field("term_vector", "no")
  350. .endObject()
  351. .startObject("field2")
  352. .field("type", "text")
  353. .field("term_vector", "yes")
  354. .endObject()
  355. .startObject("field3")
  356. .field("type", "text")
  357. .field("term_vector", "with_offsets")
  358. .endObject()
  359. .startObject("field4")
  360. .field("type", "text")
  361. .field("term_vector", "with_positions")
  362. .endObject()
  363. .startObject("field5")
  364. .field("type", "text")
  365. .field("term_vector", "with_positions_offsets")
  366. .endObject()
  367. .startObject("field6")
  368. .field("type", "text")
  369. .field("term_vector", "with_positions_offsets_payloads")
  370. .endObject()
  371. .endObject()
  372. .endObject().endObject());
  373. DocumentMapper defaultMapper = parser.parse("type", new CompressedXContent(mapping));
  374. ParsedDocument doc = defaultMapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  375. .bytes(XContentFactory.jsonBuilder()
  376. .startObject()
  377. .field("field1", "1234")
  378. .field("field2", "1234")
  379. .field("field3", "1234")
  380. .field("field4", "1234")
  381. .field("field5", "1234")
  382. .field("field6", "1234")
  383. .endObject()),
  384. XContentType.JSON));
  385. assertThat(doc.rootDoc().getField("field1").fieldType().storeTermVectors(), equalTo(false));
  386. assertThat(doc.rootDoc().getField("field1").fieldType().storeTermVectorOffsets(), equalTo(false));
  387. assertThat(doc.rootDoc().getField("field1").fieldType().storeTermVectorPositions(), equalTo(false));
  388. assertThat(doc.rootDoc().getField("field1").fieldType().storeTermVectorPayloads(), equalTo(false));
  389. assertThat(doc.rootDoc().getField("field2").fieldType().storeTermVectors(), equalTo(true));
  390. assertThat(doc.rootDoc().getField("field2").fieldType().storeTermVectorOffsets(), equalTo(false));
  391. assertThat(doc.rootDoc().getField("field2").fieldType().storeTermVectorPositions(), equalTo(false));
  392. assertThat(doc.rootDoc().getField("field2").fieldType().storeTermVectorPayloads(), equalTo(false));
  393. assertThat(doc.rootDoc().getField("field3").fieldType().storeTermVectors(), equalTo(true));
  394. assertThat(doc.rootDoc().getField("field3").fieldType().storeTermVectorOffsets(), equalTo(true));
  395. assertThat(doc.rootDoc().getField("field3").fieldType().storeTermVectorPositions(), equalTo(false));
  396. assertThat(doc.rootDoc().getField("field3").fieldType().storeTermVectorPayloads(), equalTo(false));
  397. assertThat(doc.rootDoc().getField("field4").fieldType().storeTermVectors(), equalTo(true));
  398. assertThat(doc.rootDoc().getField("field4").fieldType().storeTermVectorOffsets(), equalTo(false));
  399. assertThat(doc.rootDoc().getField("field4").fieldType().storeTermVectorPositions(), equalTo(true));
  400. assertThat(doc.rootDoc().getField("field4").fieldType().storeTermVectorPayloads(), equalTo(false));
  401. assertThat(doc.rootDoc().getField("field5").fieldType().storeTermVectors(), equalTo(true));
  402. assertThat(doc.rootDoc().getField("field5").fieldType().storeTermVectorOffsets(), equalTo(true));
  403. assertThat(doc.rootDoc().getField("field5").fieldType().storeTermVectorPositions(), equalTo(true));
  404. assertThat(doc.rootDoc().getField("field5").fieldType().storeTermVectorPayloads(), equalTo(false));
  405. assertThat(doc.rootDoc().getField("field6").fieldType().storeTermVectors(), equalTo(true));
  406. assertThat(doc.rootDoc().getField("field6").fieldType().storeTermVectorOffsets(), equalTo(true));
  407. assertThat(doc.rootDoc().getField("field6").fieldType().storeTermVectorPositions(), equalTo(true));
  408. assertThat(doc.rootDoc().getField("field6").fieldType().storeTermVectorPayloads(), equalTo(true));
  409. }
  410. public void testEagerGlobalOrdinals() throws IOException {
  411. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  412. .startObject("properties").startObject("field")
  413. .field("type", "text")
  414. .field("eager_global_ordinals", true)
  415. .endObject().endObject()
  416. .endObject().endObject());
  417. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  418. assertEquals(mapping, mapper.mappingSource().toString());
  419. FieldMapper fieldMapper = (FieldMapper) mapper.mappers().getMapper("field");
  420. assertTrue(fieldMapper.fieldType().eagerGlobalOrdinals());
  421. }
  422. public void testFielddata() throws IOException {
  423. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  424. .startObject("properties").startObject("field")
  425. .field("type", "text")
  426. .endObject().endObject()
  427. .endObject().endObject());
  428. DocumentMapper disabledMapper = parser.parse("type", new CompressedXContent(mapping));
  429. assertEquals(mapping, disabledMapper.mappingSource().toString());
  430. IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> {
  431. FieldMapper fieldMapper = (FieldMapper) disabledMapper.mappers().getMapper("field");
  432. fieldMapper.fieldType().fielddataBuilder("test");
  433. });
  434. assertThat(e.getMessage(), containsString("Fielddata is disabled"));
  435. mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  436. .startObject("properties").startObject("field")
  437. .field("type", "text")
  438. .field("fielddata", true)
  439. .endObject().endObject()
  440. .endObject().endObject());
  441. DocumentMapper enabledMapper = parser.parse("type", new CompressedXContent(mapping));
  442. assertEquals(mapping, enabledMapper.mappingSource().toString());
  443. FieldMapper enabledFieldMapper = (FieldMapper) enabledMapper.mappers().getMapper("field");
  444. enabledFieldMapper.fieldType().fielddataBuilder("test"); // no exception this time
  445. String illegalMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  446. .startObject("properties").startObject("field")
  447. .field("type", "text")
  448. .field("index", false)
  449. .field("fielddata", true)
  450. .endObject().endObject()
  451. .endObject().endObject());
  452. IllegalArgumentException ex = expectThrows(IllegalArgumentException.class,
  453. () -> parser.parse("type", new CompressedXContent(illegalMapping)));
  454. assertThat(ex.getMessage(), containsString("Cannot enable fielddata on a [text] field that is not indexed"));
  455. }
  456. public void testFrequencyFilter() throws IOException {
  457. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  458. .startObject("properties").startObject("field")
  459. .field("type", "text")
  460. .field("fielddata", true)
  461. .startObject("fielddata_frequency_filter")
  462. .field("min", 2d)
  463. .field("min_segment_size", 1000)
  464. .endObject()
  465. .endObject().endObject()
  466. .endObject().endObject());
  467. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  468. assertEquals(mapping, mapper.mappingSource().toString());
  469. TextFieldMapper fieldMapper = (TextFieldMapper) mapper.mappers().getMapper("field");
  470. TextFieldType fieldType = fieldMapper.fieldType();
  471. assertThat(fieldType.fielddataMinFrequency(), equalTo(2d));
  472. assertThat(fieldType.fielddataMaxFrequency(), equalTo((double) Integer.MAX_VALUE));
  473. assertThat(fieldType.fielddataMinSegmentSize(), equalTo(1000));
  474. }
  475. public void testNullConfigValuesFail() throws MapperParsingException, IOException {
  476. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
  477. .startObject("type")
  478. .startObject("properties")
  479. .startObject("field")
  480. .field("type", "text")
  481. .field("analyzer", (String) null)
  482. .endObject()
  483. .endObject()
  484. .endObject().endObject());
  485. Exception e = expectThrows(MapperParsingException.class, () -> parser.parse("type", new CompressedXContent(mapping)));
  486. assertEquals("[analyzer] must not have a [null] value", e.getMessage());
  487. }
  488. public void testNotIndexedFieldPositionIncrement() throws IOException {
  489. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  490. .startObject("properties").startObject("field")
  491. .field("type", "text")
  492. .field("index", false)
  493. .field("position_increment_gap", 10)
  494. .endObject().endObject().endObject().endObject());
  495. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  496. () -> parser.parse("type", new CompressedXContent(mapping)));
  497. assertEquals("Cannot set position_increment_gap on field [field] without positions enabled", e.getMessage());
  498. }
  499. public void testAnalyzedFieldPositionIncrementWithoutPositions() throws IOException {
  500. for (String indexOptions : Arrays.asList("docs", "freqs")) {
  501. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  502. .startObject("properties").startObject("field")
  503. .field("type", "text")
  504. .field("index_options", indexOptions)
  505. .field("position_increment_gap", 10)
  506. .endObject().endObject().endObject().endObject());
  507. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  508. () -> parser.parse("type", new CompressedXContent(mapping)));
  509. assertEquals("Cannot set position_increment_gap on field [field] without positions enabled", e.getMessage());
  510. }
  511. }
  512. public void testEmptyName() throws IOException {
  513. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject()
  514. .startObject("type")
  515. .startObject("properties")
  516. .startObject("")
  517. .field("type", "text")
  518. .endObject()
  519. .endObject()
  520. .endObject().endObject());
  521. // Empty name not allowed in index created after 5.0
  522. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  523. () -> parser.parse("type", new CompressedXContent(mapping))
  524. );
  525. assertThat(e.getMessage(), containsString("name cannot be empty string"));
  526. }
  527. public void testIndexPrefixIndexTypes() throws IOException {
  528. {
  529. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  530. .startObject("properties").startObject("field")
  531. .field("type", "text")
  532. .field("analyzer", "standard")
  533. .startObject("index_prefixes").endObject()
  534. .field("index_options", "offsets")
  535. .endObject().endObject().endObject().endObject());
  536. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  537. FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
  538. FieldType ft = prefix.fieldType;
  539. assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, ft.indexOptions());
  540. }
  541. {
  542. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  543. .startObject("properties").startObject("field")
  544. .field("type", "text")
  545. .field("analyzer", "standard")
  546. .startObject("index_prefixes").endObject()
  547. .field("index_options", "freqs")
  548. .endObject().endObject().endObject().endObject());
  549. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  550. FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
  551. FieldType ft = prefix.fieldType;
  552. assertEquals(IndexOptions.DOCS, ft.indexOptions());
  553. assertFalse(ft.storeTermVectors());
  554. }
  555. {
  556. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  557. .startObject("properties").startObject("field")
  558. .field("type", "text")
  559. .field("analyzer", "standard")
  560. .startObject("index_prefixes").endObject()
  561. .field("index_options", "positions")
  562. .endObject().endObject().endObject().endObject());
  563. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  564. FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
  565. FieldType ft = prefix.fieldType;
  566. if (indexService.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
  567. assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
  568. } else {
  569. assertEquals(IndexOptions.DOCS, ft.indexOptions());
  570. }
  571. assertFalse(ft.storeTermVectors());
  572. }
  573. {
  574. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  575. .startObject("properties").startObject("field")
  576. .field("type", "text")
  577. .field("analyzer", "standard")
  578. .startObject("index_prefixes").endObject()
  579. .field("term_vector", "with_positions_offsets")
  580. .endObject().endObject().endObject().endObject());
  581. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  582. FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
  583. FieldType ft = prefix.fieldType;
  584. if (indexService.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
  585. assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
  586. } else {
  587. assertEquals(IndexOptions.DOCS, ft.indexOptions());
  588. }
  589. assertTrue(ft.storeTermVectorOffsets());
  590. }
  591. {
  592. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  593. .startObject("properties").startObject("field")
  594. .field("type", "text")
  595. .field("analyzer", "standard")
  596. .startObject("index_prefixes").endObject()
  597. .field("term_vector", "with_positions")
  598. .endObject().endObject().endObject().endObject());
  599. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  600. FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
  601. FieldType ft = prefix.fieldType;
  602. if (indexService.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
  603. assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
  604. } else {
  605. assertEquals(IndexOptions.DOCS, ft.indexOptions());
  606. }
  607. assertFalse(ft.storeTermVectorOffsets());
  608. }
  609. }
  610. public void testFastPhraseMapping() throws IOException {
  611. QueryShardContext queryShardContext = indexService.newQueryShardContext(
  612. randomInt(20), null, () -> {
  613. throw new UnsupportedOperationException();
  614. }, null);
  615. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  616. .startObject("properties")
  617. .startObject("field")
  618. .field("type", "text")
  619. .field("analyzer", "my_stop_analyzer")
  620. .field("index_phrases", true)
  621. .endObject()
  622. .startObject("synfield")
  623. .field("type", "text")
  624. .field("analyzer", "standard") // will be replaced with MockSynonymAnalyzer
  625. .field("index_phrases", true)
  626. .endObject()
  627. .endObject()
  628. .endObject().endObject());
  629. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  630. assertEquals(mapping, mapper.mappingSource().toString());
  631. queryShardContext.getMapperService().merge("type", new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE);
  632. Query q = new MatchPhraseQueryBuilder("field", "two words").toQuery(queryShardContext);
  633. assertThat(q, is(new PhraseQuery("field._index_phrase", "two words")));
  634. Query q2 = new MatchPhraseQueryBuilder("field", "three words here").toQuery(queryShardContext);
  635. assertThat(q2, is(new PhraseQuery("field._index_phrase", "three words", "words here")));
  636. Query q3 = new MatchPhraseQueryBuilder("field", "two words").slop(1).toQuery(queryShardContext);
  637. assertThat(q3, is(new PhraseQuery(1, "field", "two", "words")));
  638. Query q4 = new MatchPhraseQueryBuilder("field", "singleton").toQuery(queryShardContext);
  639. assertThat(q4, is(new TermQuery(new Term("field", "singleton"))));
  640. Query q5 = new MatchPhraseQueryBuilder("field", "sparkle a stopword").toQuery(queryShardContext);
  641. assertThat(q5,
  642. is(new PhraseQuery.Builder().add(new Term("field", "sparkle")).add(new Term("field", "stopword"), 2).build()));
  643. MatchQuery matchQuery = new MatchQuery(queryShardContext);
  644. matchQuery.setAnalyzer(new MockSynonymAnalyzer());
  645. Query q6 = matchQuery.parse(MatchQuery.Type.PHRASE, "synfield", "motor dogs");
  646. assertThat(q6, is(new MultiPhraseQuery.Builder()
  647. .add(new Term[]{
  648. new Term("synfield._index_phrase", "motor dogs"),
  649. new Term("synfield._index_phrase", "motor dog")})
  650. .build()));
  651. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  652. .bytes(XContentFactory.jsonBuilder()
  653. .startObject()
  654. .field("field", "Some English text that is going to be very useful")
  655. .endObject()),
  656. XContentType.JSON));
  657. IndexableField[] fields = doc.rootDoc().getFields("field._index_phrase");
  658. assertEquals(1, fields.length);
  659. try (TokenStream ts = fields[0].tokenStream(queryShardContext.getMapperService().indexAnalyzer(), null)) {
  660. CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class);
  661. ts.reset();
  662. assertTrue(ts.incrementToken());
  663. assertEquals("Some English", termAtt.toString());
  664. }
  665. {
  666. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  667. .startObject("properties").startObject("field")
  668. .field("type", "text")
  669. .field("index", "false")
  670. .field("index_phrases", true)
  671. .endObject().endObject()
  672. .endObject().endObject());
  673. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  674. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  675. );
  676. assertThat(e.getMessage(), containsString("Cannot set index_phrases on unindexed field [field]"));
  677. }
  678. {
  679. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  680. .startObject("properties").startObject("field")
  681. .field("type", "text")
  682. .field("index_options", "freqs")
  683. .field("index_phrases", true)
  684. .endObject().endObject()
  685. .endObject().endObject());
  686. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  687. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  688. );
  689. assertThat(e.getMessage(), containsString("Cannot set index_phrases on field [field] if positions are not enabled"));
  690. }
  691. }
  692. public void testIndexPrefixMapping() throws IOException {
  693. QueryShardContext queryShardContext = indexService.newQueryShardContext(
  694. randomInt(20), null, () -> {
  695. throw new UnsupportedOperationException();
  696. }, null);
  697. {
  698. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  699. .startObject("properties").startObject("field")
  700. .field("type", "text")
  701. .field("analyzer", "standard")
  702. .startObject("index_prefixes")
  703. .field("min_chars", 1)
  704. .field("max_chars", 10)
  705. .endObject()
  706. .endObject().endObject()
  707. .endObject().endObject());
  708. DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
  709. assertEquals(mapping, mapper.mappingSource().toString());
  710. assertThat(mapper.mappers().getMapper("field._index_prefix").toString(), containsString("prefixChars=1:10"));
  711. FieldMapper fieldMapper = (FieldMapper) mapper.mappers().getMapper("field");
  712. MappedFieldType fieldType = fieldMapper.fieldType;
  713. Query q = fieldType.prefixQuery("goin", CONSTANT_SCORE_REWRITE, queryShardContext);
  714. assertEquals(new ConstantScoreQuery(new TermQuery(new Term("field._index_prefix", "goin"))), q);
  715. q = fieldType.prefixQuery("internationalisatio", CONSTANT_SCORE_REWRITE, queryShardContext);
  716. assertEquals(new PrefixQuery(new Term("field", "internationalisatio")), q);
  717. ParsedDocument doc = mapper.parse(SourceToParse.source("test", "type", "1", BytesReference
  718. .bytes(XContentFactory.jsonBuilder()
  719. .startObject()
  720. .field("field", "Some English text that is going to be very useful")
  721. .endObject()),
  722. XContentType.JSON));
  723. IndexableField[] fields = doc.rootDoc().getFields("field._index_prefix");
  724. assertEquals(1, fields.length);
  725. }
  726. {
  727. String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  728. .startObject("properties").startObject("field")
  729. .field("type", "text")
  730. .field("analyzer", "standard")
  731. .startObject("index_prefixes").endObject()
  732. .endObject().endObject()
  733. .endObject().endObject());
  734. CompressedXContent json = new CompressedXContent(mapping);
  735. DocumentMapper mapper = parser.parse("type", json);
  736. FieldMapper fieldMapper = (FieldMapper) mapper.mappers().getMapper("field");
  737. MappedFieldType fieldType = fieldMapper.fieldType;
  738. Query q1 = fieldType.prefixQuery("g", CONSTANT_SCORE_REWRITE, queryShardContext);
  739. assertThat(q1, instanceOf(PrefixQuery.class));
  740. Query q2 = fieldType.prefixQuery("go", CONSTANT_SCORE_REWRITE, queryShardContext);
  741. assertThat(q2, instanceOf(ConstantScoreQuery.class));
  742. Query q5 = fieldType.prefixQuery("going", CONSTANT_SCORE_REWRITE, queryShardContext);
  743. assertThat(q5, instanceOf(ConstantScoreQuery.class));
  744. Query q6 = fieldType.prefixQuery("goings", CONSTANT_SCORE_REWRITE, queryShardContext);
  745. assertThat(q6, instanceOf(PrefixQuery.class));
  746. }
  747. {
  748. String illegalMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  749. .startObject("properties").startObject("field")
  750. .field("type", "text")
  751. .field("analyzer", "standard")
  752. .startObject("index_prefixes")
  753. .field("min_chars", 1)
  754. .field("max_chars", 10)
  755. .endObject()
  756. .startObject("fields")
  757. .startObject("_index_prefix").field("type", "text").endObject()
  758. .endObject()
  759. .endObject().endObject()
  760. .endObject().endObject());
  761. IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> {
  762. indexService.mapperService()
  763. .merge("type", new CompressedXContent(illegalMapping), MergeReason.MAPPING_UPDATE);
  764. });
  765. assertThat(e.getMessage(), containsString("Field [field._index_prefix] is defined twice in [type]"));
  766. }
  767. {
  768. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  769. .startObject("properties").startObject("field")
  770. .field("type", "text")
  771. .field("analyzer", "standard")
  772. .startObject("index_prefixes")
  773. .field("min_chars", 11)
  774. .field("max_chars", 10)
  775. .endObject()
  776. .endObject().endObject()
  777. .endObject().endObject());
  778. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  779. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  780. );
  781. assertThat(e.getMessage(), containsString("min_chars [11] must be less than max_chars [10]"));
  782. }
  783. {
  784. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  785. .startObject("properties").startObject("field")
  786. .field("type", "text")
  787. .field("analyzer", "standard")
  788. .startObject("index_prefixes")
  789. .field("min_chars", 0)
  790. .field("max_chars", 10)
  791. .endObject()
  792. .endObject().endObject()
  793. .endObject().endObject());
  794. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  795. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  796. );
  797. assertThat(e.getMessage(), containsString("min_chars [0] must be greater than zero"));
  798. }
  799. {
  800. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  801. .startObject("properties").startObject("field")
  802. .field("type", "text")
  803. .field("analyzer", "standard")
  804. .startObject("index_prefixes")
  805. .field("min_chars", 1)
  806. .field("max_chars", 25)
  807. .endObject()
  808. .endObject().endObject()
  809. .endObject().endObject());
  810. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  811. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  812. );
  813. assertThat(e.getMessage(), containsString("max_chars [25] must be less than 20"));
  814. }
  815. {
  816. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  817. .startObject("properties").startObject("field")
  818. .field("type", "text")
  819. .field("analyzer", "standard")
  820. .field("index_prefixes", (String) null)
  821. .endObject().endObject()
  822. .endObject().endObject());
  823. MapperParsingException e = expectThrows(MapperParsingException.class,
  824. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  825. );
  826. assertThat(e.getMessage(), containsString("[index_prefixes] must not have a [null] value"));
  827. }
  828. {
  829. String badConfigMapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
  830. .startObject("properties").startObject("field")
  831. .field("type", "text")
  832. .field("index", "false")
  833. .startObject("index_prefixes").endObject()
  834. .endObject().endObject()
  835. .endObject().endObject());
  836. IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
  837. () -> parser.parse("type", new CompressedXContent(badConfigMapping))
  838. );
  839. assertThat(e.getMessage(), containsString("Cannot set index_prefixes on unindexed field [field]"));
  840. }
  841. }
  842. }