solrj-WiKi

  solrj是一个访问solr的客户端,它提供了一个接口,用于添加、更新、删除索引数据.

solrj跨版本兼容

   solrj一般保持向后兼容,所以你可以使用新版本的solrj访问老版本的solr服务,也可以使用老版本的solrj访问新版本的solr.

  如果混合了1.x和以后的主版本,那么必须设置响应解析器XML:

server.setParser(new XMLResponseParser());

 

   3.x和4.x或者以后的版本混合的solr工作正常,只要不更改这个请求writer为二进制格式.

  有一个例外情况javabin格式,兼容方式在主版本之间已经改变.

设置类路径

  solrj需要的最小jar包集合:

  /dist:

    apache-solr-solrj-*.jar

  /dist/solrj-lib:

    • commons-codec-1.3.jar
    • commons-httpclient-3.1.jar
    • commons-io-1.4.jar
    • jcl-over-slf4j-1.5.5.jar
    • slf4j-api-1.5.5.jar

    /lib:

     slf4j-jdk14-1.5.5.jar

Maven

  在官方maven仓库中solrj是可以使用的,加入一下依赖到pom.xml中:

<dependency>
    <artifactId>solr-solrj</artifactId>
    <groupId>org.apache.solr</groupId>
    <version>4.7.2</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

 

  如果使用EmbeddedSolrServer,加入一下依赖:

<dependency>
    <artifactId>solr-core</artifactId>
    <groupId>org.apache.solr</groupId>
    <version>4.7.2</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

 

   同样,如果使用了EmbeddedSolrServer,注意Servlet API的依赖:

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>3.0</version>
</dependency>

 

   如果碰到NoClassDefFoundError 异常,加入一下依赖:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.6.6</version>
</dependency>

 

 HttpSolrServer

  HttpSolrServer使用Apache Commons HTTP Client连接solr. 注意:在solr4.0中, CommonsHttpSolrServer 改成了 HttpSolrServer ,StreamingUpdateSolrServer 改成了ConcurrentUpdateSolrServer.

        String url = "http://localhost:8983/solr";
        /*
         * HttpSolrServer线程安全,如果使用下面构造器,必须对所有的请求重用相同的实例.如果实例在运行中创建的,它可能会导致连接泄漏.
         * 推荐的做法就是保持每个solr服务url的HttpSolrServer的静态实例,所有的请求都共享这个实例.
         * 参考https://issues.apache.org/jira/browse/SOLR-861查看更多细节.
         */
        SolrServer server = new HttpSolrServer(url);

具体可以参考:HttpSolrServer-采用静态工厂方法,创建HttpSolrServer单实例.如果你有更好的方法,可以回复一下,互相交流.

修改其他的Connection设置:

      String url = "http://localhost:8983/solr";
        HttpSolrServer server = new HttpSolrServer(url);
        server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
        server.setConnectionTimeout(5000); // 5 seconds to establish TCP
        // 下面的设置被完整的提供,它们一般不是必须选择的,最好是在查阅java文档之后来确认它们是否必须使用.
        server.setSoTimeout(1000); // socket read timeout
        server.setDefaultMaxConnectionsPerHost(100);
        server.setMaxTotalConnections(100);
        server.setFollowRedirects(false); // defaults to false
        // 允许压缩默认为false.
        // Server side must support gzip or deflate for this to have any effect.
        server.setAllowCompression(true);

 使用

   首先创建一个服务的实例:

 SolrServer server = new HttpSolrServer("http://HOST:8983/solr/");

 

   删除所有索引数据:

server.deleteByQuery( "*:*" );

 

 构造一个document:

    SolrInputDocument doc1 = new SolrInputDocument();
    doc1.addField( "id", "id1", 1.0f );
    doc1.addField( "name", "doc1", 1.0f );
    doc1.addField( "price", 10 );

 

   构造另外一个document,最好是批量处理document,因为每次调用solrServer,就是一次http调用.

    SolrInputDocument doc2 = new SolrInputDocument();
    doc2.addField( "id", "id2", 1.0f );
    doc2.addField( "name", "doc2", 1.0f );
    doc2.addField( "price", 20 );

 

   创建一个document的集合:

    Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
    docs.add( doc1 );
    docs.add( doc2 );

 

   添加集合到solrServer,并提交.

 server.add( docs );
server.commit()

 

  为了在添加documents之后立刻提交,可以这样使用:

  UpdateRequest req = new UpdateRequest();
  req.setAction( UpdateRequest.ACTION.COMMIT, false, false );
  req.add( docs );
  UpdateResponse rsp = req.process( server );

 

