solr 全文检索引擎总结 (2)

引言

续上一个博文,原生的实在是太麻烦了,这次我们用spring集成的工具类。这里我使用了SpringBoot。

正经一点的搜索

分词器

我们这样搜索类似于写SQL语句,真正的在搜索栏中查询难道要用"*"或者是指定那个字段嘛?当然不是,为了更符合我们的习惯,我们要配置IK中文分词器和相关的域

IK下载地址:https://github.com/EugenePig/ik-analyzer-solr5

编译就不再赘述了,可以使用Maven打包进行生成jar文件,然后放到Solr的server/solr-webapp/webapp/WEB-INF/lib目录中。

接下来要把IK能力声明到我们的核心中,把下面的代码放到server/solr/mycore/conf/managed-schema中

<fieldType name="text_ik" class="solr.TextField">
    <analyzer type="index" useSmart="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
    <analyzer type="query" useSmart="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>

然后重启我们的Solr

Solr stop -all

然后我们在自己定义的核心中选择Analysis,我们随便写一句话,让IK分词器为我们分词。

ik.png

配置域

域大致分为三种:普通域、复制域、静态域

普通域:普通域就是我们在数据库中存放的字段,也就是能够显示在前端页面上的,但是不一定是作为查询依据的

复制域:作为搜索检索的字段

动态域:如果某个字段是不确定的参数,需要用动态域进行声明,好比我们生产很多产品,但是电视有尺寸,手机有网络制式,电视里的规格对于手机并不适用吧,所以像这样的规格就要用到动态域。

实战

因为之前我们没有配置域,所以是默认生成的,我们需要自己手动创建,所以我们要删除原来的内容。在web中的Documents输入下面的代码,并且选择类型为XML

<delete><query>*:*</query></delete><commit/>

设置域

因为我这里的实例时乌云镜像的数据库,所以我只对于相关的进行配置,大家做个参考就好。

找到自己定义的核心下面的managed-schema

路径\server\solr\mycore\conf\managed-schema

这是标准域,也就是我需要反馈到前端页面上的数据字段。注意一下数据类型嗷

<field name="wybug_title" type="string" indexed="true" stored="true" multiValued="false" docValues="false" />
<field name="wybug_author" type="text_ik" indexed="true" stored="true" multiValued="false" docValues="false" />
<field name="wybug_date" type="pdate" indexed="true" stored="true" multiValued="false" docValues="false" />
<field name="wybug_type" type="string" indexed="false" stored="true" multiValued="false" docValues="false" />
<field name="wybug_level" type="string" indexed="true" stored="true" multiValued="false" docValues="false" />
<field name="wybug_detail" type="text_ik" indexed="true" stored="true" multiValued="false" docValues="false" />

这是复制域,就是我们提供搜索关键字的字段。

<field name="keywords" type="text_general" indexed="true" stored="false" multiValued="true" />
<copyField source="wybug_level" dest="keywords" />
<copyField source="wybug_title" dest="keywords" />
<copyField source="wybug_detail" dest="keywords" />

在这个Demo中没有用到动态域

数据初始化

设置好后重启solr,并且执行原来的从数据库导入solr的代码// 由于时间关系,对于标准域中的字段,我没有完全添加,注意一下嗷。

String solrUrl = "http://localhost:8983/solr/mycore";
HttpSolrClient solrClient = new HttpSolrClient.Builder(solrUrl)
    .withConnectionTimeout(10000)  // 连接超时时间
    .withSocketTimeout(60000) // 通讯超时时间
    .build(); // 构造
SolrInputDocument solrInputFields = new SolrInputDocument();
DruidDataSource source = new DruidDataSource();
source.setDriverClassName("com.mysql.cj.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/wooyun?serverTimezone=UTC");
source.setUsername("study");
source.setPassword("123456");
DruidPooledConnection conn = source.getConnection();
Statement statement = conn.createStatement();
String sql = "SELECT * FROM bugs";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
    solrInputFields = new SolrInputDocument();
    solrInputFields.addField("id", resultSet.getInt("id"));
    solrInputFields.addField("wybug_title", resultSet.getString("wybug_title"));
    solrInputFields.addField("wybug_date", resultSet.getString("wybug_date"));
    solrInputFields.addField("wybug_level", resultSet.getString("wybug_level"));
    solrInputFields.addField("wybug_detail", resultSet.getString("wybug_detail"));
    solrClient.add(solrInputFields);
}
solrClient.commit();

创建工程

现在我以springBoot作为实例进行演示,创建一个SpringBoot工程,进行如下配置。

导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.alc</groupId>
    <artifactId>springboot-solr</artifactId>
    <version>1.0</version>
    <name>springboot-solr</name>
    <description>Demo4SpringBoot2Solr</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-solr</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置application.yml

spring:
  data:
    solr:
      host: http://localhost:8983/solr/mycore
server:
  port: 8088

配置启动器(SpringbootApplication)

package com.alc.springbootsolr;

import org.apache.solr.client.solrj.SolrClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.repository.config.EnableSolrRepositories;

