写点什么

AWS 云搜索的使用:极简 Java API

  • 2013-07-18
  • 本文字数:14877 字

    阅读完需:约 49 分钟

当前,许多应用重度依赖于搜索功能。从电子商务网站中寻找合适的产品,到社交网络中搜索寻人,再到地图网站中寻找 POI 和地址,依赖于搜索的应用非常广泛。

亚马逊新推出的云搜索服务,为自行实现搜索功能或定制安装 Apache Lucene Apache Solr elasticsearch 等流行产品提供了可行的替代方式。他们这样描述该服务:

“它是一个完全托管的云搜索服务,该服务允许用户十分方便地在应用中集成快速且高度可扩展的搜索功能。【它】让用户摆脱了运营和扩展搜索平台的负担。用户不用再去关心硬件配置、数据分区和软件补丁的问题了。”

这个实现方式中,我们发现的唯一缺点是缺乏用于上传数据和实现搜索的 Java API。虽然亚马逊提供的 REST API 也能达到上述的目的,但是在 Java 代码中使用它们并不方便。为了简化搜索调用和数据上传功能,我们开发了一些简单的 Java API,将在本文中逐一介绍。

数据定义

尽管亚马逊提供了数据上传和搜索响应的数据定义(以 XML JSON 两种方式),但数据上传的文档中仅定义了 Relax NG 模式,而搜索响应则未定义任何模式。

在我们的实现方式中,我们决定使用 XML 数据格式而不是 JSON,这是因为进行 XML 数据封装更加简单——XML 使用规范的数据格式,而 JSON 则是动态的(JSON 的标签是动态定义,每个请求各异)。我们分别用下边的两种模式(列表 1 和列表 2)来上传数据和搜索结果。

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
………………………………………………………………………………………….
<xsd:complexType name="fieldType">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="field_nameType" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="addType">
<xsd:sequence>
<xsd:element name="field" type="fieldType" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="id" type="IDType" />
<xsd:attribute name="version" type="versionType" />
<xsd:attribute name="lang" type="xsd:language" />
</xsd:complexType>
<xsd:complexType name="deleteType">
<xsd:attribute name="id" type="IDType" />
<xsd:attribute name="version" type="versionType" />
</xsd:complexType>
<xsd:complexType name="batchType">
<xsd:sequence>
<xsd:element name="add" type="addType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="delete" type="deleteType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="batch" type="batchType" />
<xsd:simpleType name="statusType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="success"/>
<xsd:enumeration value="error" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="errorsType">
<xsd:sequence>
<xsd:element name="error" type="xsd:string" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="warningsType">
<xsd:sequence>
<xsd:element name="warning" type="xsd:string" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="responseType">
<xsd:sequence>
<xsd:element name="errors" type="errorsType" minOccurs="0" />
<xsd:element name="warnings" type="warningsType" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="status" type="statusType"/>
<xsd:attribute name="adds" type="xsd:int"/>
<xsd:attribute name="deletes" type="xsd:int"/>
</xsd:complexType>
<xsd:element name="response" type="responseType" />
</xsd:schema>
Listing 1 Upload data schema
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://cloudsearch.amazonaws.com/2011-02-01/results"
xmlns="http://cloudsearch.amazonaws.com/2011-02-01/results"
elementFormDefault="qualified">
<xsd:complexType name="constraintType">
<xsd:attribute name="value" type="xsd:string"/>
<xsd:attribute name="count" type="xsd:int"/>
</xsd:complexType>
<xsd:complexType name="facetType">
<xsd:sequence>
<xsd:element name="constraint" type="constraintType" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="facetsType">
<xsd:sequence>
<xsd:element name="facet" type="facetType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="infoType">
<xsd:attribute name="rid" type="xsd:string" />
<xsd:attribute name="time-ms" type="xsd:int" />
<xsd:attribute name="cpu-time-ms" type="xsd:int" />
</xsd:complexType>
<xsd:complexType name="dType">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="name" type="xsd:string" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="hitType">
<xsd:sequence>
<xsd:element name="d" type="dType" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="hitsType">
<xsd:sequence>
<xsd:element name="hit" type="hitType" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="found" type="xsd:int" />
<xsd:attribute name="start" type="xsd:int" />
</xsd:complexType>
<xsd:complexType name="resultsType">
<xsd:sequence>
<xsd:element name="rank" type="xsd:string" />
<xsd:element name="match-expr" type="xsd:string" />
<xsd:element name="hits" type="hitsType" minOccurs="0"/>
<xsd:element name="facets" type="facetsType" minOccurs="0"/>
<xsd:element name="info" type="infoType" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="results" type="resultsType"/>
<xsd:complexType name="messageType">
<xsd:attribute name="severity" type="xsd:string" />
<xsd:attribute name="code" type="xsd:string" />
<xsd:attribute name="message" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="errorType">
<xsd:sequence>
<xsd:element name="error" type="xsd:string" />
<xsd:element name="rid" type="xsd:string" />
<xsd:element name="time-ms" type="xsd:int" />
<xsd:element name="cpu-time-ms" type="xsd:int" />
<xsd:element name="messages" type="messageType" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="error" type="errorType" />
</xsd:schema>
Listing 2 Search results data schema

