Java objectmapper - Search records using secondary index and expressions

I know the Java client allows reading records using secondary index (filters) and expressions (non indexed bins) However I could not find any such functionality support in the java objectmapper (GitHub - aerospike/java-object-mapper: The Java Object Mapper is a simple, light-weight framework used to map POJOs to the Aerospike database. Using simple annotations or a configuration YAML file to describe how to map the data to Aerospike, the project takes the tedium out of mapping the data through the powerful, low level interface.) library. Is it possible to read records using expressions using objectmapper?

Hi, thanks for your interest in the Java Object Mapper!

At the moment secondary indexes are not directly supported, although there are plans to do so in the future. However, you can still use them, albeit not quite as easily as we’re looking to do in the future. Let’s look at an example. First we need an business object:

@AerospikeRecord(namespace = "test", set = "person")
public static class Person {
	private String name;
	private int age;
	@AerospikeKey
	private int id;
		
	public Person(@ParamFrom("id") int id, @ParamFrom("name") String name, @ParamFrom("age")int age) {
		super();
		this.name = name;
		this.age = age;
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public int getAge() {
		return age;
	}
	public int getId() {
		return id;
	}
	@Override
	public String toString() {
		return String.format("{id=%d, name=%s, age=%d}", id, name, age);
	}
}

Next, we need to set up the mapper and insert some data:

IAerospikeClient client = new AerospikeClient("127.0.0.1", 3000);
AeroMapper mapper = new AeroMapper.Builder(client).build();
		
mapper.save(new Person(1, "Tim", 312));
mapper.save(new Person(2, "Bob", 44));
mapper.save(new Person(3, "Sue", 56));
mapper.save(new Person(4, "Rob", 23));
mapper.save(new Person(5, "Jim", 32));
mapper.save(new Person(6, "Bob", 78));

Let’s assume there is a secondary index on age called “age_idx”. Also, we need the namespace name and the set name. This can either be coded as constants, or if you want to pick them up from the object definition you can do:

ClassCacheEntry<Person> entry = ClassCache.getInstance().loadClass(Person.class, mapper);
String namespace = entry.getNamespace();
String set = entry.getSetName();

Then to get do the secondary index, you need to set it up like you normally would in Aerospike, but use the Object Mapper to convert the Record to a Person:

System.out.println("Query...");
Statement statement = new Statement();
statement.setFilter(Filter.range("age", 25, 100));
statement.setNamespace(namespace);
statement.setSetName(set);
		
RecordSet records = client.query(null, statement);
while (records.next()) {
	Record record = records.getRecord();
	Person person = mapper.convertToObject(Person.class, record);
	System.out.println(person);
}
records.close();

Now, if you want to add a filter onto this expression, you can do so in the normal way Aerospike supports:

System.out.println("Expression...");
statement.setPredExp(PredExp.stringBin("name"), PredExp.stringValue("Bob"), PredExp.stringEqual());
records = client.query(null, statement);
while (records.next()) {
	Record record = records.getRecord();
	Person person = mapper.convertToObject(Person.class, record);
	System.out.println(person);
}
records.close();

Note that here I’m using predicate expressions which are now deprecated, but if you have a version which supports Expressions these can equally well be used.

We might as well see how to do scans too:

System.out.println("Scan...");
client.scanAll(mapper.getScanPolicy(Person.class), namespace, set, (key, record) -> {
	Person person = mapper.convertToObject(Person.class, record);
	System.out.println(person);
});

Note in this last example I have not used null for the policy, but rather retrieved the policy from the Person class, which can be set up in the Builder().

Hope this helps, please let me know if you have any questions.

1 Like

One minor point too. Depending on your version of the Object Mapper, the line:

Person person = mapper.convertToObject(Person.class, record);

may need to change to

Person person = mapper.getMappingConverter().convertToObject(Person.class, record);

Note that we have now released version 1.3.0 of the Object Mapper, and this now supports scans and queries natively, without the need to do this extra work. You can check it out at GitHub - aerospike/java-object-mapper: The Java Object Mapper is a simple, light-weight framework.

1 Like

Thanks for the feature. I was looking at the query method in the Aeromapper, it allows you to create queries with filters and expressions but returns void. So if you want to return the RecordSet into a list you need to do the processing yourselves using the processor lambda. The code looks like this

List<User> users = new ArrayList<>();
        aeroMapper.query(policy, User.class, x -> {
            users.add(x);
            return true;
        }, Filter.equal("age", 33));
        return users;

Is it possible to add overriden query methods in the Aeromapper which returns List of entities? So that the same code would look like this

return aeroMapper.query(policy, User.class, Filter.equal("age", 33));
© 2021 Copyright Aerospike, Inc. | All rights reserved. Creators of the Aerospike Database.