【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

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:444789

评论

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

架构实战营 模块三 作业

三叔叔_拖延症晚期

顶级高手改变模型|靠谱点评

无量靠谱

告别尴尬-找回MySQL数据库密码

龙眼果

MySQL

鸿蒙轻内核定时器Swtmr:不受硬件和数量限制,满足用户需求

华为云开发者联盟

鸿蒙 定时器 OpenHarmony LiteOS-M 软件定时器

架构实战营 - 模块 3 - 作业

Vincent

#架构实战营

外包学生管理系统的架构

feitian

外包学生管理系统的架构文档

木云先森

架构实战营

Vue进阶(幺玖肆):keep-alive 实现页面缓存

No Silver Bullet

Vue 7月日更 keep-alive 动态组件

如何用Camtasia给视频添加字幕?

淋雨

视频剪辑 Camtasia 录屏软件

测评EasyRecovery的数据恢复效果与多种功能

淋雨

EasyRecovery 文件恢复 硬盘数据恢复

抖音引流获客APP系统开发

获客I3O6O643Z97

抖音霸屏 抖音、快手获客系统

NumPy之:多维数组中的线性代数

程序那些事

Python 数据分析 Numpy 程序那些事

Load and preprocess images

毛显新

Python 深度学习 tensorflow 计算机视觉

Vue进阶(幺捌陆):异步请求导致页面数据渲染错误问题解决

No Silver Bullet

Vue 异步请求 7月日更 $set

架构实战营第一期 -- 模块三作业

clay

架构实战营

模块3作业“学生管理系统”架构设计

王小森

架构实战营模块三作业

tt

架构实战营

模块三:学生管理系统架构详细设计

柱林

深入浅出Node.js第一章阅读总结

Alex

JavaScript node.js

Python OpenCV 学习轻松点,复习一下模板匹配吧

梦想橡皮擦

Python 7月日更

模块三 作业

SAKIN

【架构训练营】模块三作业

zclau

OPPO小布助手算法系统的探索、实践与思考

OPPO小布助手

人工智能 深度学习 对话 智能助手 智能对话

矿机矿池挖矿系统开发

获客I3O6O643Z97

挖矿矿池系统开发案例 PHA矿机挖矿

架构实战营模块 3 作业

zlz

十大自动化测试工具,你在用哪些?

禅道项目管理

测试 自动化测试

未雨绸缪最好,亡羊补牢也行|靠谱点评

无量靠谱

模块三作业

VE

架构实战营

悲剧!IDEA 突然找不到类了?

楼下小黑哥

Java 后端 IDEA

Tensorflow小技巧(一)

毛显新

Python tensorflow pandas

技术人生第5篇——浅谈如何成为技术一号位?

阿里巴巴中间件

云计算 阿里云 云原生 中间件 技术人生

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