我们使用 xjc binding compiler 生成上述的两种模式的 Java 类,这样就能通过 Java Architecture for XML Binding (JAXB) 进行自动封装 / 解封装。

查询定义

除了数据定义,实现搜索 API 还需要查询定义。我们已经创建了一组类,用来实现亚马逊的查询定义

这个搜索查询的核心是过滤器。我们引入了 SearchQueryFilter 接口,并提供了两种实现方式——Search Query Value Filter(列表 3)和 Search Query Filter Operation(列表 4)。

复制代码
public class SearchQueryValueFilter implements SearchQueryFilter{
private String _field;
private String _value;
private boolean _isExclude;
private boolean _isNumeric;
public SearchQueryValueFilter(){}
public SearchQueryValueFilter(String field, String value, boolean isNumeric, boolean isExclude){
_field = field;
_value = value;
_isExclude = isExclude;
_isNumeric = isNumeric;
}
public String getField() {
return _field;
}
public void setField(String field) {
_field = field;
}
public String getValue() {
return _value;
}
public void setValue(String value) {
_value = value;
}
public boolean isExclude() {
return _isExclude;
}
public void setExclude(boolean isExclude) {
_isExclude = isExclude;
}
public boolean isNumeric() {
return _isNumeric;
}
public void setNumeric(boolean isNumeric) {
_isNumeric = isNumeric;
}
@Override
public String toString(){
StringBuffer sb = new StringBuffer();
if(_isExclude){
sb.append("(not ");
}
if(_field != null){
sb.append(_field);
sb.append(":");
}
if(!_isNumeric){
sb.append("'");
}
sb.append(_value);
if(!_isNumeric){
sb.append("'");
}
if(_isExclude){
sb.append(")");
}
return sb.toString();
}
}
Listing 3 Value filter implementation
public class SearchQueryFilterOperation implements SearchQueryFilter {
List<SearchQueryFilter> _filters;
FilterOperation _operation;
public SearchQueryFilterOperation(){
_operation = FilterOperation.and;
_filters = new LinkedList<SearchQueryFilter>();
}
public List<SearchQueryFilter> getFilters() {
return _filters;
}
public void setFilters(List<SearchQueryFilter> filters) {
_filters = filters;
}
public void addFilters(SearchQueryFilter filter) {
_filters.add(filter);
}
public FilterOperation getOperation() {
return _operation;
}
public void setOperation(FilterOperation operation) {
_operation = operation;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("(");
sb.append(_operation);
for(SearchQueryFilter f : _filters){
sb.append(" ");
sb.append(f);
}
sb.append(")");
return sb.toString();
}
public enum FilterOperation{
and, or
}
}
Listing 4 Operation filter implementation

Search Query Value Filter 类支持开发者使用等于、小于、大于、区间(同样支持负值比较)等运算符设置单个字段的限制。而 Search Query Filter Operation 类还支持开发者使用 AND/OR 操作符,将多个 Search Query Value Filters 和 Search Query Filter Operations 组合使用。通过这两个类的组合使用,就能实现亚马逊云搜索所支持的任意查询过滤器的表达式了。

亚马逊云搜索支持分面分类(Faceted classification ):

