All Products
Search
Document Center

Tablestore:Scenarios

Last Updated:Apr 10, 2024

The secondary index feature allows you to create an index on the specified columns. Data in the generated index table is sorted based on the specified index key columns. All data written to the data table is automatically synchronized to the index table. After you write data to the data table, you can query the data from the index table that is created for the data table. This improves the query efficiency.

Sample scenario

This topic describes how to use the secondary index feature to query phone records. When a phone call is complete, the information about the phone call is recorded in a data table.

The following section describes the primary key columns and predefined columns of the data table:

  • The CellNumber and StartTime columns are used as the primary key columns of the data table. Each value in the CellNumber column indicates a calling number, and each value in the StartTime column indicates the start time of a phone call.

  • The CalledNumber, Duration, and BaseStationNumber columns are used as the predefined columns of the data table. Each value in the CalledNumber column indicates a called number, each value in the Duration column indicates the duration of a phone call, and each value in the BaseStationNumber column indicates a base station number.

The following table provides the sample data in the data table. The Wide Column model of Tablestore sorts rows in a data table based on their primary keys and provides the GetRange operation to query data.

CellNumber

StartTime (UNIX timestamp)

CalledNumber

Duration

BaseStationNumber

123456

1532574644

654321

60

1

234567

1532574714

765432

10

1

234567

1532574734

123456

20

3

345678

1532574795

123456

5

2

345678

1532574861

123456

100

2

456789

1532584054

345678

200

3

You can use data tables, global secondary indexes, or local secondary indexes to perform the following queries based on your business requirements:

  • Query the rows in which the value of the CellNumber column is 234567.

  • Query the rows in which the value of the CalledNumber column is 123456.

  • Query the rows in which the value of the BaseStationNumber column is 2 and the value of the StartTime column is 1532574740.

  • Query the values of the Duration column for the rows in which the value of the BaseStationNumber column is 3 and the value of the StartTime column ranges from 1532574861 to 1532584054.

  • Query the total, average, maximum, and minimum call duration of all phone calls forwarded by the base station 3. The value of the StartTime column ranges from 1532574861 to 1532584054.

  • Query the rows in which the value of the CellNumber column is 456789 and the value of the CalledNumber column is 345678.

Data queries

You can select an appropriate method to query data based on your query requirements.

Methods

The following table describes how to implement different queries.

Important
  • For more information about the sample code that is used to create a data table and a secondary index table, see Appendix: Sample code for creating the data table and secondary index tables.

    After you create a data table, you need to write sample data to the data table. The data in the data table is automatically synchronized to the index table created for the data table. For more information about how to write data, see Write data.

  • Tablestore automatically adds the primary key columns of a data table that are not specified as index key columns to the index table created for the data table. The primary key columns of the data table and the index key column are used as the primary key columns of the index table.

  • If you use a local secondary index, the first primary key column of the index table must be the same as the first primary key column of the data table for which the index table is created.

Query

Method

Query the rows in which the value of the CellNumber column is 234567

You can directly call the GetRange operation to scan the data table.

Query the rows in which the value of the CalledNumber column is 123456

You can create an index table based on the CalledNumber column and call the GetRange operation to scan the index table.

Query the rows in which the value of the BaseStationNumber column is 2 and the value of the StartTime column is 1532574740

You can create an index table based on the BaseStationNumber and StartTime columns and call the GetRange operation to scan the index table whose index type is global secondary index.

Query the values of the Duration column for the rows in which the value of the BaseStationNumber column is 3 and the value of the StartTime column ranges from 1532574861 to 1532584054

You can create an index table based on the BaseStationNumber and StartTime columns, specify that only the values of the Duration column are returned, and then call the GetRange operation to scan the index table whose index type is global secondary index.

You can manually query the values of the Duration column from the data table or specify the Duration column as an attribute column of the index table.

To query the total, average, maximum, and minimum call duration of all phone calls that are forwarded by the base station 3 and whose start time ranges from 1532574861 to 1532584054, you can also use this query method. Then, you can perform aggregation on the Duration column to obtain the final results.

Note

You can also execute SQL statements to obtain the results without client-side calculations. For more information, see Query data.

Query the rows in which the value of the CellNumber column is 456789 and the value of the CalledNumber column is 345678

