Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions client/src/com/aerospike/client/AerospikeClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -5452,6 +5452,9 @@ private String buildCreateIndexInfoCommand(
Version currentServerVersion = node.getServerVersion();
String createIndexCommand = currentServerVersion.isGreaterOrEqual(Version.SERVER_VERSION_8_1) ? "sindex-create:namespace=": "sindex-create:ns=";

// Server versions 8.1.3+ use the "integer" index type instead of "numeric".
indexType = resolveIndexType(indexType, currentServerVersion);

sb.append(createIndexCommand);
sb.append(namespace);

Expand Down Expand Up @@ -5511,6 +5514,23 @@ private String buildCreateIndexInfoCommand(
return sb.toString();
}

/**
* Map the requested index type to the type the target server understands.
* Server versions 8.1.3+ use "integer" instead of "numeric", so a NUMERIC
* request is upgraded to INTEGER on those servers and an INTEGER request is
* downgraded to NUMERIC on older servers. All other index types are returned
* unchanged. Package-private for unit testing.
*/
static IndexType resolveIndexType(IndexType indexType, Version serverVersion) {
if (indexType == IndexType.NUMERIC && serverVersion.isGreaterOrEqual(Version.SERVER_VERSION_8_1_3)) {
return IndexType.INTEGER;
}
if (indexType == IndexType.INTEGER && serverVersion.isLessThan(Version.SERVER_VERSION_8_1_3)) {
return IndexType.NUMERIC;
}
return indexType;
}

private String buildDropIndexInfoCommand(Node node, String namespace, String setName, String indexName) {
StringBuilder sb = new StringBuilder(500);
Version currentServerVersion = node.getServerVersion();
Expand Down
10 changes: 8 additions & 2 deletions client/src/com/aerospike/client/query/IndexType.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*/
public enum IndexType {
/**
* Number index.
* Number index. Use {@link #INTEGER} for server versions 8.1.3+.
*/
NUMERIC,

Expand All @@ -38,5 +38,11 @@ public enum IndexType {
/**
* 2-dimensional spherical geospatial index.
*/
GEO2DSPHERE;
GEO2DSPHERE,

/**
* Integer index. Requires server version 8.1.3+. Use {@link #NUMERIC} for
* server versions prior to 8.1.3.
*/
INTEGER;
}
1 change: 1 addition & 0 deletions client/src/com/aerospike/client/util/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
public final class Version implements Comparable<Version> {
public static final Version SERVER_VERSION_8_1 = new Version(8, 1, 0, 0);
public static final Version SERVER_VERSION_8_1_2 = new Version(8, 1, 2, 0);
public static final Version SERVER_VERSION_8_1_3 = new Version(8, 1, 3, 0);
public static final Version SERVER_VERSION_PSCAN = new Version(4, 9, 0, 3);
public static final Version SERVER_VERSION_QUERY_SHOW = new Version(5, 7, 0, 0);
public static final Version SERVER_VERSION_PQUERY_BATCH_ANY = new Version(6, 0, 0, 0);
Expand Down
83 changes: 83 additions & 0 deletions test/src/com/aerospike/client/AerospikeClientIndexTypeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2012-2026 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.aerospike.client;

import static org.junit.Assert.assertSame;

import org.junit.Test;

import com.aerospike.client.query.IndexType;
import com.aerospike.client.util.Version;

/**
* Server-independent unit tests for {@link AerospikeClient#resolveIndexType}.
*
* Server versions 8.1.3+ use the "integer" index type instead of "numeric".
* The client transparently maps between the two based on the target server
* version. This is the only place the mapping is observable: on the server,
* "numeric" and "integer" collapse to the same internal type, so the created
* index carries no record of which spelling was used.
*/
public class AerospikeClientIndexTypeTest {
private static Version version(int major, int minor, int patch, int build) {
return new Version(major, minor, patch, build);
}

@Test
public void numericUpgradesToIntegerOn813() {
// Exact boundary.
assertSame(IndexType.INTEGER, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(8, 1, 3, 0)));
}

@Test
public void numericUpgradesToIntegerAbove813() {
assertSame(IndexType.INTEGER, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(8, 1, 4, 0)));
assertSame(IndexType.INTEGER, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(9, 0, 0, 0)));
// Build component past the boundary still counts as >= 8.1.3.0.
assertSame(IndexType.INTEGER, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(8, 1, 3, 5)));
}

@Test
public void numericUnchangedBelow813() {
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(8, 1, 2, 0)));
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(8, 1, 2, 99)));
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(8, 0, 0, 0)));
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.NUMERIC, version(4, 9, 0, 3)));
}