“分面分类系统支持对一个对象赋予多个特征(属性),支持按照多种方式对分类排序,而非按照单一的、预定的分类顺序。一个分面包括‘定义清晰、相互独立、完全穷尽的方面,某类属性、特征或是特定的主题’。【1】例如,藏书可以按照作者,主题,日期等归类。”

分面分类应用于分面搜索系统,用户在这种系统中能够从多方面进行信息的导航(译者注:如书籍可以从作者、主题、出版日期等不同的分面),多方面对应于不同顺序的分面。

AWS 支持按分面控制搜索执行以及对搜索结果排序。同时还支持开发者控制返回的搜索结果中包含的分面数量。所有的分面操作由 Search Query Facet(列表 5)这个类来实现。

复制代码
public class SearchQueryFacet {
private String _name;
private int _maxFacets;
private List<String> _constraints;
private FacetSort _sort;
public SearchQueryFacet(String name){
_name = name;
_maxFacets = -1;
_constraints = null;
_sort = FacetSort.none;
}
public SearchQueryFacet(String name, int maxFacets){
_name = name;
_maxFacets = maxFacets;
_constraints = null;
_sort = FacetSort.none;
}
public SearchQueryFacet(String name, int maxFacets, FacetSort sort){
_name = name;
_maxFacets = maxFacets;
_constraints = null;
_sort = sort;
}
public SearchQueryFacet(String name, int maxFacets, List<String> constraints){
_name = name;
_maxFacets = maxFacets;
_constraints = constraints;
_sort = FacetSort.none;
}
public SearchQueryFacet(String name, List<String> constraints){
_name = name;
_maxFacets = -1;
_constraints = constraints;
_sort = FacetSort.none;
}
public SearchQueryFacet(String name, FacetSort sort, List<String> constraints){
_name = name;
_maxFacets = -1;
_constraints = constraints;
_sort = sort;
}
public SearchQueryFacet(String name, FacetSort sort){
_name = name;
_maxFacets = -1;
_constraints = null;
_sort = sort;
}
public String getName() {
return _name;
}
public void setName(String name) {
_name = name;
}
public int getMaxFacets() {
return _maxFacets;
}
public void setMaxFacets(int maxFacets) {
_maxFacets = maxFacets;
}
public FacetSort getSort() {
return _sort;
}
public void setSort(FacetSort sort) {
_sort = sort;
}
public int get_maxFacets() {
return _maxFacets;
}
public void set_maxFacets(int _maxFacets) {
this._maxFacets = _maxFacets;
}
public List<String> getConstraints() {
return _constraints;
}
public void setConstraints(List<String> constraints) {
_constraints = constraints;
}
public void addConstraint(String constraint) {
if(_constraints == null)
_constraints = new LinkedList<String>();
_constraints.add(constraint);
}
@Override
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("&facet=");
sb.append(_name);
if(_maxFacets > 0){
sb.append("&facet-");
sb.append(_name);
sb.append("-top-n=");
sb.append(_maxFacets);
}
if((_constraints != null) && (_constraints.size() > 0)){
sb.append("&facet-");
sb.append(_name);
sb.append("-constraints=");
boolean first = true;
for(String c : _constraints){
if(!first)
sb.append("%2C");
else
first = false;
sb.append("%27");
sb.append(c);
sb.append("%27");
}
}
if(!_sort.equals(FacetSort.none)){
sb.append("&facet-");
sb.append(_name);
sb.append("-sort=");
sb.append(_sort);
}
return sb.toString();
}
public enum FacetSort{
none, alpha, count, max, sum
}
}
Listing 5 Facets control class

最后 Search Query Sort 类(列表 6)实现了开发者对结果排序的控制。

复制代码
public class SearchQuerySort {
private List<SearchRank> _ranks;
public SearchQuerySort(){
_ranks = new LinkedList<SearchRank>();
}
public void addRank(SearchRank rank){
_ranks.add(rank);
}
@Override
public String toString(){
if(_ranks.size() == 0)
return null;
StringBuffer sb = new StringBuffer();
sb.append("&rank=");
boolean first = true;
for(SearchRank r : _ranks){
if(!first)
sb.append("%2C");
else
first = false;
sb.append(r);
}
return sb.toString();
}
public static class SearchRank{
private String _name;
private boolean _ascending;
public SearchRank(){
_ascending = true;
}
public SearchRank(String name){
_ascending = true;
_name = name;
}
public SearchRank(String name, boolean ascending){
_ascending = ascending;
_name = name;
}
@Override
public String toString(){
if(_ascending)
return _name;
return "-" + _name;
}
}
}
Listing 6 Sort control class

CloudSearch 查询除了将所有的参数汇总到一起,还增加了页码信息和一组返回字段

这个查询类还提供了一个方法——HTTP 查询转换(列表 7),将搜索查询的所有部分汇总,并生成能被搜索处理的 HTTP 字符串。

复制代码
public String toHttpQuery() throws Exception{
StringBuffer sb = new StringBuffer();
sb.append("?results-type=xml");
if(_size > 0){
sb.append("&size=");
sb.append(_size);
}
if(_start > 0){
sb.append("&start=");
sb.append(_start);
}
if((_fields != null) && (_fields.size() > 0)){
sb.append("&return-fields=");
boolean first = true;
for(String f : _fields){
if(!first)
sb.append("%2C");
else
first = false;
sb.append(f);
}
}
if(_filter != null){
if(_filter instanceof SearchQueryValueFilter)
sb.append("&q=");
else
sb.append("&bq=");
sb.append(URLEncoder.encode(_filter.toString(), "UTF8"));
}
if((_facets != null) && (_facets.size() > 0)){
for(SearchQueryFacet f : _facets){
sb.append(f);
}
}
if((_sorts != null) && (_sorts.size() > 0)){
for(SearchQuerySort s : _sorts){
sb.append(s);
}
}
return sb.toString();
}
Listing 7 Convert to HTTP query method

我们使用 Apache HttpComponents 来实现与亚马逊云搜索的通信。

测试我们的 API

我们使用亚马逊提供的 IMDB 样例来进行验证。首次单元测试(列表 8)用于验证我们实现的搜索 API。

