Hibernate Search(基于version3.4)--第四章Mapping entities to the index structure
Mapping entities to the index structure
?
4.1. 映射一个实体(Mapping an entity)
在第一章中,你已经知道了建立实体索引的所有元信息是通过注解描述的,所以不需要xml的映射文件。但是你依然可以使用Hibernate的映射文件来配置基本的Hibernate映射,但Hibernate Search的配置只能通过注解来表达。
?
4.1.1.基本映射(Basic mapping)
我们先介绍最常使用的注解。
?
4.1.1.1. @Indexed
首先,我们必须要声明一个持久化类是可索引的。这可以由注解@Indexed来注明,所有没有@Indexed的实体将忽略indexing步骤。
?
Example 4.1. Making a class indexable with @Indexed
?
Example 4.3, “Using @Fields to map a property multiple times”中,summary属性域被索引了两次,一次是以summary作为索引文档的域名,以tokenized的方式建立索引,另一次是以summary_forSort作为索引文档的域名,以untokenized的方式建立索引。在@Fields中,@Field支持两个有用的属性:
analyzer:定义@Analyzer注解针对具体的lucene field而不是实体内中的属性域。bridge:定义@FieldBridge注解针对具体的lucene field而不是实体内中的属性域。关于@Analyzer和@FieldBridge,下面的章节有更详细的说明。
?
?
4.1.3. 嵌入和关联对象(Embedded and associated objects)
关联对象和嵌入的对象都可以被索引作为根实体索引的一部分。当实体内中的属性域为关联对象时,这就会显得非常的有用。
?
假设在Example 4.4, “Indexing associations”例子中,它的目的是想要返回city为Atlanta的place,那么在Lucene query parser language中,它会被解析为address.city:Atlanta。例子中会建立名为Place的index,该index会包含这些field: address.id, address.street和address.city,这些field都可以用于搜索。
?
Example 4.4. Indexing associations
?
?
Tip :Filters 和 char filters会按他们在@AnalyzerDef中定义的顺序来应用的。因些顺序是重要的。
?
有些tokenizers, token filters 或char filters需要加载资源文件(比如配置文件,元数据文件等)。stop filter和synonym filter就需要这样的文件。如果资源文件的charset与VM默认的不一样,你可以通过resource_charset 参数明确地指定资源文件的charset。
?
Example 4.11. Use a specific charset to load the property file
?
?
?
?
一旦定义了一个Analyzer,它就可以通过@Analyzer注解来重用。如Example 4.12, “Referencing an analyzer by name”.所示。
?
Example 4.12. Referencing an analyzer by name
?noneHTMLStripCharFilterFactory?移除HTML中标准的标签,保留文本内容?none?none?
?
Table 4.2. Example of available tokenizers
FactoryDescriptionParametersAdditional
dependenciesStandardTokenizerFactory使用Lucene的StandardTokenizernonenoneHTMLStripCharFilterFactory移除HTML中标准的标签,保留文本容,之后交由StandardTokenizer处理nonesolr-corePatternTokenizerFactory通过一个指定的正则表达式来划分词语。
pattern:用于划分词语的正则表达式。
group:says which pattern group to extract into tokens
solr-core?
Table 4.3. Examples of available filters
?
FactoryDescriptionParametersAdditional
dependenciesStandardFilterFactory从token中移除'.'和''s'nonesolr-coreLowerCaseFilterFactory小写所有的tokennonesolr-coreStopFilterFactory移除与列表中的stop word匹配的tokenwords:指向一个包含stop words的资源文件
ignoreCase:与stop word的比较是否要忽略大小写
solr-coreSnowballPorterFilterFactory把一个单词还原为它的词干形式(像protect, protects,
language:Danish,Dutch, English,Finnish, French,German, Italian,Norwegian,Portuguese, Russian,Spanish, Swedish 或其他语言solr-coreISOLatin1AccentFilterFactory
protection都会使用protect作为token)移除发音符,比如French(法语)
nonesolr-corePhoneticFilterFactory向token stream插入发音相似的单词作为相似token(similar token)
encoder:值为DoubleMetaphone,Metaphone, Soundex或RefinedSoundex之一。
?
inject:true就插入新的token到token stream,false就替代现有的token。
?
maxCodeLength:设置可生成code的最大长度。只支持Metaphone和DoubleMetaphone encoder。
solr-core and commons-codecCollationKeyFilterFactory转换每个token成java.text.CollationKey,并使用IndexableBinaryStringTools对java.text.CollationKey进行编码,从而把它保存到index term。
?
?custom,? language,country,? variant,strength,decomposition? see Lucene's CollationKeyFilter javadocs for more info?solr-core and commons-io?
我们推荐通过IDE工具查看org.apache.solr.analysis.TokenizerFactory和org.apache.solr.analysis.TokenFilterFactory的所有可用的实现。
?
4.3.3. 动态解析器选择(试验性的)(Dynamic analyzer selection (experimental))
到目前为止,我们所定义的analyzer都是静态的。然而,可能有这样的需求,我们需要根据实体类的具体状态来决定使用哪个analyzer,比如说在一个多语言的应用环境中。拿Example 4.13, “Usage of @AnalyzerDiscriminator”中的BlogEntry类来说,analyzer需要根据language属性来决定使用哪个analyzer,那么对词干(stemmer)解析就可以指定正确的语言来解析文本。
?
Hibernate Search引进了AnalyzerDiscriminator注解来实现Dynamic analyzer selection。Example 4.13, “Usage of @AnalyzerDiscriminator”展示了这个注解的使用。
?
Example 4.13. Usage of @AnalyzerDiscriminator
?
在这个例子中,song title被索引在两个field中,title field使用的是standard analyzer,而title_stemmed使用的是stemming analyzer。query会根据对应的field使用了合适的analyzer。
?
Tip :通过@AnalyzerDef定义的analyzer,你同样通过searchFactory.getAnalyzer(String)来获取。
?
4.4. Bridges
在我们讨论实体类的基本映射的时候,一个很重要的因素一直被我们忽略。在Lucene所有的index field必须渲染为字符串类型。所有标注为@Field的属性域都要转换为String来建立索引。我们到现在才提出这个问题是因为在大多数情况下,Hibnerate Search都会自动地完成转换工作,这些工作都由内建的bridge完成的。然而,有些时候你需要一个更好的控制这个转换过程。
?
4.4.1. Built-in bridges
Hibernate Search捆绑了大量的bridge完成Java属性类型到字符串的转换。
?
?
?
Hibernate Search comes bundled with a set of built-in bridges between a Java property type and
its full text representation.?
null:默认地,null值不会被索引。Lucene也不支持null元素。然而有时候使用一个自定义的token来代替null值会显得非常有用。 See Section 4.1.1.2, “@Field” for more information
?
java.lang.String:按原本的值来索引。
?
short, Short, integer, Integer, long, Long, float, Float, double, Double, BigInteger, BigDecimal的数值型类型:转换到字符串的表示形式。注意,Lucene不能自动完成数值型的对比工作(如在range query中),它们需要补齐(padded)。
?
Note :使用range query是有争议的而且有一些缺点。一个可选的方法是使用一个Filter query,它可以过滤一个查询到合适的范围。Hibernate Search支持补齐机制(padding mechanism)。
?
java.util.Date:日期型保存成yyyyMMddHHmmssSSS的字符串形式 (例如200611072203012表示2006年11月7日 22:03 12ms) 。你不必为它的格式化忧心,重要的是,当你使用DateRange查询的时候,你需要知道日期应该表示成GMT形式。
?
一般来说,保存的Date不需要精确到毫秒。@DateBridge定义了合适的解决方法让你去保存Date到index。@DateBridge(resolution=Resolution.DAY)定义了Date只保存到具体的'日',而不是毫秒。
?
?
你可以使用通过@FieldBridge注解的impl参数应用上面定义的StringBridge。
?
ParameterizedBridge接口可以与StringBridge,TwoWayStringBridge,FieldBridge一起实现。所有的实现必须是线程安全的,参数必须在初始化时设置。
?
4.4.2.1.2. 按类型的bridge(Type aware bridge)
?
有时候能知道bridge应用在哪个类型上面是非常有用的:
知道getter方法返回类型的bridge知道类的类型通过类级别的bridge一个例子是bridge处理enums在一种自定义的形式,但在访问时应用实际的enum类型。任何实现了 AppliedOnTypeAwareBridge接口的bridge会自动的注入所需要的类型。像parameters一样,类型注入不需要关心线程安全。
?
?
4.4.2.1.3. 两种方式的bridge(Two-way bridge)
如果你的bridge实现应用在id属性(使用了@DocumentId注解),你需要使用TwoWayStringBridge接口,该接口继承于StringBridge接口。Hibernate Search需要读取id的字符串表示并生成对象表示形式。同样也是使用@FieldBridge注解。
?
Example 4.17. Implementing a TwoWayStringBridge usable for id properties
?
?
?
4.4.2.2. FieldBridge
有些用例不仅仅是把对象转换成字符串来映射属性到lucene index中,但实现FieldBridge会让你得到最大的灵活性。该接口为你提供了属性值,并让你按你所希望的方式映射到Lucene Document。比如说你希望把一个属性域保存在两个不同的document field中。这个接口的概念与Hibernate UserType非常类似。
?
Example 4.18. Implementing the FieldBridge interface
?
在Example 4.18, “Implementing the FieldBridge interface”中,fields并不是直接地添加到Document。相反,添加操作委托给LuceneOptions helper;helper会应用注解中的@Field的选项,像Store,TermVector或@Boost值。这也用于封装复杂的COMPRESS的实现。虽然委托LuceneOptions去添加fields到Document是推荐的,但你也可以直接编辑Document和忽略LuceneOptions。
?
Tip :像LuceneOptions这些类从Lucene API中变化而来的,它们作用是保护你的应用和简化代码。你可以使用它们,但并不是必须的。
?
4.4.2.3. ClassBridge
有时候组合一个实体的多个属性到Lucene index中会很有用的。@ClassBridge包含一个或多个@ClassBridge可以定义在class级别。在这种情况下, 自定义的field bridge的value参数不再是属性域的值,而是该实体实例。@ClassBridge支持termVector属性。
?
Example 4.19. Implementing a class bridge
?
在这个例子中, CatFieldsClassBridge被应用到 department实例,field bridge结合了branch和network属性域并添加到index。
?
4.5. Providing your own id
Warning :这部分文档的工作还在进行中。
?
如果你扩展了Hibernate Search内部功能,你可以提供你自己的id。你可以生成唯一的id值添加到index中去。它必须在创建an org.hibernate.search.Work的时候提供给Hibernate Search——document id是构造器中的一个参数。
?
4.5.1. The ProvidedId annotation
@PrivideId与@DocumentId不一样,它是标注在类级别上的。你可以通过bridge属性来指定一个自定义的bridge实现。同样地,如果你使用@ProvidedId注解一个类,该类的子类也会也会得到该注解,但是它不是通过@Inherited得到这种效果的。另外,要确保@ProvidedId与@DocumentId不能同时使用,否则你的系统就会崩溃。
?
Example 4.20. Providing your own id
@ProvidedId (bridge = org.my.own.package.MyCustomBridge)@Indexedpublic class MyClass{ @Field String MyString; ...}?
4.6. Programmatic API?
Warning:该功能还在测试阶段。API在以后可能会有所改变。
?
略。?
?
?
?
?
?
?