@SpringBootApplication
@EnableSolrRepositories
public class SpringbootSolrApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootSolrApplication.class, args);
    }
//    这个不写也会进行自动注入的,只是IDE会报错,加上这个就会扫描到Bean,加上也不算错,不加也没问题。
//    @Autowired
//    private SolrClient solrClient;
//
//    @Bean
//    public SolrTemplate getSolrTemplate() {
//        return new SolrTemplate(solrClient);
//    }
}

定义实体

package com.alc.springbootsolr.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.annotation.Id;
import org.springframework.data.solr.core.mapping.SolrDocument;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@SolrDocument(solrCoreName = "mycore")
public class Bugs implements Serializable {
    @Id
    @Field
    private Integer id;
    private String wybug_id;
    @Field
    private String wybug_title;
    private String wybug_corp;
    private String wybug_author;
    @Field
    private String wybug_date;
    private String wybug_open_date;
    private String wybug_type;
    private String wybug_level;
    private String wybug_rank_0;
    private String wybug_status;
    private String wybug_from;
    private String wybug_tags;
    @Field
    private String wybug_detail;
    private String wybug_reply;
    private String replys;
    private String wybug_level_fromcorp;
    private String wybug_rank_fromcorp;
    @Field
    private Integer Ranks;

}

定义solr操作的工具类

package com.alc.springbootsolr.search;

import com.alc.springbootsolr.entity.Bugs;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.SolrParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.Query;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.data.solr.core.query.result.ScoredPage;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Component
public class SolrUtil {
    @Autowired
    private SolrTemplate solrTemplate; // solr的工具类,由Spring.data.solr提供

    public List<Bugs> search(String keywords, Long page) {
        System.out.println(keywords + "<===>" + page); // 显示一下我们的关键字和页码
        Query query = new SimpleQuery(); 
        Criteria filter; // 定义我们的过滤器
        if (keywords.matches("[0-9]+")) {
            filter = new Criteria("id").is(keywords); // 如果是数字的话,就判断id
        } else { // 否则判断标题和内容
            filter = new Criteria("wybug_title").contains(keywords);
            filter.or("wybug_detail").contains(keywords); // 或包含的关系,相当于满足一个就够了,不需要解释叭。
        }
        query.setOffset(page);// 设置页码
        query = query.addCriteria(filter);  // 把我们的条件加进去

        ScoredPage<Bugs> bugs = solrTemplate.queryForPage("", query, Bugs.class);
        List<Bugs> content = bugs.getContent();
        return content;
    }

    public boolean deleteAll() {
        Query query = new SimpleQuery("*:*");
        solrTemplate.delete("", query);
        solrTemplate.commit("");
        return true;
    }
}

定义service层

package com.alc.springbootsolr.service;

import com.alc.springbootsolr.entity.Bugs;
import com.alc.springbootsolr.search.SolrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SearchService {
    @Autowired
    private SolrUtil solrUtil;

    public List<Bugs> search(String keywords,Long page) {
        return solrUtil.search(keywords,page);
    }

    public boolean deleteAll() {
        return solrUtil.deleteAll();
    }
}

定义controller层

package com.alc.springbootsolr.controller;

import com.alc.springbootsolr.entity.Bugs;
import com.alc.springbootsolr.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class SearchController {
    @Autowired
    private SearchService searchService;

    @GetMapping("/search")
    public List<Bugs> search(String keywords,Long page) {
        return searchService.search(keywords,page);
    }
    @GetMapping("/deleteAll")
    public boolean deleteAll() {
        return searchService.deleteAll();
    }
}

测试

访问我们的接口:

http://localhost:8088/search?keywords=sina&page=1

http://localhost:8088/search?keywords=sina&page=2

http://localhost:8088/search?keywords=sina&page=3

http://localhost:8088/search?keywords=sina&page=4

http://localhost:8088/deleteAll

http://localhost:8088/search?keywords=sina&page=1

http://localhost:8088/search?keywords=sina

我们发现在删除之前是可以访问到数据的,删除之后就无法访问得到数据了。

search.png
delete.png
research.png

解释

Q:为什么在查询、删除和提交的时候总有一个参数("")呢?

A:在我用的老版本确实不存在第一个参数,但是根据源码分析,String collection,里面是集合的名称,所以能够联想到是我们定义的core,因为我们在yml中配置了core了,所以就不需要进行写入任何内容了,如果说host是这样配置的 http://localhost:8983/solr/ ,那么就需要配置collection了,切记,不要重复配置,因为这样肯定会报错的,可以尝试一下。

当配置出现问题的时候,或者是域配置错了,就会出现这样的错误

ERROR 19596 --- [nio-8088-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Error from server at http://localhost:8983/solr/mycore: Expected mime type application/octet-stream but got text/html. <html>
****这里是HTML代码****

Q:@Field是干嘛的?

A:我们在写入对象的时候,定义实体bean和索引的关系,但是我的这个实例中,没有封装查询,所以在这个样例中是没有效果的,但是建议添加上。

评论 0

还没有评论,来说点什么吧。

提交后需要审核才会公开。