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
  • 相关阅读:
    用PHP判断oicq是否在线的小程序
    我的PHP树的代码,可以嵌套任意层
    用PEAR来写你的下一个php程序(潘凡Night Sailer)(1)
    php在线文本编辑器
    分析HTML,并将结果存到一个数组中。看看里面的注释吧。:)
    聊天室php
    基于文件、数据库的计数器
    hust The mell hell
    UVA 10003 Cutting Sticks(区间DP)
    zoj 3197 Google Book(最小区间覆盖)
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12286173.html
Copyright © 2011-2022 走看看