1212import org .elasticsearch .action .search .ClosePointInTimeRequest ;
1313import org .elasticsearch .action .search .OpenPointInTimeRequest ;
1414import org .elasticsearch .action .search .OpenPointInTimeResponse ;
15+ import org .elasticsearch .action .search .SearchRequest ;
16+ import org .elasticsearch .action .search .SearchShardsRequest ;
1517import org .elasticsearch .action .search .SearchType ;
1618import org .elasticsearch .action .search .TransportClosePointInTimeAction ;
1719import org .elasticsearch .action .search .TransportOpenPointInTimeAction ;
20+ import org .elasticsearch .action .search .TransportSearchShardsAction ;
1821import org .elasticsearch .cluster .metadata .IndexMetadata ;
1922import org .elasticsearch .common .bytes .BytesReference ;
2023import org .elasticsearch .common .settings .Settings ;
2124import org .elasticsearch .core .TimeValue ;
25+ import org .elasticsearch .index .query .RangeQueryBuilder ;
2226import org .elasticsearch .plugins .Plugin ;
2327import org .elasticsearch .plugins .PluginsService ;
2428import org .elasticsearch .search .builder .PointInTimeBuilder ;
3943
4044public class SearchPhaseCoordinatorAPMMetricsTests extends ESSingleNodeTestCase {
4145 private static final String indexName = "test_coordinator_search_phase_metrics" ;
46+ private static final String secondIndexName = "test_coordinator_search_phase_metrics_2" ;
4247 private final int num_primaries = randomIntBetween (2 , 7 );
4348
49+ private static final String CAN_MATCH_SEARCH_PHASE_METRIC = "es.search_response.took_durations.can_match.histogram" ;
4450 private static final String DFS_QUERY_SEARCH_PHASE_METRIC = "es.search_response.took_durations.dfs_query.histogram" ;
4551 private static final String DFS_SEARCH_PHASE_METRIC = "es.search_response.took_durations.dfs.histogram" ;
4652 private static final String FETCH_SEARCH_PHASE_METRIC = "es.search_response.took_durations.fetch.histogram" ;
@@ -63,8 +69,22 @@ private void setUpIndex() throws Exception {
6369 );
6470 ensureGreen (indexName );
6571
66- prepareIndex (indexName ).setId ("1" ).setSource ("body" , "doc1" ).setRefreshPolicy (IMMEDIATE ).get ();
67- prepareIndex (indexName ).setId ("2" ).setSource ("body" , "doc2" ).setRefreshPolicy (IMMEDIATE ).get ();
72+ prepareIndex (indexName ).setId ("1" ).setSource ("body" , "doc1" , "@timestamp" , "2024-11-01" ).setRefreshPolicy (IMMEDIATE ).get ();
73+ prepareIndex (indexName ).setId ("2" ).setSource ("body" , "doc2" , "@timestamp" , "2024-12-01" ).setRefreshPolicy (IMMEDIATE ).get ();
74+ prepareIndex (indexName ).setId ("3" ).setSource ("body" , "doc3" , "@timestamp" , "2025-01-01" ).setRefreshPolicy (IMMEDIATE ).get ();
75+
76+ createIndex (
77+ secondIndexName ,
78+ Settings .builder ()
79+ .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , num_primaries )
80+ .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 0 )
81+ .build ()
82+ );
83+ ensureGreen (secondIndexName );
84+
85+ prepareIndex (secondIndexName ).setId ("4" ).setSource ("body" , "doc1" , "@timestamp" , "2025-11-01" ).setRefreshPolicy (IMMEDIATE ).get ();
86+ prepareIndex (secondIndexName ).setId ("5" ).setSource ("body" , "doc2" , "@timestamp" , "2025-12-01" ).setRefreshPolicy (IMMEDIATE ).get ();
87+ prepareIndex (secondIndexName ).setId ("6" ).setSource ("body" , "doc3" , "@timestamp" , "2026-01-01" ).setRefreshPolicy (IMMEDIATE ).get ();
6888 }
6989
7090 @ After
@@ -82,19 +102,24 @@ public void testSearchQueryThenFetch() {
82102 client ().prepareSearch (indexName ).setSearchType (SearchType .QUERY_THEN_FETCH ).setQuery (simpleQueryStringQuery ("doc1" )),
83103 "1"
84104 );
85- assertMeasurements (List .of (QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ));
105+ assertMeasurements (List .of (QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ), 1 );
106+ assertNotMeasured (
107+ List .of (CAN_MATCH_SEARCH_PHASE_METRIC , DFS_SEARCH_PHASE_METRIC , DFS_QUERY_SEARCH_PHASE_METRIC , OPEN_PIT_SEARCH_PHASE_METRIC )
108+ );
86109 }
87110
88111 public void testDfsSearch () {
89112 assertSearchHitsWithoutFailures (
90113 client ().prepareSearch (indexName ).setSearchType (SearchType .DFS_QUERY_THEN_FETCH ).setQuery (simpleQueryStringQuery ("doc1" )),
91114 "1"
92115 );
93- assertMeasurements (List .of (DFS_SEARCH_PHASE_METRIC , DFS_QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ));
116+ assertMeasurements (List .of (DFS_SEARCH_PHASE_METRIC , DFS_QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ), 1 );
117+ assertNotMeasured (List .of (CAN_MATCH_SEARCH_PHASE_METRIC , QUERY_SEARCH_PHASE_METRIC , OPEN_PIT_SEARCH_PHASE_METRIC ));
94118 }
95119
96120 public void testPointInTime () {
97121 OpenPointInTimeRequest request = new OpenPointInTimeRequest (indexName ).keepAlive (TimeValue .timeValueMinutes (10 ));
122+ request .indexFilter (simpleQueryStringQuery ("doc1" ));
98123 OpenPointInTimeResponse response = client ().execute (TransportOpenPointInTimeAction .TYPE , request ).actionGet ();
99124 BytesReference pointInTimeId = response .getPointInTimeId ();
100125
@@ -106,12 +131,67 @@ public void testPointInTime() {
106131 .setQuery (simpleQueryStringQuery ("doc1" )),
107132 "1"
108133 );
109- assertMeasurements (List .of (OPEN_PIT_SEARCH_PHASE_METRIC , QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ));
134+ assertMeasurements (List .of (OPEN_PIT_SEARCH_PHASE_METRIC , QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ), 1 );
135+ assertNotMeasured (List .of (DFS_SEARCH_PHASE_METRIC , DFS_QUERY_SEARCH_PHASE_METRIC ));
136+ } finally {
137+ client ().execute (TransportClosePointInTimeAction .TYPE , new ClosePointInTimeRequest (pointInTimeId )).actionGet ();
138+ }
139+ }
140+
141+ public void testPointInTimeWithPreFiltering () {
142+ OpenPointInTimeRequest request = new OpenPointInTimeRequest (indexName , secondIndexName ).keepAlive (TimeValue .timeValueMinutes (10 ));
143+ request .indexFilter (new RangeQueryBuilder ("@timestamp" ).gte ("2025-07-01" ));
144+ OpenPointInTimeResponse response = client ().execute (TransportOpenPointInTimeAction .TYPE , request ).actionGet ();
145+ BytesReference pointInTimeId = response .getPointInTimeId ();
146+
147+ try {
148+ assertSearchHitsWithoutFailures (
149+ client ().prepareSearch ()
150+ .setPointInTime (new PointInTimeBuilder (pointInTimeId ))
151+ .setSize (1 )
152+ .setPreFilterShardSize (1 )
153+ .setQuery (simpleQueryStringQuery ("doc3" )),
154+ "6"
155+ );
156+ assertMeasurements (List .of (OPEN_PIT_SEARCH_PHASE_METRIC , QUERY_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC ), 1 );
157+ assertMeasurements (
158+ List .of (CAN_MATCH_SEARCH_PHASE_METRIC ),
159+ 2 // one during open PIT, one during can-match phase of search
160+ );
161+ assertNotMeasured (List .of (DFS_SEARCH_PHASE_METRIC , DFS_QUERY_SEARCH_PHASE_METRIC ));
110162 } finally {
111163 client ().execute (TransportClosePointInTimeAction .TYPE , new ClosePointInTimeRequest (pointInTimeId )).actionGet ();
112164 }
113165 }
114166
167+ public void testCanMatchSearch () {
168+ assertSearchHitsWithoutFailures (
169+ client ().prepareSearch (indexName )
170+ .setSearchType (SearchType .QUERY_THEN_FETCH )
171+ .setPreFilterShardSize (1 )
172+ .setQuery (simpleQueryStringQuery ("doc1" )),
173+ "1"
174+ );
175+
176+ assertMeasurements (List .of (CAN_MATCH_SEARCH_PHASE_METRIC , FETCH_SEARCH_PHASE_METRIC , QUERY_SEARCH_PHASE_METRIC ), 1 );
177+ assertNotMeasured (List .of (DFS_SEARCH_PHASE_METRIC , DFS_QUERY_SEARCH_PHASE_METRIC , OPEN_PIT_SEARCH_PHASE_METRIC ));
178+ }
179+
180+ public void testSearchShards () {
181+ var request = new SearchShardsRequest (
182+ new String [] { indexName },
183+ SearchRequest .DEFAULT_INDICES_OPTIONS ,
184+ simpleQueryStringQuery ("doc1" ),
185+ null ,
186+ null ,
187+ randomBoolean (),
188+ randomBoolean () ? null : randomAlphaOfLength (10 )
189+ );
190+ var resp = client ().execute (TransportSearchShardsAction .TYPE , request ).actionGet ();
191+ assertThat (resp .getGroups (), hasSize (num_primaries ));
192+ assertMeasurements (List .of (CAN_MATCH_SEARCH_PHASE_METRIC ), 1 );
193+ }
194+
115195 private void resetMeter () {
116196 getTestTelemetryPlugin ().resetMeter ();
117197 }
@@ -120,11 +200,18 @@ private TestTelemetryPlugin getTestTelemetryPlugin() {
120200 return getInstanceFromNode (PluginsService .class ).filterPlugins (TestTelemetryPlugin .class ).toList ().get (0 );
121201 }
122202
123- private void assertMeasurements (Collection <String > metricNames ) {
203+ private void assertNotMeasured (Collection <String > metricNames ) {
204+ for (var metricName : metricNames ) {
205+ List <Measurement > measurements = getTestTelemetryPlugin ().getLongHistogramMeasurement (metricName );
206+ assertThat (metricName , measurements , hasSize (0 ));
207+ }
208+ }
209+
210+ private void assertMeasurements (Collection <String > metricNames , int numberOfMeasurements ) {
124211 for (var metricName : metricNames ) {
125212 List <Measurement > measurements = getTestTelemetryPlugin ().getLongHistogramMeasurement (metricName );
126- assertThat (measurements , hasSize (1 ));
127- assertThat (measurements .getFirst ().getLong (), greaterThanOrEqualTo (0L ));
213+ assertThat (metricName , measurements , hasSize (numberOfMeasurements ));
214+ assertThat (metricName , measurements .getFirst ().getLong (), greaterThanOrEqualTo (0L ));
128215 }
129216 }
130217}
0 commit comments