更新文档流(streamimg documents)

  大多数情况下,ConcurrentUpdateSolrServer就可以满足需要.作为一种选择,下面给出的解决方法可以应用

  这是一个在一个请求里更新所有文档的最优化的方案:

  

HttpSolrServer server = new HttpSolrServer();
Iterator<SolrInputDocument> iter = new Iterator<SolrInputDocument>(){
     public boolean hasNext() {
        boolean result ;
        // set the result to true false to say if you have more documensts
        return result;
      }

      public SolrInputDocument next() {
        SolrInputDocument result = null;
        // construct a new document here and set it to result
        return result;
      }
};
server.add(iter);

 

也可以使用addBeans(Iterator<?> beansIter)来索引对象.

直接添加POJO到Solr

  使用注解创建一个java bean.@Field可以用到字段(field)或者是方法(setter)上.如果索引字段名称和bean的字段名称不相同,可以使用别名来做一个映射,

import org.apache.solr.client.solrj.beans.Field;

 public class Item {
    @Field
    String id;

    @Field("cat")
    String[] categories;

    @Field
    List<String> features;

  }

 

  或者是在setter方法中添加:

    @Field("cat")
   public void setCategory(String[] c){
       this.categories = c;
   }

 

  获取SolrServer实例:

SolrServer server = getSolrServer();

 

  创建bean实例:

    Item item = new Item();
    item.id = "one";
    item.categories =  new String[] { "aaa", "bbb", "ccc" };

 

  添加到solr:

server.addBean(item);

 

  添加bean集合:

  List<Item> beans ;
  //add Item objects to the list
  server.addBeans(beans);

 

solr事务处理

  solr在服务器级别实现事务处理.也就是说每次commit, optimize, or rollback都应用到了所有的请求中.更新solr最恰当的方式就是使用单独的进程来处理,以避免在提交或者是回滚时产生的竞争冲突.同样,最好是使用批处理来索引,因为提交和优化都是高消耗的程序.

读数据--数据库

import java.io.IOException;
import java.net.MalformedURLException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;

import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.common.SolrInputDocument;

public class Test
{
    private static int fetchSize = 1000;
    private static String url = "http://localhost:8983/solr/core1/";
    private static HttpSolrServer solrCore;

    public Test() throws MalformedURLException
    {
         solrCore = new HttpSolrServer(url);
    }

    /**
     * Takes an SQL ResultSet and adds the documents to solr. Does it in batches
     * of fetchSize.
     * 
     * @param rs
     *            A ResultSet from the database.
     * @return The number of documents added to solr.
     * @throws SQLException
     * @throws SolrServerException
     * @throws IOException
     */
    public long addResultSet(ResultSet rs) throws SQLException,
            SolrServerException, IOException
    {
        long count = 0;
        int innerCount = 0;
        Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
        ResultSetMetaData rsm = rs.getMetaData();
        int numColumns = rsm.getColumnCount();
        String[] colNames = new String[numColumns + 1];

        /**
         * JDBC numbers the columns starting at 1, so the normal java convention
         * of starting at zero won't work.
         */
        for (int i = 1; i < (numColumns + 1); i++)
        {
            colNames[i] = rsm.getColumnName(i);
            /**
             * If there are fields that you want to handle manually, check for
             * them here and change that entry in colNames to null. This will
             * cause the loop in the next section to skip that database column.
             */
            // //Example:
            // if (rsm.getColumnName(i) == "db_id")
            // {
            // colNames[i] = null;
            // }
        }

        while (rs.next())
        {
            count++;
            innerCount++;

            SolrInputDocument doc = new SolrInputDocument();

            /**
             * At this point, take care of manual document field assignments for
             * which you previously assigned the colNames entry to null.
             */
            // //Example:
            // doc.addField("solr_db_id", rs.getLong("db_id"));

            for (int j = 1; j < (numColumns + 1); j++)
            {
                if (colNames[j] != null)
                {
                    Object f;
                    switch (rsm.getColumnType(j))
                    {
                        case Types.BIGINT:
                        {
                            f = rs.getLong(j);
                            break;
                        }
                        case Types.INTEGER:
                        {
                            f = rs.getInt(j);
                            break;
                        }
                        case Types.DATE:
                        {
                            f = rs.getDate(j);
                            break;
                        }
                        case Types.FLOAT:
                        {
                            f = rs.getFloat(j);
                            break;
                        }
                        case Types.DOUBLE:
                        {
                            f = rs.getDouble(j);
                            break;
                        }
                        case Types.TIME:
                        {
                            f = rs.getDate(j);
                            break;
                        }
                        case Types.BOOLEAN:
                        {
                            f = rs.getBoolean(j);
                            break;
                        }
                        default:
                        {
                            f = rs.getString(j);
                        }
                    }
                    doc.addField(colNames[j], f);
                }
            }
            docs.add(doc);

            /**
             * When we reach fetchSize, index the documents and reset the inner
             * counter.
             */
            if (innerCount == fetchSize)
            {
                solrCore.add(docs);
                docs.clear();
                innerCount = 0;
            }
        }

        /**
         * If the outer loop ended before the inner loop reset, index the
         * remaining documents.
         */
        if (innerCount != 0)
        {
            solrCore.add(docs);
        }
        return count;
    }
}
View Code

 

 设置RequestWriter

  solrj允许使用XML和二进制两种格式上传内容,默认设置是XML.下面使用的是二进制上传内容,solrj获取结果采用相同的格式.并且这样极大的提高了性能,因为它减少了XML编组开销.

