提交后
当事务成功提交直接通过 @DocStore
映射到 ElasticSearch 的实体 Bean 或通过成为嵌入式文档的一部分(非规范化)间接映射到 ElasticSearch 的实体 Bean 将发生需要传播到 ElasticSearch 的更改。
这些实体 Bean 的处理发生在后台线程中,以便不影响事务的正常响应时间。
更改基于其 DocStoreMode
传播
- UPDATE - 通过其批量 API 将更改发送到 ElasticSearch。
- QUEUE - 将更改推送到队列以供以后处理。
- IGNORE - Ebean 忽略更改,期望应用程序根据需要查找和传播更改。
事务 DocStoreMode.IGNORE
可以将事务设置为 DocStoreMode.IGNORE
,然后 Ebean 将忽略该事务中的所有更改。这适用于大批量处理,其中最好让 Ebean 不执行更改的正常处理,而是让应用程序搜索要以后传播到 ElasticSearch 的更改。
Transaction transaction = server.beginTransaction();
transaction.setDocStoreMode(DocStoreMode.IGNORE);
try {
// perform lots of changes and we don't want
// Ebean to propagate those (as it would normall)
transaction.commit();
} finally {
transaction.end();
}
// typically application code later finds and
// updates indexes as necessary
// for example:
Query<Product> query = server.find(Product.class)
.where()
.ge("whenModified", new Timestamp(since))
.query();
// update products modified after a given dateTime
server.docStore().indexByQuery(query, 1000);
插入
插入实体 Bean 时,它将作为添加到 DocStoreUpdate 并发送到 DocStoreUpdateProcessor。
这会转换为 ElasticSearch 批量更新中的 index
条目。
示例:插入国家/地区
Country country = new Country("SA","South Africa");
country.save();
批量 API
{"index":{"_id":"SA","_type":"country","_index":"country"}}
{"name":"South Africa"}
删除
删除实体 Bean 时,它将作为添加到 DocStoreUpdate 并发送到 DocStoreUpdateProcessor。
这会转换为 ElasticSearch 批量更新中的 delete
条目。
示例:删除国家/地区
Ebean.delete(Country.class, "SA");
批量 API
{"delete":{"_id":"SA","_type":"country","_index":"country"}}
更新
处理更新比插入和删除更复杂,因为在更新中,我们不仅需要更新主 @DocStore
索引,还需要更新任何索引,其中受影响/更新的属性已作为嵌入式文档的一部分(通常通过 @DocEmbedded
)包含在内。
示例:更新国家/地区
Country sa = fetchSaFromDocStore();
sa.setName("Sud Africa");
sa.save();
批量 API
{"update":{"_id":"SA","_type":"country","_index":"country"}}
{"doc":{"name":"Sud Africa"}}
嵌入式文档
当我们更新实体 Bean 时,还需要更新实体 Bean 已嵌入其中的索引。
每个 @DocEmbedded
表示一个嵌入式文档(非规范化)。当实体 Bean 更新时,Ebean 还会查找以更新任何相关的嵌入式文档。
基于映射(@DocEmbedded
文档属性),Ebean 知道在更新实体 Bean 时需要检查/更新的 嵌套路径
。
例如,嵌入在订单和联系人中的客户
例如,让我们索引客户,但还将客户作为嵌入式文档包含在订单索引和联系人索引中。
已索引的客户
@DocStore
@Entity
public class Customer ...
嵌入在联系人中的客户
@DocStore
@Entity
public class Contact extends BasicDomain {
...
@ManyToOne(optional = false)
@DocEmbedded(doc = "id,name")
Customer customer;
嵌入在订单中的客户
@DocStore
@Entity
@Table(name = "orders")
public class Order extends BasicDomain {
...
@NotNull @ManyToOne
@DocEmbedded(doc = "id,status,name,billingAddress(*,country(*)")
Customer customer;
当客户姓名更新时,Ebean 需要
- 更新客户索引
- 更新任何相关的联系人(基于嵌套路径更新)
- 更新任何相关的订单(基于嵌套路径更新)
当 Ebean 启动时,它使用映射,读取 @DocEmbedded
doc
属性,并确定嵌套文档结构。然后,为每个 嵌套路径
注册一个侦听器。在上面的示例中,向客户注册了 2 个侦听器,其中一个将更新联系人(如果客户姓名更改),另一个将更新订单(如果姓名、状态或账单地址更改)。
更改客户姓名
如果我们找到客户 2 并将其姓名更改为“Roberto”,我们将看到
批量 API
{"update":{"_id":"2","_type":"customer","_index":"customer"}}
{"doc":{"name":"Roberto","whenModified":1459206556280,"version":2}}
{"update":{"_id":"5","_type":"order","_index":"order"}}
{"doc":{"customer":{"id":2,"status":"NEW","name":"Roberto","billingAddress":null}}}
{"update":{"_id":"2","_type":"order","_index":"order"}}
{"doc":{"customer":{"id":2,"status":"NEW","name":"Roberto","billingAddress":null}}}
{"update":{"_id":"4","_type":"contact","_index":"contact"}}
{"doc":{"customer":{"id":2,"name":"Roberto"}}}
- 第一个条目更新客户索引
- 第二个和第三个更新订单 5 和订单 2(Roberto 的相关订单)
- 第四个更新联系人 4(Roberto 的相关联系人)
嵌套路径
对于每个 嵌套路径
,Ebean 将执行 ElasticSearch 扫描查询,以查找索引中需要更新的条目。
查找相关订单
{"fields":["customer.id","id"],"query":{"filtered":{
"filter":{
"terms":{"customer.id":[2]}
}
}}}
查找相关联系人
{"fields":["customer.id","id"],"query":{"filtered":{
"filter":{
"terms":{"customer.id":[2]}
}
}}}
它将针对数据库执行 ORM 查询,以构建要包含在批量 API 调用中的 JSON,但如上所述,它将执行 ElasticSearch 扫描查询以查找所有需要更新的相关条目。
例如,嵌入式国家/地区
在下面的示例中,客户索引包含账单和送货地址的嵌入式文档,而这反过来又嵌入了国家/地区。在此示例中,"billingAddress.country.code"
和 "shippingAddress.country.code"
是 嵌套路径
,Ebean 需要检查这些路径以查看当国家/地区名称更改时需要更新哪些客户索引。
在下面的示例中,国家/地区嵌入在客户索引中,既在账单地址中又在送货地址中。当我们更新国家/地区时,还需要更新其账单或送货地址中包含该国家/地区的任何客户文档。
@DocStore
@Entity
public class Customer extends BasicDomain {
...
@DocEmbedded(doc = "*,country(*)")
@ManyToOne(cascade = CascadeType.ALL)
Address billingAddress;
@DocEmbedded(doc = "*,country(*)")
@ManyToOne(cascade = CascadeType.ALL)
Address shippingAddress;
例如,嵌套路径 - billingAddress.country.code
Ebean 将使用 嵌套路径
对 ElasticSearch 执行扫描查询,以便找到由于嵌入式文档更改而需要更新的文档。
查找 billingAddress.country.code = SA 的客户
{"fields":["billingAddress.id","id"],"query":{
"filtered":{"filter":
{"terms":{"billingAddress.country.code":["SA"]}
}}
}}
查找 shippingAddress.country.code = SA 的客户
{"fields":["shippingAddress.id","id"],"query":{
"filtered":{"filter":
{"terms":{"billingAddress.country.code":["SA"]}
}}
}}
查找 customer.billingAddress.country.code = SA 的订单
国家/地区也通过 customer.billingAddress 嵌入在订单索引中,因此我们还将找到具有此嵌入式国家/地区的订单。
{"fields":["customer.id","id"],"query":{"filtered":{
"filter":{
"terms":{"customer.billingAddress.country.code":["SA"]}
}
}}}