概述
对于映射,我们需要定义
- 哪些实体应映射到索引
- 对于每种实体类型,对象图的哪一部分包含在文档中
- 哪些字符串属性实际上是
codes
,不应进行分析 - 我们希望哪些字符串属性同时包含
已分析和“原始”
字段,以便进行搜索和排序 - 任何额外的 ElasticSearch 特定映射
@DocStore - 要映射的实体
我们对要映射到 ElasticSearch 索引的每个实体添加 @DocStore
注释。
// Store contact in ElasticSearch
@DocStore
@Entity
public class Contact {
默认情况下,@DocStore 表示
- @OneToMany 和 @ManyToMany 不包含在内
- @ManyToOne 和 @OneToOne 仅包含关联的 @Id 属性
- 所有其他持久属性都包含在文档中
您可以通过指定要通过 doc
包含的属性来有效减少包含在索引中的属性。
示例:仅索引某些属性
@DocStore(doc="firstName, lastName, email")
@Entity
public class Contact {
...
我希望将要索引的属性减少到上述属性,这相对罕见。TODO:添加 @DocIgnore 和 @DocProperty(ignore=true) 支持。
@DocEmbedded - 嵌入式文档
在 @ManyToOne 和 @OneToMany 属性上,您可以使用 @DocEmbedded
指定应包含在要索引的文档中的属性。
示例:嵌入式 ManyToOne
将客户 ID、名称嵌入到联系人索引中。
@DocStore
@Entity
public class Contact {
...
// denormalise including the customer id and name
// into the 'contact' document
@DocEmbedded(doc="id,name")
@ManyToOne(optional=false)
Customer customer;
示例:嵌入式 OneToMany
嵌入一些客户详细信息(客户 ID 和名称)。嵌入订单详细信息(作为 ElasticSearch “嵌套”属性,因为它是一个 @OneToMany)。
@DocStore
@Entity
public class Order {
...
@DocEmbedded(doc="id,name")
@ManyToOne(optional=false)
Customer customer;
@DocEmbedded
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
List<OrderDetail> details;
示例:嵌入嵌套
嵌入更多客户详细信息,包括嵌套的 billingAddress 和 billingAddress.country。嵌入更多订单详细信息,包括嵌套的产品,其中包含 id、sku 和名称。
@DocStore
@Entity
public class Order {
...
// embed some customer details including the billingAddress
@DocEmbedded(doc = "id,name,status,billingAddress(*,country(*))")
@ManyToOne(optional=false)
Customer customer;
@DocEmbedded(doc = "*,product(id,sku,name)")
@OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
List<OrderDetail> details;
@DocCode - 作为“代码”的字符串
我们希望使用 @DocCode
映射一些字符串属性,以便不会分析属性值,而是将它们视为文本值/代码(不会由分析器小写/词干化等)。
Ebean 会自动将 UUID
、Enum
和任何字符串 @Id
属性视为“代码”,并且您无需使用 @DocCode
对它们进行注释。
请注意,如果您将 @DocCode 放置在产品 sku 上,那么无论它嵌入在何处,它都被视为代码。因此,如果产品 sku 嵌入在订单索引中,它也将被视为那里的 @DocCode 属性。
示例
我们希望将产品 sku 值视为文本代码(不分析)。
@DocStore
@Entity
public class Product {
// treat sku as a "code" (not analysed)
@DocCode
String sku;
@DocSortable
String name;
映射
@DocCode 属性被映射为未分析。
"properties" : {
"sku": { "type": "string", "index": "not_analyzed" },
...
@DocSortable - 已分析和未分析
我们希望使用 @DocSortable
注释一些字符串属性,这样做会为该属性提供已分析和未分析/原始字段。我们可以将已分析字段用于文本搜索,我们可以将未分析/原始字段用于排序(和 ElasticSearch 聚合功能)。
请注意,如果您将 @DocSortable 放置在客户名称上,那么无论它嵌入在何处,它都被视为可排序。因此,如果客户名称嵌入在订单索引中,它也将被视为那里的 @DocSortable。
示例:客户
我们希望能够按客户名称排序。
@DocStore
@Entity
public class Customer {
...
@DocSortable
String name;
示例:产品
我们希望能够按产品名称排序(并且我们可以按产品 sku 排序,因为它是代码)。
@DocStore
@Entity
public class Product {
@DocCode
String sku;
@DocSortable
String name;
映射
@DocSortable 属性使用附加的“原始”未分析字段进行映射。
"properties" : {
"name": { "type": "string", "fields": {
"raw": { "type": "string", "index": "not_analyzed" } } },
...
查询使用 - 按排序
当您编写 Ebean 查询并指定按排序子句时,Ebean 会自动转换按排序子句以使用关联的“原始”字段(如果已定义)。
List<Product> products = server.find(Product.class)
.setUseDocStore(true)
.order().asc("name")
.findList();
Elastic 查询
// name.raw used automatically for sort order
{"sort":[{"name.raw":{"order":"asc"}}],"query":{"match_all":{}}}
查询使用 - 术语表达式
“等于”转换为 Elastic “term”查询,对于 @DocSortable 属性,term 表达式将使用关联的“原始”字段。
类似地,“大于”、“小于”、“大于或等于”和“小于或等于”也转换为范围查询,在可用时也使用“原始”字段。
List<Product> products = server.find(Product.class)
.setUseDocStore(true)
.where().eq("name","Chair")
.findList();
Elastic 查询
// name.raw used automatically for 'term' expression
{"query":{"filtered":{"filter":{"term":{"name.raw":"Chair"}}}}}
@DocProperty
@DocProperty
提供所有额外的映射选项,包括
store
默认值 falseboost
默认值 1includeInAll
默认值 trueenabled
默认值 truenorms
默认值 truedocValues
默认值 truenullValue
analyzer
searchAnalyzer
copyTo
index 选项
- DOCS、FREQS、POSITIONS、OFFSETS
它还提供标志,将 code
和 sortable
设置为 @DocCode
和 @DocSortable
的替代项。
@DocProperty
可以放在属性上,也可以放在 @DocStore mapping
属性上,此处的映射有效地覆盖任何现有的属性映射。
@DocStore(mapping = {
@DocMapping(name = "description",
options = @DocProperty(enabled = false)),
@DocMapping(name = "notes",
options = @DocProperty(boost = 1.5f, store = true))
})
@Entity
public class Content {
映射生成
为了有效地将 ElasticSearch 与相对结构化的 ORM 文档结合使用,我们需要使用适当的属性映射(类型、代码、可排序性等)创建 ElasticSearch 索引,这在某种程度上类似于 SQL 数据库的 DDL。
ebean.docstore.generateMapping=true
使用 ebean.docstore.generateMapping=true
,Ebean 将为每个映射的 bean 类型(使用 @DocStore)生成一个映射文件。默认情况下,这些映射文件进入 src/main/resources
,然后进入 elastic-mapping
,这可以通过 DocStoreConfig pathToResources 和 mappingPath 进行配置。
预计在开发/测试期间使用此功能。
ebean.docstore.dropCreate=true
使用 ebean.docstore.dropCreate=true
,Ebean 在启动时将删除所有映射的索引,并使用生成的映射重新创建它们。
预计在开发/测试期间使用此功能。
ebean.docstore.create=true
使用 ebean.docstore.create=true
,Ebean 在启动时将检查哪些索引存在,并使用生成的映射创建任何缺失的索引。
预计在开发/测试期间使用此功能。
请注意,仅在 dropCreate
为 false 时才使用 create=true
。
示例映射
可在 src/main/resources/elastic-mapping 中找到示例应用程序的生成映射示例。
示例:product_v1.mapping.json
{
"mappings" : {
"product" : {
"properties" : {
"sku": { "type": "string", "fields": { "raw": { "type": "string", "index": "not_analyzed" } } },
"name": { "type": "string", "fields": { "raw": { "type": "string", "index": "not_analyzed" } } },
"whenCreated": { "type": "date" },
"whenModified": { "type": "date" },
"version": { "type": "long" }
}
}
}
}
示例:customer_v1.mapping.json
{
"mappings" : {
"customer" : {
"properties" : {
"status": { "type": "string", "index": "not_analyzed" },
"name": { "type": "string" },
"smallNote": { "type": "string" },
"anniversary": { "type": "date" },
"billingAddress" : {
"properties" : {
"id": { "type": "long" },
"line1": { "type": "string" },
"line2": { "type": "string" },
"city": { "type": "string" },
"country" : {
"properties" : {
"code": { "type": "string", "index": "not_analyzed" },
"name": { "type": "string" }
}
},
"whenCreated": { "type": "date" },
"whenModified": { "type": "date" },
"version": { "type": "long" }
}
},
"shippingAddress" : {
"properties" : {
"id": { "type": "long" },
"line1": { "type": "string" },
"line2": { "type": "string" },
"city": { "type": "string" },
"country" : {
"properties" : {
"code": { "type": "string", "index": "not_analyzed" },
"name": { "type": "string" }
}
},
"whenCreated": { "type": "date" },
"whenModified": { "type": "date" },
"version": { "type": "long" }
}
},
"whenCreated": { "type": "date" },
"whenModified": { "type": "date" },
"version": { "type": "long" }
}
}
}
}