如何使用休眠搜索按本地化字符串排序(@ElementCollection Map)

How to order by localized String with hibernate search (@ElementCollection Map)

我有以下实体:

@Entity
@Table(name = "item")
public class Item {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(length = 16, updatable = false)
  private UUID id;


  @Column(nullable = false)
  @KeywordField
  private String classifier;


  @OneToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = ATTRIBUTE_LOCALIZEDNAME)
  @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
  @IndexedEmbedded(name = ATTRIBUTE_LOCALIZEDNAME)
  private LocalizedString localizedname = new LocalizedString();

}

和:

@Entity
@Table(name = "localizedstring")
public class LocalizedString {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(length = 16, updatable = false)
  private UUID id;

  @ElementCollection(fetch = FetchType.EAGER)
  @MapKeyColumn(name = "locale")
  @CollectionTable(name = "localizedstring_stringvalue", joinColumns = @JoinColumn(name = "localizedstring"))
  @KeywordField(
    sortable = Sortable.YES,
    normalizer = BaseAnalysisConfigurer.NORMALIZER_SORT
  )
  private final Map<String, String> stringvalue = new HashMap<>();


}

这 2 个实体在 3 table 秒内重新生成,例如以下数据:

项目table:

id;classifier;name;localizedname
item-id-1;SALUT;Herr;localized-sting-id-1
item-id-2;SALUT;Frau;localized-sting-id-2

本地化字符串table:

id
localized-sting-id-1
localized-sting-id-2

localizedstring-stringvalue table:

localizedstring;locale;stringvalue
localized-sting-id-1;de;Herr
localized-sting-id-2;de;Frau
localized-sting-id-1;en;Mr
localized-sting-id-2;en;Mrs

现在我可以像这样按分类器搜索和排序项目数据了:

Search.session(entityManager)
  .search(Organization.class)
    .where(
      queryBuilder.bool()
        .must(queryBuilder.match()
          .field("classifier").matching("something")
    )
    .sort(
      queryBuilder
        .field("classifier")
          .order(SortOrder.ASC)
    )

但我想根据登录的用户以德语或英语根据本地化字符串的值对项目进行排序... 类似于:

    Search.session(entityManager)
      .search(Organization.class)
        .where(
          queryBuilder.bool()
            .must(queryBuilder.match()
              .field("classifier").matching("SALUT")
        )
        .sort(
          queryBuilder
            .field("localizedname.stringvalue")
              .where("localizedname.locale" == "de")
                .order(SortOrder.ASC)
        )

这样的事情有可能吗??

possible,但是真的不会表现好

相反,请考虑在查询时以您实际需要的方式为数据编制索引。

例如,使用 custom bridge with field templates 为每个语言环境创建一个字段:

  @ElementCollection(fetch = FetchType.EAGER)
  @MapKeyColumn(name = "locale")
  @CollectionTable(name = "localizedstring_stringvalue", joinColumns = @JoinColumn(name = "localizedstring"))
  @PropertyBinding(binder = @PropertyBinderRef(type = LocalizedStringsBinder.class))
  private final Map<String, String> stringvalue = new HashMap<>();
public class LocalizedStringsBinder implements PropertyBinder {

    @Override
    public void bind(PropertyBindingContext context) { 
        context.dependencies().useRootOnly();

        IndexSchemaObjectField rootField = context.indexSchemaElement() 
                .objectField( context.bridgedElement().name() );

        rootField
                .objectFieldTemplate( "locale" )
                .matchingPathGlob( "locale_*" );
        rootField
                .fieldTemplate( "locale_value", f -> f
                        .asString() )
                .matchingPathGlob( "locale_*.value" );
        rootField
                .fieldTemplate( "locale_value_sort", f -> f
                        .asString().sortable(Sortable.YES)
                        .normalizer(BaseAnalysisConfigurer.NORMALIZER_SORT) )
                .matchingPathGlob( "locale_*.value_sort" );

        context.bridge( Map.class, new Bridge( rootField.toReference() ) );
    }


    private static class Bridge implements PropertyBridge<Map> {

        private final IndexObjectFieldReference rootFieldReference;

        private Bridge(IndexObjectFieldReference rootFieldReference) {
            this.rootFieldReference = rootFieldReference;
        }

        @Override
        public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
            Map<String, String> strings = (Map<String, String>) bridgedElement;

            DocumentElement rootObject = target.addObject( rootFieldReference ); 

            for ( Map.Entry<String, String> entry : strings.entrySet() ) {
                String locale = entry.getKey();
                DocumentElement localeObject = rootObject.addObject( "locale_" + locale ); 
                String fieldValue = entry.getValue();
                localeObject.addValue( "value", fieldValue ); 
                localeObject.addValue( "value_sort", fieldValue ); 
            }
        }
    }
}

那你可以这样搜索:

    Search.session(entityManager).search(Organization.class)
        .where( f -> f.bool()
            .must(queryBuilder.match()
              .field("classifier").matching("SALUT")
        )
        .sort( f -> f.field("stringvalue.locale_de.value_sort").asc() )
        .fetchHits( 20 );