【发布时间】:2017-01-01 16:34:07
【问题描述】:
我有一个hazelcast Ilist,学生类包含5个属性,如(id,name,address,number,school)。现在列表中有10k条记录,我怎样才能找到名字是tony和number是的学生001很快除了for循环。我知道如果它是Imap我可以使用谓词过滤,但它是一个列表,我没有找到Ilist的谓词。任何帮助,非常感谢。
【问题讨论】:
我有一个hazelcast Ilist,学生类包含5个属性,如(id,name,address,number,school)。现在列表中有10k条记录,我怎样才能找到名字是tony和number是的学生001很快除了for循环。我知道如果它是Imap我可以使用谓词过滤,但它是一个列表,我没有找到Ilist的谓词。任何帮助,非常感谢。
【问题讨论】:
不幸的是,使用某种谓词或其他魔法无法做到这一点。你必须做一个循环。但是,为了加快速度,您应该在包含该列表的成员上运行此搜索。分区是由列表的名称定义的。您基本上可以自己编写一个小型“查询引擎”来利用列表顶部的 Hazelcast 谓词。
我创建了一个基本示例,但您很可能可以对其进行优化。
一个简单的学生类:
public class Student implements Serializable {
private long id;
private String name;
private String address;
private String number;
private String school;
public long getId() { return id; }
public void setId(long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String getNumber() { return number; }
public void setNumber(String number) { this.number = number; }
public String getSchool() { return school; }
public void setSchool(String school) { this.school = school; }
@Override
public String toString() {
return "Student{" + "id=" + id
+ ", name='" + name + '\''
+ ", address='" + address + '\''
+ ", number='" + number + '\''
+ ", school='" + school + '\'' + '}';
}
}
搜索执行者:
public class StudentSearch {
private final IExecutorService executorService;
public StudentSearch(HazelcastInstance hazelcastInstance) {
this.executorService =
hazelcastInstance.getExecutorService("student_search");
}
public Student findFirstByNameAndNumber(String listName,
String name,
String number)
throws Exception {
Predicate namePredicate = Predicates.equal("name", name);
Predicate numberPredicate = Predicates.equal("number", number);
Predicate predicate = Predicates.and(namePredicate, numberPredicate);
StudentSearchTask task = new StudentSearchTask(listName, predicate);
Future<Student> future = executorService.submitToKeyOwner(task, listName);
return future.get();
}
private static class StudentSearchTask
implements Callable<Student>,
DataSerializable,
HazelcastInstanceAware {
private HazelcastInstance hazelcastInstance;
private String listName;
private Predicate predicate;
public StudentSearchTask() {
}
public StudentSearchTask(String listName, Predicate predicate) {
this.listName = listName;
this.predicate = predicate;
}
@Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
this.hazelcastInstance = hazelcastInstance;
}
@Override
public Student call() throws Exception {
IList<Student> list = hazelcastInstance.getList(listName);
Optional<Map.Entry<String, Student>> first =
list.stream()
.map(this::makeMapEntry)
.filter(predicate::apply)
.findFirst();
return first.orElse(makeMapEntry(null)).getValue();
}
@Override
public void writeData(ObjectDataOutput out) throws IOException {
out.writeUTF(listName);
out.writeObject(predicate);
}
@Override
public void readData(ObjectDataInput in) throws IOException {
listName = in.readUTF();
predicate = in.readObject();
}
private Map.Entry<String, Student> makeMapEntry(Student student) {
return new QueryEntry(listName, student);
}
}
// Used to query the list entries
private static class QueryEntry
implements Map.Entry<String, Student>,
Extractable {
private final String key;
private final Student value;
private QueryEntry(String key, Student value) {
this.key = key;
this.value = value;
}
@Override
public Object getAttributeValue(String attributeName)
throws QueryException {
if ("number".equals(attributeName)) {
return value.getNumber();
} else if ("name".equals(attributeName)) {
return value.getName();
}
return null;
}
@Override
public AttributeType getAttributeType(String attributeName)
throws QueryException {
return AttributeType.STRING;
}
@Override
public String getKey() {
return key;
}
@Override
public Student getValue() {
return value;
}
@Override
public Student setValue(Student value) {
throw new UnsupportedOperationException();
}
}
}
最后如何运行这段代码:
List<Student> students = hz.getList(listName);
addStudents(students);
StudentSearch search = new StudentSearch(hz);
Student result = search
.findFirstByNameAndNumber(listName, "Tony", "001");
System.out.println(result);
我希望这会有所帮助:)
【讨论】:
我无法获取对象中的 id,因为您需要查询 id+name(因此 id 不是唯一的?)。如果您知道需要查询它们,为什么要将它们存储在 Set 中(请提供更多信息)。
正如您指出的那样,Set 中没有谓词。恕我直言,这是因为无法索引与 Key 无关的条目。如果无法添加索引(或至少对键进行范围扫描),谓词的概念就会崩溃,因为任何查询仍将遍历整个集合。据我所知,您没有太多选择:
如果您使用 set must to have unique entries,请不要!
在这种情况下,将其移动到地图上,并像任何人一样使用键,例如您的对象 id。如果可能存在 id、重复项,您可以制作更复杂的键,例如 id+name 甚至散列整个对象。一旦您必须放置一个新对象,请创建密钥并检查它是否已经存在,如果如此,则使用您的自定义逻辑进行回退。 Map 会为您提供您想要的所有索引和谓词。
另一方面,如果由于某些不受您控制的原因,您必须使用 set...,那么您可以通过多种方式进行操作,但我建议如下:
监听对集合的任何修改(或者如果是静态的或一致性不是问题,请定期扫描集合
构建您的自定义索引
如何建立索引:
这实际上取决于您想要的性能、您可以接受的 RAM 影响以及查询的不同程度。 (假设您只有查询总是相同的,例如“名称等于”)。
MultiMap<String, String> index
// index.put(name, key)
您可以通过在每个 Set 修改中添加、删除条目来构建索引,在 MultiMap 中使用 object.name 作为键,并将 Set 中的实际 Key 作为 multimap 中的值。搜索给定的 name 后,您只需执行以下操作(伪伪代码)
MultiMap<String, String> index;
Map<String, your_object_class> your_set;
function getByName(String name)
{
List<String> name_key_set index.get( name );
List<your_object_class> out;
for(String key : name_key_set)
out.add(index.get(key));
return out;
}
IMO 没有什么可以在 Set 上调用 query(将 query 称为检索数据的聪明方法,而不是暴力迭代),因为任何此类系统都需要 key=>value 条目。
有了更多信息,我们可以更好地帮助您:)
【讨论】: