写点什么

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

评论

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

3DCAT荣获2021金陀螺“年度XR行业技术创新奖”“年度优秀VR行业应用奖”两项大奖

3DCAT实时渲染

云计算 教育 VR/AR 渲染 渲染器

使用无参数函数进行命令执行

网络安全学海

黑客 网络安全 信息安全 渗透测试 安全漏洞

(1-14/14) 首位销售人员

mtfelix

300天创作 2022Y300P

Android 64位架构适配

百瓶技术

andiod 客户端

【高并发】导致并发编程频繁出问题的“幕后黑手”

冰河

并发编程 多线程 高并发 协程 异步编程

redis未授权访问漏洞复现

喀拉峻

redis 黑客 网络安全 安全 信息安全

如何处理消息丢失问题?

JavaEdge

1月月更

混沌工程之 Linux 网络故障模拟工具TC

zuozewei

Linux 混沌工程 1月月更

ThinkPHP6和GatewayWorker简单的示例

CRMEB

深入浅出Apache Pulsar(1):Pulsar vs Kafka

云智慧AIOps社区

kafka 云原生 消息队列 kafka运维 Apache Pulsar 消息系统

如何基于知识图谱实体解析技术进行数据优化?

索信达控股

人工智能 AI 知识图谱 数据优化 索信达控股

聚类算法有哪些?又是如何分类?

郑州埃文科技

数据分析 聚类算法

政法委跨单位重点人员联防联控平台建设,治安防控系统开发

a13823115807

网络安全kali渗透学习 web渗透入门 Kali系统的国内源配置

学神来啦

表单数据高级搜索功能设计

全象云低代码

搜索引擎 前端 低代码 搜索 表单

Kafka 为什么这么快?多的是你不知道的事

码哥字节

kafka 消息队列 1月日更 1月月更

人效将是快消品企业未来发展的最大瓶颈

百度大脑

人工智能

Go 语言快速入门指南:Go 并发初识

宇宙之一粟

golang 并发 Go 语言 1月月更

实战 MongoDB Aggregate

PingCode研发中心

mongo pipeline Expression

腾讯自选股如何实现单位小时内完成千万级数据运算

ninetyhe

腾讯 海量数据 分布式,

Hoo虎符研究院|区块链简报 20220117期

区块链前沿News

Hoo虎符 Hoo 虎符研究院 区块链资讯

前额皮质如何影响我们的工作效率?

LigaAI

工作效率 脑科学

十大视频场景化应用工具+五大视频领域冠军/顶会算法重磅开源!

百度大脑

恒源云(GPUSHARE)_实例关机后如何操作迁移?

恒源云

gpu 运维 实例

使用Rainbond打包业务模块,实现业务积木式拼装

北京好雨科技有限公司

CPython 性能将提升 5 倍?faster-python 项目 PEP 659 源码级解读

阿里巴巴终端技术

Python 源码 源码分析 CPython

架构实战营第 4 期 -- 模块七作业

烈火干柴烛灭田边残月

架构实战营

Scrum Master如何参与每日Scrum(Daily Scrum)

Bruce Talk

Scrum 敏捷 Agile Coach/Facilitate

打造手淘极简包的轻量化框架

阿里巴巴终端技术

ios android 框架设计 移动开发 包大小

APICloud 原生模块、H5模块、多端组件使用教程

YonBuilder低代码开发平台

前端开发 APP开发 APICloud 模块 跨端开发

Python 为什么不设计 do-while 循环结构?

Python猫

Python

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