如何通过IMap<String,Set<MyObject>>中的对象属性查询?

How to query by object attributes from IMap<String,Set<MyObject>>?

Hazelcast docs 解释了当我们有 IMap<String,Object> 时如何做到这一点,但没有说明当映射值是一个集合时的情况。

我看到两个选项:

编辑:实施最终与 mikhail-baksheev 建议的相似。请注意 CollectionExtractor 使用 ifblock 来决定应收集 MyObject 中的哪个字段。有没有更漂亮(但仍然有效)的解决方案?

public class CollectionExtractor extends ValueExtractor<Set<MyObject>, String> {

    @Override
    public void extract(
            final Set<MyObject> target,
            final String argument,
            final ValueCollector collector ) {

        if ( argument.equals( "field_1" ) ) {
            for ( final MyObject o : target ) {
                collector.addObject( o.getField_1() );
            }
        } else if ( argument.equals( "field_2" ) ) {
            for ( final MyObject o : target ) {
                collector.addObject( o.getField_2() );
            }
        }
    }
}

@Getter
public class MyObject implements Serializable {

    private String field_1;
    private String field_2;
}

例如,你想查询一些定义为参数集的对象Set<Parameter>:

public class Parameter {
    private String name;
    private Object value;

    public Parameter( String name, Object value ) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }
}

首先,为Parameters实现ValueExtarcator:

public class ParameterExtractor extends ValueExtractor<Set<Parameter>, String> {
   @Override
   public void extract( Set<Parameter> parameters, String parameterName, ValueCollector collector ) {
      for ( Parameter p : parameters ) {
          if ( parameterName.equals( p.getName() ) ) {
              collector.addObject( p.getValue() );
          }
      }
   }
}

接下来,将 ValueExtractor 添加到自定义属性的地图配置中 parameter:

Config config = new Config( "my-instance" );
MapConfig mapConfig = new MapConfig( "map" );
mapConfig.addMapAttributeConfig( new MapAttributeConfig( "parameter", ParameterExtractor.class.getName() ) );
config.addMapConfig( mapConfig );

向地图中添加一些数据:

HazelcastInstance hzInstance = Hazelcast.getOrCreateHazelcastInstance( config );
IMap<String, Set<Parameter>> objectsMap = hzInstance.getMap( "map" );
objectsMap.put( "user1", ImmutableSet.of(
                new Parameter( "type", "user" ),
                new Parameter( "firstName", "Joe" ) ) );

现在可以使用方括号查询指定属性的数据:

objectsMap.values( Predicates.and( Predicates.equal( "parameter[type]", "user" ), Predicates.equal( "parameter[firstName]", "Joe" ) ) );

方括号中的值是一个custom argument,这个值将作为第二个参数传递给值提取器的extract方法,可用于查找所需的属性。

编辑:如果您有特殊类型的对象在集合中定义的字段,例如MyObject class 从您的示例中,您可以为 MyObject 的每个字段实现 ValueExtractor,而不是使用自定义属性来查找所需的字段:

public class Field_1Extractor extends ValueExtractor<Set<MyObject>, String> {
    @Override
    public void extract(
            final Set<MyObject> target,
            final String argument,
            final ValueCollector collector ) {

            for ( final MyObject o : target ) {
                collector.addObject( o.getField_1() );
            }
    }
}

Edit:您也可以使用 ValueExtractor 在值集合中查找包含特定元素的条目。例如,"any" 谓词的提取器:list[any] == some value:

public class ListValueExtractor extends ValueExtractor<List<String>, String> implements Serializable {

    @Override
    public void extract( List<String> listValues, String argument, ValueCollector collector ) {
        if ( "any".equals( argument ) ) {
            for ( String v : listValues ) {
                collector.addObject( v );
            }
        }
    }
}

使用:

public class Main {

    public static void main( String[] args ) {
        Config cfg = new Config();
        MapConfig mapConfig = new MapConfig( "things" );
        mapConfig.addMapAttributeConfig( new MapAttributeConfig( "list", ListValueExtractor.class.getName() ) );//register extractor for 'list'
        cfg.addMapConfig( mapConfig );
        HazelcastInstance instance = Hazelcast.newHazelcastInstance( cfg );
        IMap<String, List<String>> mapThings = instance.getMap( "things" );

        mapThings.put( "Joe", new ArrayList<String>() {{
            add( "apple" );
            add( "box" );
            add( "laptop" );
        }} );

        mapThings.put( "Denis", new ArrayList<String>() {{
            add( "apple" );
            add( "pencil" );
            add( "jacket" );
        }} );
        EntryObject e = new PredicateBuilder().getEntryObject();
        Predicate withApple = Predicates.equal( "list[any]", "apple" );
        Predicate withLaptop = Predicates.equal( "list[any]", "laptop" );

        List<String> usersWithApple = mapThings.entrySet( withApple ).stream().map( Map.Entry::getKey ).collect( Collectors.toList() );
        List<String> usersWithLaptop = mapThings.entrySet( withLaptop ).stream().map( Map.Entry::getKey ).collect( Collectors.toList() );

        System.out.println( usersWithApple ); // [Joe, Denis]
        System.out.println( usersWithLaptop ); // [Joe]
    }
}

也许 hazelcast 有更方便的方法来搜索地图值集合,但我没有在文档中找到它