1515import org .elasticsearch .action .search .SearchShardsGroup ;
1616import org .elasticsearch .action .search .SearchShardsRequest ;
1717import org .elasticsearch .action .search .SearchShardsResponse ;
18+ import org .elasticsearch .action .search .SearchType ;
1819import org .elasticsearch .action .search .TransportSearchShardsAction ;
1920import org .elasticsearch .blobcache .shared .SharedBlobCacheService ;
2021import org .elasticsearch .cluster .metadata .DataStream ;
@@ -1100,6 +1101,119 @@ public void testCanMatchSkipsPartiallyMountedIndicesWhenFrozenNodesUnavailable()
11001101 }
11011102 }
11021103
1104+ public void testTimestampAsAlias () throws Exception {
1105+ doTestCoordRewriteWithAliasField ("@timestamp" );
1106+ }
1107+
1108+ public void testEventIngestedAsAlias () throws Exception {
1109+ doTestCoordRewriteWithAliasField ("event.ingested" );
1110+ }
1111+
1112+ private void doTestCoordRewriteWithAliasField (String aliasFieldName ) throws Exception {
1113+ internalCluster ().startMasterOnlyNode ();
1114+ internalCluster ().startCoordinatingOnlyNode (Settings .EMPTY );
1115+ final String dataNodeHoldingRegularIndex = internalCluster ().startDataOnlyNode ();
1116+ final String dataNodeHoldingSearchableSnapshot = internalCluster ().startDataOnlyNode ();
1117+
1118+ String timestampFieldName = randomAlphaOfLengthBetween (3 , 10 );
1119+ String [] indices = new String [] { "index-0001" , "index-0002" };
1120+ for (String index : indices ) {
1121+ Settings extraSettings = Settings .builder ()
1122+ .put (INDEX_ROUTING_REQUIRE_GROUP_SETTING .getConcreteSettingForNamespace ("_name" ).getKey (), dataNodeHoldingRegularIndex )
1123+ .build ();
1124+
1125+ assertAcked (
1126+ indicesAdmin ().prepareCreate (index )
1127+ .setMapping (
1128+ XContentFactory .jsonBuilder ()
1129+ .startObject ()
1130+ .startObject ("properties" )
1131+
1132+ .startObject (timestampFieldName )
1133+ .field ("type" , "date" )
1134+ .endObject ()
1135+
1136+ .startObject (aliasFieldName )
1137+ .field ("type" , "alias" )
1138+ .field ("path" , timestampFieldName )
1139+ .endObject ()
1140+
1141+ .endObject ()
1142+ .endObject ()
1143+ )
1144+ .setSettings (indexSettingsNoReplicas (1 ).put (INDEX_SOFT_DELETES_SETTING .getKey (), true ).put (extraSettings ))
1145+ );
1146+ }
1147+ ensureGreen (indices );
1148+
1149+ for (String index : indices ) {
1150+ final List <IndexRequestBuilder > indexRequestBuilders = new ArrayList <>();
1151+ for (int i = 0 ; i < 10 ; i ++) {
1152+ indexRequestBuilders .add (prepareIndex (index ).setSource (timestampFieldName , "2024-11-19T08:08:08Z" ));
1153+ }
1154+ indexRandom (true , false , indexRequestBuilders );
1155+
1156+ assertThat (
1157+ indicesAdmin ().prepareForceMerge (index ).setOnlyExpungeDeletes (true ).setFlush (true ).get ().getFailedShards (),
1158+ equalTo (0 )
1159+ );
1160+ refresh (index );
1161+ forceMerge ();
1162+ }
1163+
1164+ final String repositoryName = randomAlphaOfLength (10 ).toLowerCase (Locale .ROOT );
1165+ createRepository (repositoryName , "mock" );
1166+
1167+ final SnapshotId snapshotId = createSnapshot (repositoryName , "snapshot-1" , List .of (indices [0 ])).snapshotId ();
1168+ assertAcked (indicesAdmin ().prepareDelete (indices [0 ]));
1169+
1170+ // Block the repository for the node holding the searchable snapshot shards
1171+ // to delay its restore
1172+ blockDataNode (repositoryName , dataNodeHoldingSearchableSnapshot );
1173+
1174+ // Force the searchable snapshot to be allocated in a particular node
1175+ Settings restoredIndexSettings = Settings .builder ()
1176+ .put (INDEX_ROUTING_REQUIRE_GROUP_SETTING .getConcreteSettingForNamespace ("_name" ).getKey (), dataNodeHoldingSearchableSnapshot )
1177+ .build ();
1178+
1179+ String mountedIndex = indices [0 ] + "-mounted" ;
1180+ final MountSearchableSnapshotRequest mountRequest = new MountSearchableSnapshotRequest (
1181+ TEST_REQUEST_TIMEOUT ,
1182+ mountedIndex ,
1183+ repositoryName ,
1184+ snapshotId .getName (),
1185+ indices [0 ],
1186+ restoredIndexSettings ,
1187+ Strings .EMPTY_ARRAY ,
1188+ false ,
1189+ randomFrom (MountSearchableSnapshotRequest .Storage .values ())
1190+ );
1191+ client ().execute (MountSearchableSnapshotAction .INSTANCE , mountRequest ).actionGet ();
1192+
1193+ // Allow the searchable snapshots to be finally mounted
1194+ unblockNode (repositoryName , dataNodeHoldingSearchableSnapshot );
1195+ waitUntilRecoveryIsDone (mountedIndex );
1196+ ensureGreen (mountedIndex );
1197+
1198+ String [] fieldsToQuery = new String [] { timestampFieldName , aliasFieldName };
1199+ for (String fieldName : fieldsToQuery ) {
1200+ RangeQueryBuilder rangeQuery = QueryBuilders .rangeQuery (fieldName ).from ("2024-11-01T00:00:00.000000000Z" , true );
1201+ SearchRequest request = new SearchRequest ().searchType (SearchType .QUERY_THEN_FETCH )
1202+ .source (new SearchSourceBuilder ().query (rangeQuery ));
1203+ if (randomBoolean ()) {
1204+ // pre_filter_shard_size default to 1 because there are read-only indices in the mix. It does not hurt to force it though.
1205+ request .setPreFilterShardSize (1 );
1206+ }
1207+ assertResponse (client ().search (request ), searchResponse -> {
1208+ assertThat (searchResponse .getSuccessfulShards (), equalTo (2 ));
1209+ assertThat (searchResponse .getFailedShards (), equalTo (0 ));
1210+ assertThat (searchResponse .getSkippedShards (), equalTo (0 ));
1211+ assertThat (searchResponse .getTotalShards (), equalTo (2 ));
1212+ assertThat (searchResponse .getHits ().getTotalHits ().value , equalTo (20L ));
1213+ });
1214+ }
1215+ }
1216+
11031217 private void createIndexWithTimestampAndEventIngested (String indexName , int numShards , Settings extraSettings ) throws IOException {
11041218 assertAcked (
11051219 indicesAdmin ().prepareCreate (indexName )
@@ -1148,8 +1262,7 @@ private void createIndexWithOnlyOneTimestampField(String timestampField, String
11481262 ensureGreen (index );
11491263 }
11501264
1151- private void indexDocumentsWithOnlyOneTimestampField (String timestampField , String index , int docCount , String timestampTemplate )
1152- throws Exception {
1265+ private void indexDocumentsWithOnlyOneTimestampField (String timestampField , String index , int docCount , String timestampTemplate ) {
11531266 final List <IndexRequestBuilder > indexRequestBuilders = new ArrayList <>();
11541267 for (int i = 0 ; i < docCount ; i ++) {
11551268 indexRequestBuilders .add (
@@ -1173,8 +1286,7 @@ private void indexDocumentsWithOnlyOneTimestampField(String timestampField, Stri
11731286 forceMerge ();
11741287 }
11751288
1176- private void indexDocumentsWithTimestampAndEventIngestedDates (String indexName , int docCount , String timestampTemplate )
1177- throws Exception {
1289+ private void indexDocumentsWithTimestampAndEventIngestedDates (String indexName , int docCount , String timestampTemplate ) {
11781290
11791291 final List <IndexRequestBuilder > indexRequestBuilders = new ArrayList <>();
11801292 for (int i = 0 ; i < docCount ; i ++) {
@@ -1211,7 +1323,7 @@ private void indexDocumentsWithTimestampAndEventIngestedDates(String indexName,
12111323 forceMerge ();
12121324 }
12131325
1214- private IndexMetadata getIndexMetadata (String indexName ) {
1326+ private static IndexMetadata getIndexMetadata (String indexName ) {
12151327 return clusterAdmin ().prepareState (TEST_REQUEST_TIMEOUT )
12161328 .clear ()
12171329 .setMetadata (true )
@@ -1222,7 +1334,7 @@ private IndexMetadata getIndexMetadata(String indexName) {
12221334 .index (indexName );
12231335 }
12241336
1225- private void waitUntilRecoveryIsDone (String index ) throws Exception {
1337+ private static void waitUntilRecoveryIsDone (String index ) throws Exception {
12261338 assertBusy (() -> {
12271339 RecoveryResponse recoveryResponse = indicesAdmin ().prepareRecoveries (index ).get ();
12281340 assertThat (recoveryResponse .hasRecoveries (), equalTo (true ));
0 commit comments