@Test
public void integerUnchangedOnOrAbove813() {
assertSame(IndexType.INTEGER, AerospikeClient.resolveIndexType(IndexType.INTEGER, version(8, 1, 3, 0)));
assertSame(IndexType.INTEGER, AerospikeClient.resolveIndexType(IndexType.INTEGER, version(9, 0, 0, 0)));
}

@Test
public void integerDowngradesToNumericBelow813() {
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.INTEGER, version(8, 1, 2, 0)));
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.INTEGER, version(8, 0, 0, 0)));
assertSame(IndexType.NUMERIC, AerospikeClient.resolveIndexType(IndexType.INTEGER, version(4, 9, 0, 3)));
}

@Test
public void otherTypesUnchangedAcrossVersions() {
for (IndexType type : new IndexType[] {IndexType.STRING, IndexType.GEO2DSPHERE}) {
assertSame(type, AerospikeClient.resolveIndexType(type, version(8, 1, 2, 0)));
assertSame(type, AerospikeClient.resolveIndexType(type, version(8, 1, 3, 0)));
assertSame(type, AerospikeClient.resolveIndexType(type, version(9, 0, 0, 0)));
}
}
}
4 changes: 3 additions & 1 deletion test/src/com/aerospike/test/SuiteSync.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import com.aerospike.test.sync.query.TestQueryString;
import com.aerospike.test.sync.query.TestQuerySum;
import com.aerospike.test.util.Args;
import com.aerospike.client.AerospikeClientIndexTypeTest;