server.setRequestWriter(new BinaryRequestWriter());

 

  注意:确定solrconfig.xml中同样开启"BinaryUpdateRequestHandler"的功能:

<requestHandler name="/update/javabin" class="solr.BinaryUpdateRequestHandler" />

 

 Solr中读取查询内容

  获取一个solrServer实例:

SolrServer server = getSolrServer();

 

   构造SolrQuery:

    SolrQuery query = new SolrQuery();
    query.setQuery( "*:*" );
    query.addSortField( "price", SolrQuery.ORDER.asc );

 

   执行查询:

    QueryResponse rsp = server.query( query );

 

   获取结果:

   SolrDocumentList docs = rsp.getResults();

 

   获取bean对象的结果集,bean类必须采用注解.

List<Item> beans = rsp.getBeans(Item.class);

 

 高级使用方式

  SolrJ提供了一个API来创建查询,而不是手工编码查询.下面是一个分面查询的例子:

  SolrServer server = getSolrServer();
  SolrQuery solrQuery = new  SolrQuery().
                setQuery("ipod").
                setFacet(true).
                setFacetMinCount(1).
                setFacetLimit(8).
                addFacetField("category").
                addFacetField("inStock");
  QueryResponse rsp = server.query(solrQuery);

 

 高亮--Highlighting

   设置高亮参数的方式和其他常用参数的设置方式一样:

    SolrQuery query = new SolrQuery();
    query.setQuery("foo");

    query.setHighlight(true).setHighlightSnippets(1); //set other params as needed
    query.setParam("hl.fl", "content");

    QueryResponse queryResponse = getSolrServer().query(query);

 

  这样取回高亮结果集:

    Iterator<SolrDocument> iter = queryResponse.getResults().iterator();

    while (iter.hasNext()) {
      SolrDocument resultDoc = iter.next();

      String content = (String) resultDoc.getFieldValue("content");
      String id = (String) resultDoc.getFieldValue("id"); //id is the uniqueKey field

      if (queryResponse.getHighlighting().get(id) != null) {
        List<String> highlightSnippets = queryResponse.getHighlighting().get(id).get("content");
      }
    }

 

 

负载均衡查询

  当你有多个Solr的服务器可以处理查询时,虽然你肯定喜欢选择使用的外部负载平衡器,但是solrj提供了一个简单的内置的轮询负载均衡功能LBHttpSolrServer.

使用SolrCloud

  SolrJ包含了SolrCloud的一个敏捷的客户端,也就是ZooKeeper.这就意味着你的java应用程序只需要知道Zookeeper实例,并不知道你的solr实例在什么地方,因为这些可以从ZooKeeper中获取.

  为了和SolrCloud交互,需要使用CloudSolrServer的实例,并且传入你得ZooKeeper主机地址(一个或者多个).除了CloudSolrServer的实例化不一样外,其他的处理方式都是一样的:

  

import org.apache.solr.client.solrj.impl.CloudSolrServer;
import org.apache.solr.common.SolrInputDocument;

CloudSolrServer server = new CloudSolrServer("localhost:9983");
server.setDefaultCollection("collection1");
SolrInputDocument doc = new SolrInputDocument();
doc.addField( "id", "1234");
doc.addField( "name", "A lovely summer holiday");
server.add(doc);
server.commit();

 

 

翻译:http://wiki.apache.org/solr/Solrj,

 

posted @ 2015-02-05 11:50  勿妄  阅读(463)  评论(0编辑  收藏  举报