You can create an index table based on the CellNumber and CalledNumber columns, specify the Duration and BaseStationNumber columns as the attribute columns of the index table, and then call the GetRange operation to scan the index table whose index type is local secondary index.

Query the rows in which the value of the CellNumber column is 234567

The CellNumber column is a primary key column of the data table. Therefore, you can directly call the GetRange operation to scan the data table to obtain the rows that meet the query condition.

In this case, you can set both the maximum and minimum values of the CellNumber column to 234567, and set the minimum value and maximum value of the StartTime column to 0 and INT_MAX.

Sample code:

// The cellNumber parameter in the sample code corresponds to the CellNumber column in the data table. 
private static void getRangeFromMainTable(SyncClient client, long cellNumber){
    RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TABLE_NAME);

    // Specify the start primary key column. 
    PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(0));
    rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());

    // Specify the end primary key column. 
    PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
    rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());

    rangeRowQueryCriteria.setMaxVersions(1);

    String strNum = String.format("%d", cellNumber);
    System.out.println("The cell number" + strNum + "makes the following calls:");
    while (true) {
        GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
        for (Row row : getRangeResponse.getRows()) {
            System.out.println(row);
        }

        // If the value of nextStartPrimaryKey is not null, continue to read data. 
        if (getRangeResponse.getNextStartPrimaryKey() != null) {
            rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
        } else {
            break;
        }
    }
}

Query the rows in which the value of the CalledNumber column is 123456

This query specifies a condition based on the value of the CalledNumber column, which is a predefined column of the data table. You cannot directly query from the data table. You need to create an index table named IndexOnBeCalledNumber based on the CalledNumber column, specify the CalledNumber column as a primary key column of the index table, and then call the GetRange operation to scan the index table to obtain the rows that meet the query condition.

The first primary key column of the index table is different from the first primary key column of the data table. Therefore, the index type is global secondary index.

Data in the index table

The following table describes the data in the IndexOnBeCalledNumber index table.

PK0

PK1

PK2

CalledNumber

CellNumber

StartTime

123456

234567

1532574734

123456

345678

1532574795

123456

345678

1532574861

345678

456789

1532584054

654321

123456

1532574644

765432

234567

1532574714

When you call the GetRange operation to query data, you can set both the maximum and minimum values of the CalledNumber column to 123456, set the minimum values of the CellNumber and StartTime columns to INT_MIN, and set the maximum values of the CellNumber and StartTime columns to INT_MAX. Sample code:

// The calledNumber parameter in the sample code corresponds to the CalledNumber column in the index table. 
private static void getRangeFromIndexTable(SyncClient client, long calledNumber) {
    RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX0_NAME);

    // Specify the start primary key column. 
    PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromLong(calledNumber));
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MIN);
    rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());

    // Specify the end primary key column. 
    PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromLong(calledNumber));
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
    rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());

    rangeRowQueryCriteria.setMaxVersions(1);

    String strNum = String.format("%d", calledNumber);
    System.out.println("The cell number" + strNum + "is called by the following numbers:");
    while (true) {
        GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
        for (Row row : getRangeResponse.getRows()) {
            System.out.println(row);
        }

        // If the value of nextStartPrimaryKey is not null, continue to read data. 
        if (getRangeResponse.getNextStartPrimaryKey() != null) {
            rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
        } else {
            break;
        }
    }
}

Query the rows in which the value of the BaseStationNumber column is 2 and the value of the StartTime column is 1532574740

This query specifies conditions based on the values of the BaseStationNumber and StartTime columns. The BaseStationNumber column is a predefined column of the data table. You cannot directly query from the data table. You need to create an index table named IndexOnBaseStation1 based on the BaseStationNumber and StartTime columns, specify the BaseStationNumber and StartTime columns as the primary key columns of the index table, and then call the GetRange operation to scan the index table to obtain the rows that meet the query conditions.

The first primary key column of the index table is different from the first primary key column of the data table. Therefore, the index type is global secondary index.

Data in the index table

The following table describes the data in the IndexOnBaseStation1 index table.

PK0

PK1

PK2

BaseStationNumber

StartTime

CellNumber

1

1532574644

123456

1

1532574714

234567

2

1532574795

345678

2

1532574861

345678

3

1532574734

234567

3

1532584054

456789