复制代码
public class SearchAPITester extends TestCase {
private static final String SearchURL = "search-imdb-movies-ab4fpqw4eocczpgsnrtlu4rn7i.us-east-
1.cloudsearch.amazonaws.com";
private CloudSearchClient client;
protected void setUp() throws Exception {
client = new CloudSearchClient(SearchURL);
}
protected void tearDown() {
client.close();
}
public void testSearch() throws Exception{
SearchQueryValueFilter f1 = new SearchQueryValueFilter("title", "star", false, false);
SearchQueryValueFilter f11 = new SearchQueryValueFilter("title", "war", false, true);
SearchQueryValueFilter f2 = new SearchQueryValueFilter("year", "..2000", true, false);
SearchQueryFilterOperation f12 = new SearchQueryFilterOperation();
f12.setOperation(FilterOperation.or);
f12.addFilters(f1);
f12.addFilters(f11);
SearchQueryFilterOperation f3 = new SearchQueryFilterOperation();
f3.addFilters(f12);
f3.addFilters(f2);
CloudSearchQuery query = new CloudSearchQuery(f3);
query.addField("actor");
query.addField("director");
query.addField("title");
query.addField("year");
SearchQueryFacet sf = new SearchQueryFacet("genre", 5, FacetSort.alpha);
sf.addConstraint("Drama");
sf.addConstraint("Sci-Fi");
query.addFacet(sf);
SearchQuerySort sort = new SearchQuerySort();
SearchRank r1 = new SearchRank("title");
SearchRank r2 = new SearchRank("year", false);
sort.addRank(r1);
sort.addRank(r2);
query.addSort(sort);
try {
System.out.println("Test 1 ");
SearchResults result = client.search(query);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Listing 8 Search API tester

该测试获得的结果(列表 9),和直接通过亚马逊 REST API 获得的结果相同。

SearchResults
[ID=6ddcaa561c05c4cc3dae0f2d67b89419fbfea467ac6292b612dfb3a4a547692c6bea0194d6d37630b171b100197578dc, hitcount=1942, start=0, expression=(and (or title:‘star’ (not title:‘war’)) year:…2000), execution time=35ms, cpu execution time=0ms
Hit [ID=tt0092493, values={title:[‘Crocodile’ Dundee II]year:[1988]actor:[Blinco, Maggie,Dingo, Ernie,Hogan, Paul,Holt, Jim,Kozlowski, Linda,Meillon, John,Mercurio, Gus,Rackman, Steve,Scavone, Anthony,Skilton, Gerry,Wilson, Alec]director:[Cornell, John]}]
Hit [ID=tt0078718, values={title:[…And Justice for All.]year:[1979]actor:[Bryggman, Larry,Christian, Robert,Forsythe, John,Lahti, Christine,Levene, Sam,Pacino, Al,Strasberg, Lee,Tambor, Jeffrey,Waites, Thomas G.,Warden, Jack,Williams, Jonathan]director:[Jewison, Norman]}]
Hit [ID=tt0078721, values={title:[10]year:[1979]actor:[Andrews, Julie,Crosby, Denise,Daly, Rad,Dennehy, Brian,Derek, Bo,Haven, Annette,Jones, Sam J.,LeMay, Dorothy,Money, Constance,Moore, Dudley,Royalle, Candida,Serena,Showalter, Max,Volz, Nedra,Wallace, Dee,Webber, Robert]director:[Edwards, Blake]}]
Hit [ID=tt0147800, values={title:[10 Things I Hate About You]year:[1999]actor:[Babin, Michelle,Bennett, Tim,Blake, Shelsie,Gordon-Levitt, Joseph,Junger, Gil,Keegan, Andrew,Kountz, Daniel,Krumholtz, David,Ledger, Heath,Magnuson, Katy,Matthews, Amber,Miller, Larry,Mitchell, Daryl,O’Neill, Bridget,Oleynik, Larisa,Pratt, Susan May,Snider, Tommy,Stiles, Julia,Union, Gabrielle,Zorich, Jay]director:[Junger, Gil]}]
Hit [ID=tt0214388, values={title:[100 Girls]year:[2000]actor:[Billman, Ange,Chriqui, Emmanuelle,DeBello, James,Graham, Aimee,Grant, Tanisha,Green, Johnny,Heigl, Katherine,Hiraizumi, Gina,Musiala, Agnieszka,Oleynik, Larisa,Pressly, Jaime,Ribisi, Marissa,Tucker, Jonathan]director:[Davis, Michael]}]
Hit [ID=tt0115433, values={title:[101 Dalmatians]year:[1996]actor:[Close, Glenn,Daniels, Jeff,Fielder, Harry,Fraser, Hugh,Laurie, Hugh,McInnerny, Tim,Mullard, Arthur,Plowright, Joan,Richardson, Joely,Richardson, Laurence,Shrapnel, John,Weiss, Zohren,Welker, Frank,Williams, Mark]director:[Herek, Stephen]}]
Hit [ID=tt0050083, values={title:[12 Angry Men]year:[1957]actor:[Balsam, Martin,Begley, Ed,Binns, Edward,Bond, Rudy,Cobb, Lee J.,Fiedler, John,Fonda, Henry,Kelly, James,Klugman, Jack,Marshall, E.G.,Nelson, Billy,Savoca, John,Sweeney, Joseph,Warden, Jack]director:[Lumet, Sidney]}]
Hit [ID=tt0103594, values={title:[1492: Conquest of Paradise]year:[1992]actor:[Assante, Armand,Dean, Loren,Depardieu, Gérard,Dunn, Kevin,Karyo, Tchéky,Langella, Frank,Molina, Ángela,Montero, Silvia,Rey, Fernando,Weaver, Sigourney,Wincott, Michael]director:[Scott, Ridley]}]
Hit [ID=tt0078723, values={title:[1941]year:[1979]actor:[Aykroyd, Dan,Beatty, Ned,Belushi, John,Caan, James,Cheshire, Denise,Gary, Lorraine,Hamilton, Murray,Lassick, Sydney,Lauren, Mo,Lee, Christopher,Marshall, Penny,Matheson, Tim,Mifune, Toshirô,Moriarty, Steve,Oates, Warren,Robinson, Hank,Rothstein, Debbie,Stack, Robert]director:[Spielberg, Steven]}]
Hit [ID=tt0046672, values={title:[20000 Leagues Under the Sea]year:[1954]actor:[Cooper, Ted,Daheim, John,Douglas, Kirk,Gargan, Jack,Graham, Fred,Harvey, Harry,Helton, Percy,Kerrigan, J.M.,Lorre, Peter,Lukas, Paul,Lummis, Dayton,Marr, Eddie,Mason, James,Mitchell, Laurie,Pall, Gloria,Pennick, Jack,Vigran, Herb,Wilke, Robert J.,Young, Carleton,de Corsia, Ted]director:[Fleischer, Richard]}]
Facet [name=genre, values={(Sci-Fi,237},(Drama,1063}}]
]
列表 9 搜索 API 测试结果

第二次测试(列表 10)用来验证文档的添加和删除。

复制代码
public class DocumentAPITester extends TestCase {
private static final String DocumentURL = "doc-imdb-movies-ab4fpqw4eocczpgsnrtlu4rn7i.us-east-
1.cloudsearch.amazonaws.com";
private CloudSearchDocumentClient client;
private BatchType batch;
protected void setUp() throws Exception {
client = new CloudSearchDocumentClient(DocumentURL);
FieldType title = new FieldType();
title.setName("title");
title.setValue("The Seeker: The Dark Is Rising");
FieldType director = new FieldType();
director.setName("director");
director.setValue("Cunningham, David L.");
FieldType genrea = new FieldType();
genrea.setName("genre");
genrea.setValue("Adventure");
FieldType genred = new FieldType();
genred.setName("genre");
genred.setValue("Drama");
FieldType genref = new FieldType();
genref.setName("genre");
genref.setValue("Fantasy");
FieldType genret = new FieldType();
genret.setName("genre");
genret.setValue("Thriller");
FieldType actor1 = new FieldType();
actor1.setName("actor");
actor1.setValue("McShane, Ian");
FieldType actor2 = new FieldType();
actor2.setName("actor");
actor2.setValue("Eccleston, Christopher");
FieldType actor3 = new FieldType();
actor3.setName("actor");
actor3.setValue("Conroy, Frances");
FieldType actor4 = new FieldType();
actor4.setName("actor");
actor4.setValue("Conroy, Frances");
FieldType actor5 = new FieldType();
actor5.setName("actor");
actor5.setValue("Ludwig, Alexander");
FieldType actor6 = new FieldType();
actor6.setName("actor");
actor6.setValue("Crewson, Wendy");
FieldType actor7 = new FieldType();
actor7.setName("actor");
actor7.setValue("Warner, Amelia");
FieldType actor8 = new FieldType();
actor8.setName("actor");
actor8.setValue("Cosmo, James");
FieldType actor9 = new FieldType();
actor9.setName("actor");
actor9.setValue("Hickey, John Benjamin");
FieldType actor10 = new FieldType();
actor10.setName("actor");
actor10.setValue("Piddock, Jim");
FieldType actor11 = new FieldType();
actor11.setName("actor");
actor11.setValue("Lockhart, Emma");
AddType add = new AddType();
add.setId("tt0484562");
add.setVersion(1l);
add.setLang("en");
add.getField().add(title);
add.getField().add(director);
add.getField().add(genrea);
add.getField().add(genred);
add.getField().add(genref);
add.getField().add(genret);
add.getField().add(actor1);
add.getField().add(actor2);
add.getField().add(actor3);
add.getField().add(actor4);
add.getField().add(actor5);
add.getField().add(actor6);
add.getField().add(actor7);
add.getField().add(actor8);
add.getField().add(actor9);
add.getField().add(actor10);
add.getField().add(actor11);
DeleteType delete = new DeleteType();
delete.setId("tt0301199");
delete.setVersion(1l);
batch = new BatchType();
batch.getAdd().add(add);
batch.getDelete().add(delete);
}
protected void tearDown() {
client.close();
}
public void testSearch() throws Exception{
try {
System.out.println("Test 1 ");
ResponseType result = client.index(batch);
System.out.println("Status " + result.getStatus() + " Added " + result.getAdds() + " Deleted " +
result.getDeletes());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Listing 10 Document upload tester

测试也获得了预期的结果(列表 11)

复制代码
Status SUCCESS Added 1 Deleted 1
Listing 11 Document upload test results

总结

上面这些简单的 Java API 实现了亚马逊云搜索的功能,显著简化了亚马逊云搜索功能在已有 Java 应用中的使用,必然会扩大应用的影响范围。

关于作者

Boris Lublinsky博士是诺基亚首席架构师,主要从事大数据、SOA、BPM、中间件的实现。在此之前 Boris 曾经是 Herzum 软件公司的首席架构师,为客户设计大规模的 SOA 系统,曾负责 CNA 保险公司的企业架构,参与了 CNA 的系统集成与 SOA 策略的设计和实现,构建了应用框架并实现了面向服务的架构。Boris 在企业、技术架构,软件工程方面有超过 25 年的经验。他还是 OASIS SOA 参考模型技术委员会的活跃会员,也是《Applied SOA:Service-Oriented Architecture and Design Strategies》,一书的共同作者。他还发表了大量架构、编程、大数据、SOA、BPM 的相关文章。

查看英文原文:**** Using AWS Cloud Search


感谢康锦龙对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2013-07-18 07:445151

评论

发布
暂无评论
发现更多内容

2023年AI十大展望:GPT-4领衔大模型变革,谷歌拉响警报,训练数据告急

OneFlow

人工智能 深度学习

划重点,2023 年最值得关注的 8 大技术趋势

PreMaint

人工智能 云计算 5G 技术趋势 元宇宙

云渲染市场安全吗?如何保证数据安全、财产安全?

Renderbus瑞云渲染农场

云渲染 云渲染农场 云渲染安全

IoT物联网平台「设备影子」开发实战——实践类

阿里云AIoT

JavaScript json 物联网 API 储存

EMQ携“云边协同IIoT解决方案”亮相2022世界工业互联网产业大会

EMQ映云科技

人工智能 物联网 IoT 云边协同 企业号 1 月 PK 榜

一文了解华为FusionInsight MRS HBase的集群隔离方案RSGroup

华为云开发者联盟

大数据 后端 华为云 企业号 1 月 PK 榜

为iframe正名,你可能并不需要微前端

阿里巴巴终端技术

前端 微前端 iframe

YMatrix v5.0 发布:138 项深度优化,重塑集群架构,实现性能及高可用性全方位提升!

YMatrix 超融合数据库

OLAP OLTP 超融合数据库 数据库架构设计 YMatrix

IM通讯协议专题学习(七):手把手教你如何在NodeJS中从零使用Protobuf

JackJiang

即时通讯 protobuf im开发

金融数字化为何需要低代码开发平台

力软低代码开发平台

重磅发布丨从云原生到Serverless,先行一步看见更大的技术想象力

阿里巴巴云原生

阿里云 Serverless 云原生

累计装机超过300万套 欧拉操作系统跨越生态拐点

Geek_2d6073

2022大厂投资盘点:最大的投资就是减少投资

ToB行业头条

基于云基础设施快速部署 RocketMQ 5.0 集群

Apache RocketMQ

RocketMQ 云原生 消息队列

基于开源体系的云原生微服务治理实践与探索

阿里巴巴云原生

阿里云 开源 云原生 service mesh

站酷基于服务网格ASM的生产实践

阿里巴巴云原生

阿里云 云原生 服务网格

IoT物联网设备「固件升级」OTA,「资源包更新」最佳实践——实践类

阿里云AIoT

运维 物联网 数据格式

安畅SmartOps混合云平台架构的演进之道

安畅Anchnet

云原生

Apache RocketMQ 斩获 InfoQ 2022 年度十大开源新锐项目

阿里巴巴云原生

阿里云 云原生 Apache RocketMQ

高并发环境下构建缓存服务,你需要注意这6点

华为云开发者联盟

高并发 开发 华为云 企业号 1 月 PK 榜

2022 Apache APISIX 年度记忆

API7.ai 技术团队

api 网关 APISIX 年终盘点 apache 社区

强强联手 | 尚硅谷&腾讯云EMR离线数仓教程发布

小谷哥

Apache Tomcat 存在 JsonErrorReportValve 注入漏洞(CVE-2022-45143)

墨菲安全

安全漏洞 CVE

2023 年openEuler 社区技术委员会增选,新增2位委员

openEuler

Linux 开源 操作系统 openEuler 资讯

旅游业复苏在即,区块链赋能智慧旅游新体验

旺链科技

区块链 区块链技术 区块链技术应用

DTALK直播预约 | 金融行业嘉宾分享:金融机构数据治理实践路径

袋鼠云数栈

AWS云搜索的使用:极简Java API_亚马逊云科技_Boris Lublinsky_InfoQ精选文章