Skip to content

Commit 32a9b09

Browse files
parkertimminsgmjehovich
authored andcommitted
Add tests where patterned_text is nested in object, nested, and passthrough objects (elastic#134831)
Add some tests where patterned_test is nested within objects, nested objects, and passthrough objects. Found a bug with phrase queries when patterned_text is in nested objects: elastic#134830
1 parent 247a28f commit 32a9b09

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.logsdb.patternedtext;
9+
10+
import org.apache.lucene.search.join.ScoreMode;
11+
import org.elasticsearch.action.DocWriteRequest;
12+
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
13+
import org.elasticsearch.action.bulk.BulkRequest;
14+
import org.elasticsearch.action.bulk.BulkResponse;
15+
import org.elasticsearch.action.index.IndexRequest;
16+
import org.elasticsearch.action.search.SearchResponse;
17+
import org.elasticsearch.action.support.IndicesOptions;
18+
import org.elasticsearch.common.settings.Settings;
19+
import org.elasticsearch.index.IndexSettings;
20+
import org.elasticsearch.index.mapper.extras.MapperExtrasPlugin;
21+
import org.elasticsearch.index.query.QueryBuilders;
22+
import org.elasticsearch.license.LicenseSettings;
23+
import org.elasticsearch.plugins.Plugin;
24+
import org.elasticsearch.search.SearchHit;
25+
import org.elasticsearch.test.ESSingleNodeTestCase;
26+
import org.elasticsearch.xcontent.ObjectPath;
27+
import org.elasticsearch.xcontent.XContentType;
28+
import org.elasticsearch.xpack.core.XPackPlugin;
29+
import org.elasticsearch.xpack.logsdb.LogsDBPlugin;
30+
import org.junit.After;
31+
import org.junit.Before;
32+
33+
import java.util.Collection;
34+
import java.util.HashSet;
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.Set;
38+
39+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
40+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
41+
42+
public class PatternedTextNestedObjectTests extends ESSingleNodeTestCase {
43+
44+
@Override
45+
protected Settings nodeSettings() {
46+
return Settings.builder().put(LicenseSettings.SELF_GENERATED_LICENSE_TYPE.getKey(), "trial").build();
47+
}
48+
49+
@Override
50+
protected Collection<Class<? extends Plugin>> getPlugins() {
51+
return List.of(MapperExtrasPlugin.class, XPackPlugin.class, LogsDBPlugin.class);
52+
}
53+
54+
private static final String INDEX = "test_index";
55+
56+
private static final String SHORT_MESSAGE = "some message 123 ";
57+
private static final String LONG_MESSAGE = SHORT_MESSAGE.repeat(((32 * 1024) / SHORT_MESSAGE.length()) + 1);
58+
59+
private static final Settings SYNTHETIC_SETTING = Settings.builder()
60+
.put(IndexSettings.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), "synthetic")
61+
.build();
62+
63+
@Before
64+
public void setup() {
65+
assumeTrue("Only when patterned_text feature flag is enabled", PatternedTextFieldMapper.PATTERNED_TEXT_MAPPER.isEnabled());
66+
}
67+
68+
@After
69+
public void cleanup() {
70+
assertAcked(admin().indices().prepareDelete(INDEX).setIndicesOptions(IndicesOptions.lenientExpandOpen()).get());
71+
}
72+
73+
public void testInObject() {
74+
String mapping = """
75+
{
76+
"properties": {
77+
"obj": {
78+
"type": "object",
79+
"properties": {
80+
"field_patterned_text": {
81+
"type": "patterned_text"
82+
}
83+
}
84+
}
85+
}
86+
}
87+
""";
88+
89+
var createRequest = indicesAdmin().prepareCreate(INDEX).setSettings(SYNTHETIC_SETTING).setMapping(mapping);
90+
createIndex(INDEX, createRequest);
91+
92+
String message = randomBoolean() ? SHORT_MESSAGE : LONG_MESSAGE;
93+
indexDoc("""
94+
{
95+
"obj.field_patterned_text": "%"
96+
}
97+
""".replace("%", message));
98+
99+
var actualMappings = getMapping();
100+
assertEquals("patterned_text", ObjectPath.eval("properties.obj.properties.field_patterned_text.type", actualMappings));
101+
102+
var query = randomBoolean()
103+
? QueryBuilders.matchQuery("obj.field_patterned_text", SHORT_MESSAGE)
104+
: QueryBuilders.matchPhraseQuery("obj.field_patterned_text", SHORT_MESSAGE);
105+
var searchRequest = client().prepareSearch(INDEX).setQuery(query).setSize(1);
106+
107+
assertNoFailuresAndResponse(searchRequest, searchResponse -> {
108+
assertEquals(Set.of(message), getFieldValuesFromSource(searchResponse, "obj.field_patterned_text"));
109+
});
110+
}
111+
112+
public void testInObjectInObject() {
113+
String mapping = """
114+
{
115+
"properties": {
116+
"obj": {
117+
"type": "object",
118+
"properties": {
119+
"inner": {
120+
"type": "object",
121+
"properties": {
122+
"field_patterned_text": {
123+
"type": "patterned_text"
124+
}
125+
}
126+
}
127+
}
128+
}
129+
}
130+
}
131+
""";
132+
133+
var createRequest = indicesAdmin().prepareCreate(INDEX).setSettings(SYNTHETIC_SETTING).setMapping(mapping);
134+
createIndex(INDEX, createRequest);
135+
136+
String message = randomBoolean() ? SHORT_MESSAGE : LONG_MESSAGE;
137+
indexDoc("""
138+
{
139+
"obj.inner.field_patterned_text": "%"
140+
}
141+
""".replace("%", message));
142+
143+
var actualMappings = getMapping();
144+
assertEquals(
145+
"patterned_text",
146+
ObjectPath.eval("properties.obj.properties.inner.properties.field_patterned_text.type", actualMappings)
147+
);
148+
149+
var query = randomBoolean()
150+
? QueryBuilders.matchQuery("obj.inner.field_patterned_text", SHORT_MESSAGE)
151+
: QueryBuilders.matchPhraseQuery("obj.inner.field_patterned_text", SHORT_MESSAGE);
152+
var searchRequest = client().prepareSearch(INDEX).setQuery(query).setSize(1);
153+
154+
assertNoFailuresAndResponse(searchRequest, searchResponse -> {
155+
assertEquals(Set.of(message), getFieldValuesFromSource(searchResponse, "obj.inner.field_patterned_text"));
156+
});
157+
}
158+
159+
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/134830")
160+
public void testInNested() {
161+
String mapping = """
162+
{
163+
"properties": {
164+
"obj": {
165+
"type": "nested",
166+
"properties": {
167+
"field_patterned_text": {
168+
"type": "patterned_text"
169+
}
170+
}
171+
}
172+
}
173+
}
174+
""";
175+
176+
var createRequest = indicesAdmin().prepareCreate(INDEX).setSettings(SYNTHETIC_SETTING).setMapping(mapping);
177+
createIndex(INDEX, createRequest);
178+
179+
String message = randomBoolean() ? SHORT_MESSAGE : LONG_MESSAGE;
180+
indexDoc("""
181+
{
182+
"obj.field_patterned_text": "%"
183+
}
184+
""".replace("%", message));
185+
186+
var actualMappings = getMapping();
187+
assertEquals("patterned_text", ObjectPath.eval("properties.obj.properties.field_patterned_text.type", actualMappings));
188+
189+
var innerQuery = randomBoolean()
190+
? QueryBuilders.matchQuery("obj.field_patterned_text", SHORT_MESSAGE)
191+
: QueryBuilders.matchPhraseQuery("obj.field_patterned_text", SHORT_MESSAGE);
192+
var query = QueryBuilders.nestedQuery("obj", innerQuery, ScoreMode.Avg);
193+
var searchRequest = client().prepareSearch(INDEX).setQuery(query).setSize(1);
194+
195+
assertNoFailuresAndResponse(searchRequest, searchResponse -> {
196+
assertEquals(Set.of(message), getFieldValuesFromSource(searchResponse, "obj.field_patterned_text"));
197+
});
198+
}
199+
200+
public void testInPassthrough() {
201+
String mapping = """
202+
{
203+
"properties": {
204+
"obj": {
205+
"type": "passthrough",
206+
"priority": 1,
207+
"properties": {
208+
"field_patterned_text": {
209+
"type": "patterned_text"
210+
}
211+
}
212+
}
213+
}
214+
}
215+
""";
216+
217+
var createRequest = indicesAdmin().prepareCreate(INDEX).setSettings(SYNTHETIC_SETTING).setMapping(mapping);
218+
createIndex(INDEX, createRequest);
219+
220+
String message = randomBoolean() ? SHORT_MESSAGE : LONG_MESSAGE;
221+
indexDoc("""
222+
{
223+
"obj.field_patterned_text": "%"
224+
}
225+
""".replace("%", message));
226+
227+
var actualMappings = getMapping();
228+
assertEquals("patterned_text", ObjectPath.eval("properties.obj.properties.field_patterned_text.type", actualMappings));
229+
230+
var query = randomBoolean()
231+
? QueryBuilders.matchQuery("field_patterned_text", SHORT_MESSAGE)
232+
: QueryBuilders.matchPhraseQuery("field_patterned_text", SHORT_MESSAGE);
233+
var searchRequest = client().prepareSearch(INDEX).setQuery(query).setSize(1);
234+
235+
assertNoFailuresAndResponse(searchRequest, searchResponse -> {
236+
assertEquals(Set.of(message), getFieldValuesFromSource(searchResponse, "obj.field_patterned_text"));
237+
});
238+
}
239+
240+
static Set<Object> getFieldValuesFromSource(SearchResponse response, String path) {
241+
var values = new HashSet<>();
242+
SearchHit[] hits = response.getHits().getHits();
243+
for (SearchHit hit : hits) {
244+
var sourceAsMap = hit.getSourceAsMap();
245+
Object value = ObjectPath.eval(path, sourceAsMap);
246+
values.add(value);
247+
}
248+
return values;
249+
}
250+
251+
Map<String, Object> getMapping() {
252+
return indicesAdmin().prepareGetMappings(TEST_REQUEST_TIMEOUT, INDEX).get().mappings().get(INDEX).sourceAsMap();
253+
}
254+
255+
private void indexDoc(String doc) {
256+
BulkRequest bulkRequest = new BulkRequest();
257+
var indexRequest = new IndexRequest(INDEX).opType(DocWriteRequest.OpType.CREATE).source(doc, XContentType.JSON);
258+
bulkRequest.add(indexRequest);
259+
BulkResponse bulkResponse = client().bulk(bulkRequest).actionGet();
260+
assertFalse(bulkResponse.hasFailures());
261+
safeGet(indicesAdmin().refresh(new RefreshRequest(INDEX).indicesOptions(IndicesOptions.lenientExpandOpenHidden())));
262+
}
263+
}

0 commit comments

Comments
 (0)