@RunWith(Suite.class)
@Suite.SuiteClasses({
Expand Down Expand Up @@ -121,7 +122,8 @@
TestQueryOperations.class,
TestQueryRPS.class,
TestQueryString.class,
TestQuerySum.class
TestQuerySum.class,
AerospikeClientIndexTypeTest.class
})
public class SuiteSync {
public static IAerospikeClient client = null;
Expand Down
145 changes: 145 additions & 0 deletions test/src/com/aerospike/test/sync/query/TestIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@
import org.junit.Test;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.Bin;
import com.aerospike.client.Info;
import com.aerospike.client.Key;
import com.aerospike.client.ResultCode;
import com.aerospike.client.Value;
import com.aerospike.client.cdt.CTX;
import com.aerospike.client.cluster.Node;
import com.aerospike.client.exp.Exp;
import com.aerospike.client.exp.Expression;
import com.aerospike.client.exp.LoopVarPart;
import com.aerospike.client.query.Filter;
import com.aerospike.client.query.IndexType;
import com.aerospike.client.query.RecordSet;
import com.aerospike.client.query.Statement;
import com.aerospike.client.task.IndexTask;
import com.aerospike.client.util.Version;
import com.aerospike.test.sync.TestSync;
Expand All @@ -39,6 +44,12 @@ public class TestIndex extends TestSync {
private static final String indexName = "testindex";
private static final String binName = "testbin";
private static final String setIndexName = "testsetindex";
private static final String integerIndexName = "testintegerindex";
private static final String integerBinName = "testintegerbin";
private static final String integerKeyPrefix = "testintegerkey";
private static final String numericIndexName = "testnumericindex";
private static final String numericBinName = "testnumericbin";
private static final String numericKeyPrefix = "testnumerickey";

@Test
public void createDrop() {
Expand Down Expand Up @@ -116,6 +127,140 @@ public void setIndexCreateDrop() {
}
}

@Test
public void integerIndexCreateQueryDrop() {
Assume.assumeTrue("INTEGER index type requires server version 8.1.3 or later",
args.serverVersion.isGreaterOrEqual(8, 1, 3, 0));

IndexTask task;

// Drop index if it already exists.
try {
task = client.dropIndex(args.indexPolicy, args.namespace, args.set, integerIndexName);
task.waitTillComplete();
}
catch (AerospikeException ae) {
if (ae.getResultCode() != ResultCode.INDEX_NOTFOUND) {
throw ae;
}
}

task = client.createIndex(args.indexPolicy, args.namespace, args.set, integerIndexName, integerBinName, IndexType.INTEGER);
task.waitTillComplete();

int size = 20;

for (int i = 1; i <= size; i++) {
Key key = new Key(args.namespace, args.set, integerKeyPrefix + i);
Bin bin = new Bin(integerBinName, i);
client.put(null, key, bin);
}

Statement stmt = new Statement();
stmt.setNamespace(args.namespace);
stmt.setSetName(args.set);
stmt.setBinNames(integerBinName);
stmt.setFilter(Filter.range(integerBinName, 4, 8));

RecordSet rs = client.query(null, stmt);

try {
int count = 0;

while (rs.next()) {
count++;
}
assertEquals(5, count);
}
finally {
rs.close();
}

task = client.dropIndex(args.indexPolicy, args.namespace, args.set, integerIndexName);
task.waitTillComplete();

// Ensure all nodes have dropped the index.
Node[] nodes = client.getNodes();

for (Node node : nodes) {
String cmd = IndexTask.buildStatusCommand(args.namespace, integerIndexName, node.serverVersion);
String response = Info.request(node, cmd);
int code = Info.parseResultCode(response);

assertEquals(201, code);
}
}

@Test
public void numericIndexUpgradesToIntegerQueryDrop() {
// On server versions 8.1.3+ the client transparently upgrades a NUMERIC
// request to the "integer" index type. The server collapses "numeric" and
// "integer" to the same internal type, so the create spelling cannot be
// read back; this test instead verifies the upgraded index is created and
// remains queryable end-to-end. The wire-level mapping itself is asserted
// by AerospikeClientIndexTypeTest.
Assume.assumeTrue("NUMERIC to INTEGER upgrade requires server version 8.1.3 or later",
args.serverVersion.isGreaterOrEqual(8, 1, 3, 0));

IndexTask task;

// Drop index if it already exists.
try {
task = client.dropIndex(args.indexPolicy, args.namespace, args.set, numericIndexName);
task.waitTillComplete();
}
catch (AerospikeException ae) {
if (ae.getResultCode() != ResultCode.INDEX_NOTFOUND) {
throw ae;
}
}

task = client.createIndex(args.indexPolicy, args.namespace, args.set, numericIndexName, numericBinName, IndexType.NUMERIC);
task.waitTillComplete();

int size = 20;

for (int i = 1; i <= size; i++) {
Key key = new Key(args.namespace, args.set, numericKeyPrefix + i);
Bin bin = new Bin(numericBinName, i);
client.put(null, key, bin);
}

Statement stmt = new Statement();
stmt.setNamespace(args.namespace);
stmt.setSetName(args.set);
stmt.setBinNames(numericBinName);
stmt.setFilter(Filter.range(numericBinName, 4, 8));

RecordSet rs = client.query(null, stmt);

try {
int count = 0;

while (rs.next()) {
count++;
}
assertEquals(5, count);
}
finally {
rs.close();
}

task = client.dropIndex(args.indexPolicy, args.namespace, args.set, numericIndexName);
task.waitTillComplete();

// Ensure all nodes have dropped the index.
Node[] nodes = client.getNodes();

for (Node node : nodes) {
String cmd = IndexTask.buildStatusCommand(args.namespace, numericIndexName, node.serverVersion);
String response = Info.request(node, cmd);
int code = Info.parseResultCode(response);

assertEquals(201, code);
}
}

@Test
public void ctxRestore() {
CTX[] ctx1 = new CTX[] {
Expand Down
Loading