zoukankan      html  css  js  c++  java
  • SolrJ 的运用

    SolrJ 是操作 Solr 的 Java 客户端,它提供了增加、修改、删除、查询 Solr 索引的 Java 接口。SolrJ 针对 Solr 提供了 REST 的 Http 接口进行了封装, SolrJ 底层是通过使用 HttpClient 来完成 Solr 的操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <dependencies>
    <dependency>
    <groupId>org.apache.solr</groupId>
    <artifactId>solr-solrj</artifactId>
    <version>5.2.0</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.4</version>
    </dependency>
    <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.17</version>
    </dependency>
    </dependencies>

    SQL 脚本(MySQL)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    CREATE TABLE `product` (
    `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name` varchar(200) NOT NULL COMMENT '商品名称',
    `sort_name` varchar(128) NOT NULL COMMENT '分类名称',
    `sub_sort_name` varchar(128) NOT NULL COMMENT '子分类名称',
    `price` decimal(10,0) NOT NULL COMMENT '价格',
    `sales` int(11) DEFAULT '0' COMMENT '销量',
    `area` varchar(64) NOT NULL COMMENT '地区',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=742 DEFAULT CHARSET=utf8 COMMENT='商品表';

    点此下载数据库脚本(数据从爱淘宝网站中爬取)

    建立数据模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    package org.fanlychie.model;
    import org.apache.solr.client.solrj.beans.Field;
    public class Product {
    /**
    * 主键
    */
    @Field("id")
    private Integer id;
    /**
    * 商品名称
    */
    @Field("name")
    private String name;
    /**
    * 分类名称
    */
    @Field("sortName")
    private String sortName;
    /**
    * 子分类名称
    */
    @Field("subSortName")
    private String subSortName;
    /**
    * 价格
    */
    @Field("price")
    private Double price;
    /**
    * 销量
    */
    @Field("sales")
    private Integer sales;
    /**
    * 地区
    */
    @Field("area")
    private String area;
    public Integer getId() {
    return id;
    }
    public void setId(Integer id) {
    this.id = id;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public String getSortName() {
    return sortName;
    }
    public void setSortName(String sortName) {
    this.sortName = sortName;
    }
    public String getSubSortName() {
    return subSortName;
    }
    public void setSubSortName(String subSortName) {
    this.subSortName = subSortName;
    }
    public Double getPrice() {
    return price;
    }
    public void setPrice(Double price) {
    this.price = price;
    }
    public Integer getSales() {
    return sales;
    }
    public void setSales(Integer sales) {
    this.sales = sales;
    }
    public String getArea() {
    return area;
    }
    public void setArea(String area) {
    this.area = area;
    }
    @Override
    public String toString() {
    return "Product [id=" + id + ", name=" + name + ", sortName="
    + sortName + ", subSortName=" + subSortName + ", price="
    + price + ", sales=" + sales + ", area=" + area + "]";
    }
    }

    @Field("id") 与 schema.xml 中的 <field name="id" /> 节点相呼应

    建立索引文件时,SolrJ 会将 @Field 注解的属性转换成 Solr 文档对象的字段

    在检索的时候,SolrJ 会将 Solr 文档对象的字段转换成 @Field 注解的 Bean 的属性

    schema.xml 配置片段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <schema name="core1" version="1.5">
    [ . . . . . . ]
    <field name="_version_" type="long" indexed="true" stored="true"/>
    <!-- 商品 ID -->
    <field name="id" type="int" indexed="true" stored="true" required="true"/>
    <!-- 商品名称 -->
    <field name="name" type="text_ik" indexed="true" stored="true" required="true"/>
    <!-- 商品一级分类 -->
    <field name="sortName" type="string" indexed="true" stored="true" required="true"/>
    <!-- 商品二级分类 -->
    <field name="subSortName" type="string" indexed="true" stored="true" required="true"/>
    <!-- 商品价格 -->
    <field name="price" type="double" indexed="true" stored="true" required="true"/>
    <!-- 商品销量 -->
    <field name="sales" type="int" indexed="true" stored="true" required="true"/>
    <!-- 发货地 -->
    <field name="area" type="string" indexed="true" stored="true" required="true"/>
    <!-- 检索域 -->
    <field name="text" type="text_ik" indexed="true" stored="false" multiValued="true" required="false"/>
    <!-- 唯一键 -->
    <uniqueKey>id</uniqueKey>
    <!-- 把需要检索的字段, 拷贝到 text 字段中 -->
    <copyField source="name" dest="text"/>
    <!-- 把需要检索的字段, 拷贝到 text 字段中 -->
    <copyField source="sortName" dest="text"/>
    <!-- 把需要检索的字段, 拷贝到 text 字段中 -->
    <copyField source="subSortName" dest="text"/>
    <!-- 采用 IK 中文分词的字段类型 -->
    <fieldType name="text_ik" class="solr.TextField" positionIncrementGap="100">
    <analyzer type="index">
    <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" />
    <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
    <analyzer type="query">
    <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" />
    <filter class="solr.LowerCaseFilterFactory"/>
    </analyzer>
    </fieldType>
    [ . . . . . . ]
    </schema>

    Solr 服务启动报错:

    Caused by: org.apache.solr.common.SolrException: Invalid Number: MA147LL/A

    解决办法:

    将 $SOLR_HOME/core1/conf/elevate.xml(竞价排名)配置文件中的 id 的值改为整型值即可

    使用 JDBC 从数据库获取数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package org.fanlychie.dao;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.util.ArrayList;
    import java.util.List;
    import org.fanlychie.model.Product;
    public class ProductDao {
    public static List<Product> getAll() {
    Connection conn = null;
    try {
    String username = "root";
    String password = "root";
    String url = "jdbc:mysql://localhost:3306/product_repo";
    conn = DriverManager.getConnection(url, username, password);
    PreparedStatement pstmt = conn.prepareStatement("select * from product");
    ResultSet rs = pstmt.executeQuery();
    List<Product> products = new ArrayList<Product>();
    while (rs.next()) {
    Product product = new Product();
    product.setId(rs.getInt("id"));
    product.setSales(rs.getInt("sales"));
    product.setArea(rs.getString("area"));
    product.setName(rs.getString("name"));
    product.setPrice(rs.getDouble("price")); 大专栏  SolrJ 的运用
    product.setSortName(rs.getString("sort_name"));
    product.setSubSortName(rs.getString("sub_sort_name"));
    products.add(product);
    }
    return products;
    } catch (Throwable e) {
    throw new RuntimeException(e);
    } finally {
    if (conn != null) {
    try {
    conn.close();
    } catch (Exception e) {}
    }
    }
    }
    static {
    try {
    Class.forName("com.mysql.jdbc.Driver");
    } catch (ClassNotFoundException e) {
    e.printStackTrace();
    }
    }
    }

    log4j.xml 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration SYSTEM "http://toolkit.alibaba-inc.com/dtd/log4j/log4j.dtd">
    <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%n%p %d{yyyy-MM-dd HH:mm:ss} %c : %L %n%m%n%n" />
    </layout>
    </appender>
    <logger name="org.apache" additivity="true">
    <level value="WARN" />
    </logger>
    <logger name="org.apache.http.impl.conn.DefaultClientConnection" additivity="true">
    <level value="DEBUG" />
    </logger>
    <root>
    <level value="DEBUG" />
    <appender-ref ref="console" />
    </root>
    </log4j:configuration>

    建立索引

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    private static final int RESPONSE_STATUS_OK = 0;
    public static void main(String[] args) throws Throwable {
    // 创建一个 Solr 客户端
    SolrClient solrClient = new HttpSolrClient("http://192.168.1.102:8081/solr/core1");
    // 文档对象绑定器
    DocumentObjectBinder binder = solrClient.getBinder();
    // Solr 输入文档
    List<SolrInputDocument> documents = new ArrayList<SolrInputDocument>();
    // 从数据库中取得需要建立索引的数据
    List<Product> products = ProductDao.getAll();
    for (Product product : products) {
    // 将 Bean 转换成 Solr 文档
    documents.add(binder.toSolrInputDocument(product));
    }
    // 添加文档到客户端
    solrClient.add(documents);
    // 提交事务
    UpdateResponse response = solrClient.commit();
    if (response.getStatus() == RESPONSE_STATUS_OK) {
    System.out.println("创建索引成功!");
    } else {
    System.out.println("创建索引失败!");
    }
    // 关闭
    solrClient.close();
    }

    检索文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public static void main(String[] args) throws Throwable {
    // 创建一个 Solr 客户端
    SolrClient solrClient = new HttpSolrClient("http://192.168.1.102:8081/solr/core1");
    // 创建一个 Solr 查询
    SolrQuery solrQuery = new SolrQuery();
    // 设置查询串
    solrQuery.setQuery("打底加绒上衣男");
    // 执行查询得到查询响应对象
    QueryResponse response = solrClient.query(solrQuery);
    // 从查询响应对象中获取查询结果
    SolrDocumentList documentList = response.getResults();
    // 文档对象绑定器
    DocumentObjectBinder binder = solrClient.getBinder();
    List<Product> products = new ArrayList<Product>();
    for (SolrDocument document : documentList) {
    // 将 Solr 文档对象转换成 Bean 对象
    products.add(binder.getBean(Product.class, document));
    }
    // 关闭客户端
    solrClient.close();
    // 打印消息
    System.out.println(products);
    }

    搜索结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    DEBUG 2015-12-02 21:51:06 org.apache.http.impl.conn.DefaultClientConnection : 268
    Sending request: GET /solr/core1/select?q=%E6%89%93%E5%BA%95%E5%8A%A0%E7%BB%92%E4%B8%8A%E8%A1%A3%E7%94%B7&wt=javabin&version=2 HTTP/1.1
    DEBUG 2015-12-02 21:51:06 org.apache.http.impl.conn.DefaultClientConnection : 253
    Receiving response: HTTP/1.1 200 OK
    DEBUG 2015-12-02 21:51:06 org.apache.http.impl.conn.DefaultClientConnection : 176
    Connection 0.0.0.0:53836<->192.168.1.102:8081 closed
    DEBUG 2015-12-02 21:51:06 org.apache.http.impl.conn.DefaultClientConnection : 176
    Connection 0.0.0.0:53836<->192.168.1.102:8081 closed
    [
    Product [id=371, name=男士长袖t恤男装高领紧身秋衣男青少年加绒加厚打底衫韩版上衣服, sortName=男装, subSortName=T恤, price=30.0, sales=7681, area=浙江 杭州],
    Product [id=310, name=男装长袖T恤冬季加绒加厚保暖衣青少年V领打底上衣潮男冬装加大码, sortName=男装, subSortName=T恤, price=48.0, sales=631, area=广东 深圳]
    ]

    POST 请求

    1
    QueryResponse response = solrClient.query(solrQuery, SolrRequest.METHOD.POST);

    最小匹配

    1
    solrQuery.setQuery("打底加绒上衣男");

    执行查询请求,服务器端记录的日志信息

    1
    [core1] webapp=/solr path=/select params={q=打底加绒上衣男&wt=javabin&version=2} hits=2 status=0 QTime=1

    hits = 2,即该请求匹配到 2 个文档。

    1
    2
    3
    solrQuery.setQuery("打底加绒上衣男");
    solrQuery.setParam("mm", "2");

    mm(minimal should match)最小应该匹配多少个短语(查询串分词后的短语)。

    再次执行查询请求,服务器端记录的日志信息

    1
    [core1] webapp=/solr path=/select params={mm=2&q=打底加绒上衣男&wt=javabin&version=2} hits=120 status=0 QTime=4

    hits = 120,即该请求匹配到 120 个文档。

    查询参数

    1
    solrQuery.setQuery("sortName:男装 AND area:广东 广州");

    查询分类是男装,发货地是广东广州的商品(广东广州有空格,需要转义)

    1
    [core1] webapp=/solr path=/select params={q=sortName:男装+AND+area:广东+广州&wt=javabin&version=2} hits=19 status=0 QTime=3

    结果排序

    1
    2
    3
    4
    5
    solrQuery.setQuery("羽绒服女");
    solrQuery.addSort("price", SolrQuery.ORDER.asc);
    solrQuery.addSort("sales", SolrQuery.ORDER.desc);

    先按价格升序排序,价格相同按销量降序排序。注意不能用 setSort,如

    1
    2
    3
    4
    5
    solrQuery.setQuery("羽绒服女");
    solrQuery.setSort("price", SolrQuery.ORDER.asc);
    solrQuery.setSort("sales", SolrQuery.ORDER.desc);

    该方式只会按销量降序排序,价格的排序被覆盖掉不起作用。

    facet 查询

    Facet 是 solr 的高级搜索功能之一,在检索文档的同时,能够按照 Facet 的域(字段)进行分组统计。Facet 的字段必须被索引,一般来说该字段无需分词,无需存储。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public static void main(String[] args) throws Throwable {
    // 创建一个 Solr 客户端
    SolrClient solrClient = new HttpSolrClient("http://192.168.1.102:8081/solr/core1");
    // 创建一个 Solr 查询
    SolrQuery solrQuery = new SolrQuery();
    solrQuery.setRows(Integer.MAX_VALUE);
    // 设置查询串
    solrQuery.setQuery("女装");
    // facet 查询
    solrQuery.setFacet(true);
    // 每个分组中的数据至少有一个值才返回
    solrQuery.setFacetMinCount(1);
    // 不统计 NULL 的值
    solrQuery.setFacetMissing(false);
    // 排序
    solrQuery.setFacetSort(FacetParams.FACET_SORT_COUNT);
    // facet 结果的返回行数
    solrQuery.setFacetLimit(200);
    // 分组统计的域
    solrQuery.addFacetField("sortName", "subSortName");
    // 执行查询得到查询响应对象
    QueryResponse response = solrClient.query(solrQuery, SolrRequest.METHOD.POST);
    List<FacetField> facetFieldList = response.getFacetFields();
    for (FacetField facetField : facetFieldList) {
    System.out.println(facetField.getName());
    System.out.println("---------------------------------------------------");
    List<Count> counts = facetField.getValues();
    for (Count count : counts) {
    System.out.println(count.getName() + " : " + count.getCount());
    }
    System.out.println();
    }
    solrClient.close();
    }

    输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    sortName
    ---------------------------------------------------
    女装 : 348
    男装 : 1
    subSortName
    ---------------------------------------------------
    羽绒服 : 76
    T恤 : 75
    毛呢外套 : 75
    连衣裙 : 75
    鞋子 : 48
  • 相关阅读:
    trackr: An AngularJS app with a Java 8 backend – Part III
    trackr: An AngularJS app with a Java 8 backend – Part II
    21. Wireless tools (无线工具 5个)
    20. Web proxies (网页代理 4个)
    19. Rootkit detectors (隐形工具包检测器 5个)
    18. Fuzzers (模糊测试器 4个)
    16. Antimalware (反病毒 3个)
    17. Debuggers (调试器 5个)
    15. Password auditing (密码审核 12个)
    14. Encryption tools (加密工具 8个)
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12286173.html
Copyright © 2011-2022 走看看