When you call the GetRange operation to query data, you can set both the maximum and minimum values of the BaseStationNumber column to 2, set the minimum value and maximum value of the StartTime column to 1532574740 and INT_MAX, and set the minimum value and maximum value of the CellNumber column to INT_MIN and INT_MAX. Sample code:

// The baseStationNumber parameter in the sample code corresponds to the BaseStationNumber column in the index table. 
// The startTime parameter in the sample code corresponds to the StartTime column in the index table. The startTime parameter specifies the beginning of the time range to query. 
private static void getRangeFromIndexTable(SyncClient client,
                                           long baseStationNumber,
                                           long startTime) {
    RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX1_NAME);

    // Specify the start primary key column. 
    PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(startTime));
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
    rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());

    // Specify the end primary key column. 
    PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
    rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());

    rangeRowQueryCriteria.setMaxVersions(1);

    String strBaseStationNum = String.format("%d", baseStationNumber);
    String strStartTime = String.format("%d", startTime);
    System.out.println("All called numbers forwarded by the base station" + strBaseStationNum + "that start from" + strStartTime + "are listed:");
    while (true) {
        GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
        for (Row row : getRangeResponse.getRows()) {
            System.out.println(row);
        }

        // If the value of nextStartPrimaryKey is not null, continue to read data. 
        if (getRangeResponse.getNextStartPrimaryKey() != null) {
            rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
        } else {
            break;
        }
    }
}

Query the values of the Duration column for the rows in which the value of the BaseStationNumber column is 3 and the value of the StartTime column ranges from 1532574861 to 1532584054

This query specifies conditions based on the values of the BaseStationNumber and StartTime columns and returns only the values of the Duration column. You can query data from the IndexOnBaseStation1 index table that is created in the Query the rows in which the value of the BaseStationNumber column is 2 and the value of the StartTime column is 1532574740 section to obtain the primary key of the rows that meet the query conditions. Then, query data from the data table based on the primary key to obtain the values of the Duration column.

The first primary key column of the index table is different from the first primary key column of the data table. Therefore, the index type is global secondary index.

Sample code:

// The baseStationNumber parameter in the sample code corresponds to the BaseStationNumber column in the index table. 
// The startTime and endTime parameters in the sample code correspond to the StartTime column in the index table. The startTime and endTime parameters specify the beginning and ending of the time range to query. 
// The DEFINED_COL_NAME_2 parameter in the sample code corresponds to the Duration column in the data table. 
private static void getRowFromIndexAndMainTable(SyncClient client,
 long baseStationNumber,
 long startTime,
 long endTime) {
 RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX1_NAME);

 // Specify the start primary key column. 
 PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
 startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
 startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(startTime));
 startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
 rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());

 // Specify the end primary key column. 
 PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
 endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
 endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(endTime));
 endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
 rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());

 rangeRowQueryCriteria.setMaxVersions(1);

 String strBaseStationNum = String.format("%d", baseStationNumber);
 String strStartTime = String.format("%d", startTime);
 String strEndTime = String.format("%d", endTime);

 System.out.println("The duration of calls forwarded by the base station" + strBaseStationNum + "from" + strStartTime + "to" + strEndTime + "is listed:");
 while (true) {
 GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
 for (Row row : getRangeResponse.getRows()) {
 PrimaryKey curIndexPrimaryKey = row.getPrimaryKey();
 // Specify primary key columns for the data table. 
 PrimaryKeyColumn mainCalledNumber = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_1);
 PrimaryKeyColumn callStartTime = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
 PrimaryKeyBuilder mainTablePKBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
 mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, mainCalledNumber.getValue());
 mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, callStartTime.getValue());
 PrimaryKey mainTablePK = mainTablePKBuilder.build(); 

 // Query data from the data table. 
 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, mainTablePK);
 // Read data from the Duration column in the data table. 
 criteria.addColumnsToGet(DEFINED_COL_NAME_2); 
 // Set the maxVersions parameter to 1 to read the latest version of data. 
 criteria.setMaxVersions(1);
 GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
 Row mainTableRow = getRowResponse.getRow();

 System.out.println(mainTableRow);
 }

 // If the value of nextStartPrimaryKey is not null, continue to read data. 
 if (getRangeResponse.getNextStartPrimaryKey() != null) {
 rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
 } else {
 break;
 }
 }
}

