use of org.opensearch.search.sort.NestedSortBuilder in project OpenSearch by opensearch-project.
the class SimpleNestedIT method testSortNestedWithNestedFilter.
public void testSortNestedWithNestedFilter() throws Exception {
assertAcked(prepareCreate("test").addMapping("type1", XContentFactory.jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("grand_parent_values").field("type", "long").endObject().startObject("parent").field("type", "nested").startObject("properties").startObject("parent_values").field("type", "long").endObject().startObject("child").field("type", "nested").startObject("properties").startObject("child_values").field("type", "long").endObject().endObject().endObject().endObject().endObject().endObject().endObject().endObject()));
ensureGreen();
// sum: 11
client().prepareIndex("test").setId("1").setSource(jsonBuilder().startObject().field("grand_parent_values", 1L).startArray("parent").startObject().field("filter", false).field("parent_values", 1L).startArray("child").startObject().field("filter", true).field("child_values", 1L).startObject("child_obj").field("value", 1L).endObject().endObject().startObject().field("filter", false).field("child_values", 6L).endObject().endArray().endObject().startObject().field("filter", true).field("parent_values", 2L).startArray("child").startObject().field("filter", false).field("child_values", -1L).endObject().startObject().field("filter", false).field("child_values", 5L).endObject().endArray().endObject().endArray().endObject()).get();
// sum: 7
client().prepareIndex("test").setId("2").setSource(jsonBuilder().startObject().field("grand_parent_values", 2L).startArray("parent").startObject().field("filter", false).field("parent_values", 2L).startArray("child").startObject().field("filter", true).field("child_values", 2L).startObject("child_obj").field("value", 2L).endObject().endObject().startObject().field("filter", false).field("child_values", 4L).endObject().endArray().endObject().startObject().field("parent_values", 3L).field("filter", true).startArray("child").startObject().field("child_values", -2L).field("filter", false).endObject().startObject().field("filter", false).field("child_values", 3L).endObject().endArray().endObject().endArray().endObject()).get();
// sum: 2
client().prepareIndex("test").setId("3").setSource(jsonBuilder().startObject().field("grand_parent_values", 3L).startArray("parent").startObject().field("parent_values", 3L).field("filter", false).startArray("child").startObject().field("filter", true).field("child_values", 3L).startObject("child_obj").field("value", 3L).endObject().endObject().startObject().field("filter", false).field("child_values", 1L).endObject().endArray().endObject().startObject().field("parent_values", 4L).field("filter", true).startArray("child").startObject().field("filter", false).field("child_values", -3L).endObject().startObject().field("filter", false).field("child_values", 1L).endObject().endArray().endObject().endArray().endObject()).get();
refresh();
// Without nested filter
SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("-3"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("-2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("-1"));
// With nested filter
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").setNestedFilter(QueryBuilders.termQuery("parent.child.filter", true)).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
// Nested path should be automatically detected, expect same results as above search request
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").setNestedFilter(QueryBuilders.termQuery("parent.child.filter", true)).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.parent_values").setNestedPath("parent.child").setNestedFilter(QueryBuilders.termQuery("parent.filter", false)).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedSort(new NestedSortBuilder("parent").setFilter(QueryBuilders.termQuery("parent.filter", false)).setNestedSort(new NestedSortBuilder("parent.child"))).sortMode(SortMode.MAX).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("4"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("6"));
// Check if closest nested type is resolved
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_obj.value").setNestedPath("parent.child").setNestedFilter(QueryBuilders.termQuery("parent.child.filter", true)).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
// Sort mode: sum
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").sortMode(SortMode.SUM).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("7"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("11"));
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").sortMode(SortMode.SUM).order(SortOrder.DESC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("11"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("7"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("2"));
// Sort mode: sum with filter
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").setNestedFilter(QueryBuilders.termQuery("parent.child.filter", true)).sortMode(SortMode.SUM).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
// Sort mode: avg
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").sortMode(SortMode.AVG).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").sortMode(SortMode.AVG).order(SortOrder.DESC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("1"));
// Sort mode: avg with filter
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("parent.child.child_values").setNestedPath("parent.child").setNestedFilter(QueryBuilders.termQuery("parent.child.filter", true)).sortMode(SortMode.AVG).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 3);
assertThat(searchResponse.getHits().getHits().length, equalTo(3));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[2].getId(), equalTo("3"));
assertThat(searchResponse.getHits().getHits()[2].getSortValues()[0].toString(), equalTo("3"));
}
use of org.opensearch.search.sort.NestedSortBuilder in project OpenSearch by opensearch-project.
the class SimpleNestedIT method testNestedSortWithMultiLevelFiltering.
public void testNestedSortWithMultiLevelFiltering() throws Exception {
assertAcked(prepareCreate("test").addMapping("type1", "{\n" + " \"type1\": {\n" + " \"properties\": {\n" + " \"acl\": {\n" + " \"type\": \"nested\",\n" + " \"properties\": {\n" + " \"access_id\": {\"type\": \"keyword\"},\n" + " \"operation\": {\n" + " \"type\": \"nested\",\n" + " \"properties\": {\n" + " \"name\": {\"type\": \"keyword\"},\n" + " \"user\": {\n" + " \"type\": \"nested\",\n" + " \"properties\": {\n" + " \"username\": {\"type\": \"keyword\"},\n" + " \"id\": {\"type\": \"integer\"}\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}", XContentType.JSON));
ensureGreen();
client().prepareIndex("test").setId("1").setSource("{\n" + " \"acl\": [\n" + " {\n" + " \"access_id\": 1,\n" + " \"operation\": [\n" + " {\n" + " \"name\": \"read\",\n" + " \"user\": [\n" + " {\"username\": \"grault\", \"id\": 1},\n" + " {\"username\": \"quxx\", \"id\": 2},\n" + " {\"username\": \"bar\", \"id\": 3}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"write\",\n" + " \"user\": [\n" + " {\"username\": \"quxx\", \"id\": 2},\n" + " {\"username\": \"bar\", \"id\": 3}\n" + " ]\n" + " }\n" + " ]\n" + " },\n" + " {\n" + " \"access_id\": 2,\n" + " \"operation\": [\n" + " {\n" + " \"name\": \"read\",\n" + " \"user\": [\n" + " {\"username\": \"baz\", \"id\": 4},\n" + " {\"username\": \"quxx\", \"id\": 2}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"write\",\n" + " \"user\": [\n" + " {\"username\": \"quxx\", \"id\": 2}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"execute\",\n" + " \"user\": [\n" + " {\"username\": \"quxx\", \"id\": 2}\n" + " ]\n" + " }\n" + " ]\n" + " }\n" + " ]\n" + "}", XContentType.JSON).get();
client().prepareIndex("test").setId("2").setSource("{\n" + " \"acl\": [\n" + " {\n" + " \"access_id\": 1,\n" + " \"operation\": [\n" + " {\n" + " \"name\": \"read\",\n" + " \"user\": [\n" + " {\"username\": \"grault\", \"id\": 1},\n" + " {\"username\": \"foo\", \"id\": 5}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"execute\",\n" + " \"user\": [\n" + " {\"username\": \"foo\", \"id\": 5}\n" + " ]\n" + " }\n" + " ]\n" + " },\n" + " {\n" + " \"access_id\": 3,\n" + " \"operation\": [\n" + " {\n" + " \"name\": \"read\",\n" + " \"user\": [\n" + " {\"username\": \"grault\", \"id\": 1}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"write\",\n" + " \"user\": [\n" + " {\"username\": \"grault\", \"id\": 1}\n" + " ]\n" + " },\n" + " {\n" + " \"name\": \"execute\",\n" + " \"user\": [\n" + " {\"username\": \"grault\", \"id\": 1}\n" + " ]\n" + " }\n" + " ]\n" + " }\n" + " ]\n" + "}", XContentType.JSON).get();
refresh();
// access id = 1, read, max value, asc, should use grault and quxx
SearchResponse searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("acl.operation.user.username").setNestedSort(new NestedSortBuilder("acl").setFilter(QueryBuilders.termQuery("acl.access_id", "1")).setNestedSort(new NestedSortBuilder("acl.operation").setFilter(QueryBuilders.termQuery("acl.operation.name", "read")).setNestedSort(new NestedSortBuilder("acl.operation.user")))).sortMode(SortMode.MAX).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 2);
assertThat(searchResponse.getHits().getHits().length, equalTo(2));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("grault"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("quxx"));
// access id = 1, read, min value, asc, should now use bar and foo
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("acl.operation.user.username").setNestedSort(new NestedSortBuilder("acl").setFilter(QueryBuilders.termQuery("acl.access_id", "1")).setNestedSort(new NestedSortBuilder("acl.operation").setFilter(QueryBuilders.termQuery("acl.operation.name", "read")).setNestedSort(new NestedSortBuilder("acl.operation.user")))).sortMode(SortMode.MIN).order(SortOrder.ASC)).get();
assertHitCount(searchResponse, 2);
assertThat(searchResponse.getHits().getHits().length, equalTo(2));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("bar"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("foo"));
// execute, by grault or foo, by user id, sort missing first
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("acl.operation.user.id").setNestedSort(new NestedSortBuilder("acl").setNestedSort(new NestedSortBuilder("acl.operation").setFilter(QueryBuilders.termQuery("acl.operation.name", "execute")).setNestedSort(new NestedSortBuilder("acl.operation.user").setFilter(QueryBuilders.termsQuery("acl.operation.user.username", "grault", "foo"))))).missing("_first").sortMode(SortMode.MIN).order(SortOrder.DESC)).get();
assertHitCount(searchResponse, 2);
assertThat(searchResponse.getHits().getHits().length, equalTo(2));
// missing first
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("1"));
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[1].getSortValues()[0].toString(), equalTo("1"));
// execute, by grault or foo, by username, sort missing last (default)
searchResponse = client().prepareSearch().setQuery(matchAllQuery()).addSort(SortBuilders.fieldSort("acl.operation.user.username").setNestedSort(new NestedSortBuilder("acl").setNestedSort(new NestedSortBuilder("acl.operation").setFilter(QueryBuilders.termQuery("acl.operation.name", "execute")).setNestedSort(new NestedSortBuilder("acl.operation.user").setFilter(QueryBuilders.termsQuery("acl.operation.user.username", "grault", "foo"))))).sortMode(SortMode.MIN).order(SortOrder.DESC)).get();
assertHitCount(searchResponse, 2);
assertThat(searchResponse.getHits().getHits().length, equalTo(2));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("foo"));
// missing last
assertThat(searchResponse.getHits().getHits()[1].getId(), equalTo("1"));
}
use of org.opensearch.search.sort.NestedSortBuilder in project OpenSearch by opensearch-project.
the class SimpleNestedIT method testLeakingSortValues.
// https://github.com/elastic/elasticsearch/issues/31554
public void testLeakingSortValues() throws Exception {
assertAcked(prepareCreate("test").setSettings(Settings.builder().put("number_of_shards", 1)).addMapping("test-type", "{\n" + " \"dynamic\": \"strict\",\n" + " \"properties\": {\n" + " \"nested1\": {\n" + " \"type\": \"nested\",\n" + " \"properties\": {\n" + " \"nested2\": {\n" + " \"type\": \"nested\",\n" + " \"properties\": {\n" + " \"nested2_keyword\": {\n" + " \"type\": \"keyword\"\n" + " },\n" + " \"sortVal\": {\n" + " \"type\": \"integer\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n" + " }\n", XContentType.JSON));
ensureGreen();
client().prepareIndex("test").setId("1").setSource("{\n" + " \"nested1\": [\n" + " {\n" + " \"nested2\": [\n" + " {\n" + " \"nested2_keyword\": \"nested2_bar\",\n" + " \"sortVal\": 1\n" + " }\n" + " ]\n" + " }\n" + " ]\n" + "}", XContentType.JSON).get();
client().prepareIndex("test").setId("2").setSource("{\n" + " \"nested1\": [\n" + " {\n" + " \"nested2\": [\n" + " {\n" + " \"nested2_keyword\": \"nested2_bar\",\n" + " \"sortVal\": 2\n" + " }\n" + " ]\n" + " } \n" + " ]\n" + "}", XContentType.JSON).get();
refresh();
SearchResponse searchResponse = client().prepareSearch().setQuery(termQuery("_id", 2)).addSort(SortBuilders.fieldSort("nested1.nested2.sortVal").setNestedSort(new NestedSortBuilder("nested1").setNestedSort(new NestedSortBuilder("nested1.nested2").setFilter(termQuery("nested1.nested2.nested2_keyword", "nested2_bar"))))).get();
assertHitCount(searchResponse, 1);
assertThat(searchResponse.getHits().getHits().length, equalTo(1));
assertThat(searchResponse.getHits().getHits()[0].getId(), equalTo("2"));
assertThat(searchResponse.getHits().getHits()[0].getSortValues()[0].toString(), equalTo("2"));
}
use of org.opensearch.search.sort.NestedSortBuilder in project OpenSearch by opensearch-project.
the class NestedSortingTests method testMultiLevelNestedSorting.
public void testMultiLevelNestedSorting() throws IOException {
XContentBuilder mapping = XContentFactory.jsonBuilder();
mapping.startObject();
{
mapping.startObject("_doc");
{
mapping.startObject("properties");
{
{
mapping.startObject("title");
mapping.field("type", "text");
mapping.endObject();
}
{
mapping.startObject("genre");
mapping.field("type", "keyword");
mapping.endObject();
}
{
mapping.startObject("chapters");
mapping.field("type", "nested");
{
mapping.startObject("properties");
{
mapping.startObject("title");
mapping.field("type", "text");
mapping.endObject();
}
{
mapping.startObject("read_time_seconds");
mapping.field("type", "integer");
mapping.endObject();
}
{
mapping.startObject("paragraphs");
mapping.field("type", "nested");
{
mapping.startObject("properties");
{
{
mapping.startObject("header");
mapping.field("type", "text");
mapping.endObject();
}
{
mapping.startObject("content");
mapping.field("type", "text");
mapping.endObject();
}
{
mapping.startObject("word_count");
mapping.field("type", "integer");
mapping.endObject();
}
}
mapping.endObject();
}
mapping.endObject();
}
mapping.endObject();
}
mapping.endObject();
}
}
mapping.endObject();
}
mapping.endObject();
}
mapping.endObject();
IndexService indexService = createIndex("nested_sorting", Settings.EMPTY, "_doc", mapping);
List<List<Document>> books = new ArrayList<>();
{
List<Document> book = new ArrayList<>();
Document document = new Document();
document.add(new TextField("chapters.paragraphs.header", "Paragraph 1", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 743));
document.add(new IntPoint("chapters.paragraphs.word_count", 743));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "chapter 3", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 400));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 400));
book.add(document);
document = new Document();
document.add(new TextField("chapters.paragraphs.header", "Paragraph 1", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 234));
document.add(new IntPoint("chapters.paragraphs.word_count", 234));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "chapter 2", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 200));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 200));
book.add(document);
document = new Document();
document.add(new TextField("chapters.paragraphs.header", "Paragraph 2", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 478));
document.add(new IntPoint("chapters.paragraphs.word_count", 478));
book.add(document);
document = new Document();
document.add(new TextField("chapters.paragraphs.header", "Paragraph 1", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 849));
document.add(new IntPoint("chapters.paragraphs.word_count", 849));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "chapter 1", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 1400));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 1400));
book.add(document);
document = new Document();
document.add(new StringField("genre", "science fiction", Field.Store.NO));
document.add(new StringField("_type", "_doc", Field.Store.NO));
document.add(new StringField("_id", "1", Field.Store.YES));
document.add(new NumericDocValuesField(PRIMARY_TERM_NAME, 0));
book.add(document);
books.add(book);
}
{
List<Document> book = new ArrayList<>();
Document document = new Document();
document.add(new TextField("chapters.paragraphs.header", "Introduction", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 76));
document.add(new IntPoint("chapters.paragraphs.word_count", 76));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "chapter 1", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 20));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 20));
book.add(document);
document = new Document();
document.add(new StringField("genre", "romance", Field.Store.NO));
document.add(new StringField("_type", "_doc", Field.Store.NO));
document.add(new StringField("_id", "2", Field.Store.YES));
document.add(new NumericDocValuesField(PRIMARY_TERM_NAME, 0));
book.add(document);
books.add(book);
}
{
List<Document> book = new ArrayList<>();
Document document = new Document();
document.add(new TextField("chapters.paragraphs.header", "A bad dream", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 976));
document.add(new IntPoint("chapters.paragraphs.word_count", 976));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "The beginning of the end", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 1200));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 1200));
book.add(document);
document = new Document();
document.add(new StringField("genre", "horror", Field.Store.NO));
document.add(new StringField("_type", "_doc", Field.Store.NO));
document.add(new StringField("_id", "3", Field.Store.YES));
document.add(new NumericDocValuesField(PRIMARY_TERM_NAME, 0));
book.add(document);
books.add(book);
}
{
List<Document> book = new ArrayList<>();
Document document = new Document();
document.add(new TextField("chapters.paragraphs.header", "macaroni", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 180));
document.add(new IntPoint("chapters.paragraphs.word_count", 180));
book.add(document);
document = new Document();
document.add(new TextField("chapters.paragraphs.header", "hamburger", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 150));
document.add(new IntPoint("chapters.paragraphs.word_count", 150));
book.add(document);
document = new Document();
document.add(new TextField("chapters.paragraphs.header", "tosti", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 120));
document.add(new IntPoint("chapters.paragraphs.word_count", 120));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "easy meals", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 800));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 800));
book.add(document);
document = new Document();
document.add(new TextField("chapters.paragraphs.header", "introduction", Field.Store.NO));
document.add(new StringField("_type", "__chapters.paragraphs", Field.Store.NO));
document.add(new TextField("chapters.paragraphs.text", "some text...", Field.Store.NO));
document.add(new SortedNumericDocValuesField("chapters.paragraphs.word_count", 87));
document.add(new IntPoint("chapters.paragraphs.word_count", 87));
book.add(document);
document = new Document();
document.add(new TextField("chapters.title", "introduction", Field.Store.NO));
document.add(new StringField("_type", "__chapters", Field.Store.NO));
document.add(new IntPoint("chapters.read_time_seconds", 10));
document.add(new NumericDocValuesField("chapters.read_time_seconds", 10));
book.add(document);
document = new Document();
document.add(new StringField("genre", "cooking", Field.Store.NO));
document.add(new StringField("_type", "_doc", Field.Store.NO));
document.add(new StringField("_id", "4", Field.Store.YES));
document.add(new NumericDocValuesField(PRIMARY_TERM_NAME, 0));
book.add(document);
books.add(book);
}
{
List<Document> book = new ArrayList<>();
Document document = new Document();
document.add(new StringField("genre", "unknown", Field.Store.NO));
document.add(new StringField("_type", "_doc", Field.Store.NO));
document.add(new StringField("_id", "5", Field.Store.YES));
document.add(new NumericDocValuesField(PRIMARY_TERM_NAME, 0));
book.add(document);
books.add(book);
}
Collections.shuffle(books, random());
for (List<Document> book : books) {
writer.addDocuments(book);
if (randomBoolean()) {
writer.commit();
}
}
DirectoryReader reader = DirectoryReader.open(writer);
reader = OpenSearchDirectoryReader.wrap(reader, new ShardId(indexService.index(), 0));
IndexSearcher searcher = new IndexSearcher(reader);
QueryShardContext queryShardContext = indexService.newQueryShardContext(0, searcher, () -> 0L, null);
FieldSortBuilder sortBuilder = new FieldSortBuilder("chapters.paragraphs.word_count");
sortBuilder.setNestedSort(new NestedSortBuilder("chapters").setNestedSort(new NestedSortBuilder("chapters.paragraphs")));
QueryBuilder queryBuilder = new MatchAllQueryBuilder();
TopFieldDocs topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(5L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(76L));
assertThat(searcher.doc(topFields.scoreDocs[1].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[1]).fields[0], equalTo(87L));
assertThat(searcher.doc(topFields.scoreDocs[2].doc).get("_id"), equalTo("1"));
assertThat(((FieldDoc) topFields.scoreDocs[2]).fields[0], equalTo(234L));
assertThat(searcher.doc(topFields.scoreDocs[3].doc).get("_id"), equalTo("3"));
assertThat(((FieldDoc) topFields.scoreDocs[3]).fields[0], equalTo(976L));
assertThat(searcher.doc(topFields.scoreDocs[4].doc).get("_id"), equalTo("5"));
assertThat(((FieldDoc) topFields.scoreDocs[4]).fields[0], equalTo(Long.MAX_VALUE));
// Specific genre
{
queryBuilder = new TermQueryBuilder("genre", "romance");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(76L));
queryBuilder = new TermQueryBuilder("genre", "science fiction");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("1"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(234L));
queryBuilder = new TermQueryBuilder("genre", "horror");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("3"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(976L));
queryBuilder = new TermQueryBuilder("genre", "cooking");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(87L));
}
// reverse sort order
{
sortBuilder.order(SortOrder.DESC);
queryBuilder = new MatchAllQueryBuilder();
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(5L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("3"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(976L));
assertThat(searcher.doc(topFields.scoreDocs[1].doc).get("_id"), equalTo("1"));
assertThat(((FieldDoc) topFields.scoreDocs[1]).fields[0], equalTo(849L));
assertThat(searcher.doc(topFields.scoreDocs[2].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[2]).fields[0], equalTo(180L));
assertThat(searcher.doc(topFields.scoreDocs[3].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[3]).fields[0], equalTo(76L));
assertThat(searcher.doc(topFields.scoreDocs[4].doc).get("_id"), equalTo("5"));
assertThat(((FieldDoc) topFields.scoreDocs[4]).fields[0], equalTo(Long.MIN_VALUE));
}
// Specific genre and reverse sort order
{
queryBuilder = new TermQueryBuilder("genre", "romance");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(76L));
queryBuilder = new TermQueryBuilder("genre", "science fiction");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("1"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(849L));
queryBuilder = new TermQueryBuilder("genre", "horror");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("3"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(976L));
queryBuilder = new TermQueryBuilder("genre", "cooking");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(180L));
}
// Nested filter + query
{
queryBuilder = new RangeQueryBuilder("chapters.read_time_seconds").to(50L);
sortBuilder = new FieldSortBuilder("chapters.paragraphs.word_count");
sortBuilder.setNestedSort(new NestedSortBuilder("chapters").setFilter(queryBuilder).setNestedSort(new NestedSortBuilder("chapters.paragraphs")));
topFields = search(new NestedQueryBuilder("chapters", queryBuilder, ScoreMode.None), sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(2L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(76L));
assertThat(searcher.doc(topFields.scoreDocs[1].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[1]).fields[0], equalTo(87L));
sortBuilder.order(SortOrder.DESC);
topFields = search(new NestedQueryBuilder("chapters", queryBuilder, ScoreMode.None), sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(2L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(87L));
assertThat(searcher.doc(topFields.scoreDocs[1].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[1]).fields[0], equalTo(76L));
}
// Multiple Nested filters + query
{
queryBuilder = new RangeQueryBuilder("chapters.read_time_seconds").to(50L);
sortBuilder = new FieldSortBuilder("chapters.paragraphs.word_count");
sortBuilder.setNestedSort(new NestedSortBuilder("chapters").setFilter(queryBuilder).setNestedSort(new NestedSortBuilder("chapters.paragraphs").setFilter(new RangeQueryBuilder("chapters.paragraphs.word_count").from(80L))));
topFields = search(new NestedQueryBuilder("chapters", queryBuilder, ScoreMode.None), sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(2L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(87L));
assertThat(searcher.doc(topFields.scoreDocs[1].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[1]).fields[0], equalTo(Long.MAX_VALUE));
sortBuilder.order(SortOrder.DESC);
topFields = search(new NestedQueryBuilder("chapters", queryBuilder, ScoreMode.None), sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(2L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(87L));
assertThat(searcher.doc(topFields.scoreDocs[1].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[1]).fields[0], equalTo(Long.MIN_VALUE));
}
// Nested filter + Specific genre
{
sortBuilder = new FieldSortBuilder("chapters.paragraphs.word_count");
sortBuilder.setNestedSort(new NestedSortBuilder("chapters").setFilter(new RangeQueryBuilder("chapters.read_time_seconds").to(50L)).setNestedSort(new NestedSortBuilder("chapters.paragraphs")));
queryBuilder = new TermQueryBuilder("genre", "romance");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("2"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(76L));
queryBuilder = new TermQueryBuilder("genre", "science fiction");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("1"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(Long.MAX_VALUE));
queryBuilder = new TermQueryBuilder("genre", "horror");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("3"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(Long.MAX_VALUE));
queryBuilder = new TermQueryBuilder("genre", "cooking");
topFields = search(queryBuilder, sortBuilder, queryShardContext, searcher);
assertThat(topFields.totalHits.value, equalTo(1L));
assertThat(searcher.doc(topFields.scoreDocs[0].doc).get("_id"), equalTo("4"));
assertThat(((FieldDoc) topFields.scoreDocs[0]).fields[0], equalTo(87L));
}
searcher.getIndexReader().close();
}
Aggregations