To improve query efficiency, you can create an index table named IndexOnBaseStation2 based on the BaseStationNumber and StartTime columns and specify the Duration column as an attribute column of the index table. Then, call the GetRange operation to scan the index table to obtain the rows that meet the query conditions.

Data in the index table

The following table describes the data in the IndexOnBaseStation2 index table.

PK0

PK1

PK2

Defined0

BaseStationNumber

StartTime

CellNumber

Duration

1

1532574644

123456

60

1

1532574714

234567

10

2

1532574795

345678

5

2

1532574861

345678

100

3

1532574734

234567

20

3

1532584054

456789

200

When you call the GetRange operation to query data, you can set both the maximum and minimum values of the BaseStationNumber column to 3, set the minimum value and maximum value of the StartTime column to 1532574861 and 1532584054, and set the minimum value and maximum value of the CellNumber column to INT_MIN and INT_MAX.

Sample code:

// The baseStationNumber parameter in the sample code corresponds to the BaseStationNumber column in the index table. 
// The startTime and endTime parameters in the sample code correspond to the StartTime column in the index table. The startTime and endTime parameters specify the beginning and ending of the time range to query. 
// The DEFINED_COL_NAME_2 parameter in the sample code corresponds to the Duration column in the index table. 
private static void getRangeFromIndexTable(SyncClient client,
 long baseStationNumber,
 long startTime,
 long endTime) {
 RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX2_NAME);

 // Specify the start primary key column. 
 PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
 startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
 startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(startTime));
 startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
 rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());

 // Specify the end primary key column. 
 PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
 endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
 endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(endTime));
 endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
 rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());

 // Specify the column from which data is read. 
 rangeRowQueryCriteria.addColumnsToGet(DEFINED_COL_NAME_2);

 rangeRowQueryCriteria.setMaxVersions(1);

 String strBaseStationNum = String.format("%d", baseStationNumber);
 String strStartTime = String.format("%d", startTime);
 String strEndTime = String.format("%d", endTime);

 System.out.println("The duration of calls forwarded by the base station" + strBaseStationNum + "from" + strStartTime + "to" + strEndTime + "is listed:");
 while (true) {
 GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
 for (Row row : getRangeResponse.getRows()) {
 System.out.println(row);
 }

 // If the value of nextStartPrimaryKey is not null, continue to read data. 
 if (getRangeResponse.getNextStartPrimaryKey() != null) {
 rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
 } else {
 break;
 }
 }
}

Query the rows in which the value of the CellNumber column is 456789 and the value of the CalledNumber column is 345678

This query specifies conditions based on the values of the CellNumber and CalledNumber columns. The CalledNumber column is a predefined column of the data table. You cannot directly query from the data table. You need to create an index table named LocalIndexOnBeCalledNumber based on the CellNumber and CalledNumber columns, specify the CellNumber and CalledNumber columns as the primary key columns of the index table, and specify the Duration and BaseStationNumber columns as the attribute columns of the index table. Then, call the GetRange operation to scan the index table to obtain the rows that meet the query conditions.

The first primary key column of the index table is the same as the first primary key column of the data table. Therefore, the index type is local secondary index.

Data in the index table

The following table describes the data in the LocalIndexOnBeCalledNumber index table.

PK0

Defined0

PK1

Defined1

Defined2

CellNumber

CalledNumber

StartTime (UNIX timestamp)

Duration

BaseStationNumber

123456

654321

1532574644

60

1

234567

123456

1532574734

20

3

234567

765432

1532574714

10

1

345678

123456

1532574795

5

2

345678

123456

1532574861

100

2

456789

345678

1532584054

200

3

When you call the GetRange operation to query data, you can set both the maximum and minimum values of the CellNumber column to 456789, set both the maximum and minimum values of the CalledNumber column to 345678, and set the minimum value and maximum value of the StartTime column to 0 and INT_MAX. Sample code:

// The cellNumber and calledNumber parameters in the sample code correspond to the CellNumber and CalledNumber columns in the index table. 
private static void getRangeFromLocalIndex(SyncClient client, long cellNumber, long calledNumber){

    RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX3_NAME);

    // Specify the start primary key column. 
    PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
    startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromLong(calledNumber));
    startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(0));
    rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());

    // Specify the end primary key column. 
    PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
    endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromLong(calledNumber));
    endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
    rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());

    rangeRowQueryCriteria.setMaxVersions(1);

    String strNum = String.format("%d", cellNumber);
    String strCalledNum = String.format("%d", calledNumber);
    System.out.println("All records of phone calls between the calling number" + strNum + "and the called number" +strCalledNum+ "are listed:");
    while (true) {
        GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
        for (Row row : getRangeResponse.getRows()) {
            System.out.println(row);
        }

        // If the value of nextStartPrimaryKey is not null, continue to read data. 
        if (getRangeResponse.getNextStartPrimaryKey() != null) {
            rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
        } else {
            break;
        }
    }
}

Appendix: Sample code for creating the data table and secondary index tables

The following sample code shows how to create the data table and secondary index tables used in this topic. The index types include local secondary index and global secondary index.

private static final String TABLE_NAME = "CallRecordTable";
private static final String INDEX0_NAME = "IndexOnBeCalledNumber";
private static final String INDEX1_NAME = "IndexOnBaseStation1";
private static final String INDEX2_NAME = "IndexOnBaseStation2";
private static final String INDEX3_NAME = "LocalIndexOnBeCalledNumber";
private static final String PRIMARY_KEY_NAME_1 = "CellNumber";
private static final String PRIMARY_KEY_NAME_2 = "StartTime";
private static final String DEFINED_COL_NAME_1 = "CalledNumber";
private static final String DEFINED_COL_NAME_2 = "Duration";
private static final String DEFINED_COL_NAME_3 = "BaseStationNumber";

private static void createTable(SyncClient client) {
    TableMeta tableMeta = new TableMeta(TABLE_NAME);
    tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(PRIMARY_KEY_NAME_1, PrimaryKeyType.INTEGER));
    tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(PRIMARY_KEY_NAME_2, PrimaryKeyType.INTEGER));
    tableMeta.addDefinedColumn(new DefinedColumnSchema(DEFINED_COL_NAME_1, DefinedColumnType.INTEGER));
    tableMeta.addDefinedColumn(new DefinedColumnSchema(DEFINED_COL_NAME_2, DefinedColumnType.INTEGER));
    tableMeta.addDefinedColumn(new DefinedColumnSchema(DEFINED_COL_NAME_3, DefinedColumnType.INTEGER));
    // Specify the Time to Live (TTL) of data. Unit: seconds. The value -1 indicates that the data never expires. You must set the value of timeToLive to -1 if a data table has one or more index tables. 
    int timeToLive = -1; 
    // Specify the maximum number of data versions that can be retained. You must set the value of maxVersions to 1 if a data table has one or more index tables. 
    int maxVersions = 1; 

    TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);

    ArrayList<IndexMeta> indexMetas = new ArrayList<IndexMeta>();
 
    IndexMeta indexMeta0 = new IndexMeta(INDEX0_NAME);
    indexMeta0.addPrimaryKeyColumn(DEFINED_COL_NAME_1);
    indexMetas.add(indexMeta0);
   
    IndexMeta indexMeta1 = new IndexMeta(INDEX1_NAME);
    indexMeta1.addPrimaryKeyColumn(DEFINED_COL_NAME_3);
    indexMeta1.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
    indexMetas.add(indexMeta1);
   
    IndexMeta indexMeta2 = new IndexMeta(INDEX2_NAME);
    indexMeta2.addPrimaryKeyColumn(DEFINED_COL_NAME_3);
    indexMeta2.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
    indexMeta2.addDefinedColumn(DEFINED_COL_NAME_2);
    indexMetas.add(indexMeta2);
 
    IndexMeta indexMeta3 = new IndexMeta(INDEX3_NAME);
    indexMeta3.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1);
    indexMeta3.addPrimaryKeyColumn(DEFINED_COL_NAME_1);
    indexMeta3.addDefinedColumn(DEFINED_COL_NAME_2);
    indexMeta3.addDefinedColumn(DEFINED_COL_NAME_3);
    // Set the value of indexUpdateMode to IUM_SYNC_INDEX. 
    indexMeta3.setIndexUpdateMode(IUM_SYNC_INDEX);
    // Set the value of indexType to IT_LOCAL_INDEX. 
    indexMeta3.setIndexType(IT_LOCAL_INDEX);
    indexMetas.add(indexMeta3);
 
    CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions, indexMetas);

    client.createTable(request);
}