Solr

Solr概述


Solr是一个独立的企业级搜索应用服务器,它对外提供类似于的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的文件,生成索引;也可以通过Http Get操作提出查找请求,并得到指定格式的返回结果。


Solr安装部署


下载Solr


官网:https://lucene.apache.org/solr/

说明:Solr与Lucene是同步更新的,课程中使用4.10.3版本

solr官网下载地址:http://archive.apache.org/dist/lucene/solr/

解压Solr

目录结构说明:

bin:solr运行脚本

contrib:solr的一些扩展jar包,用于增强solr功能

dist:build过程中生成的war和jar文件,以及一些依赖文件

docs:solr的API文档

example:solr工程的例子目录

example/solr:标准的SolrHome,包含一个默认的SolrCore

example/webapps:包含了一个solr.war,该war可作为solr的运行示例工程,我们部署的solr服务就是用的它

licenes:solr相关的许可信息

安装环境


Solr本身集成了Jetty服务器,可以直接启动运行。 Tomcat、jetty、Jboss、underTow

打开cmd命令行窗口,进入solr解压目录中的example目录。

进行cmd执行:java -jar start.jar

启动成功,访问:http://localhost:8983/solr

虽然solr内置了Jetty服务器,但是在企业中一般使用Tomcat部署

需要环境:

Solr:4.10.3

JDK环境:1.8(solr4.10 要求jdk1.7以上版本)

服务器:Tomcat 8

配置SolrHome


SolrHome目录是solr服务运行的主目录。一个SolrHome目录包含有多个SolrCore。SolrCore目录中包含了运行solr实例的配置文件和数据文件(日志和索引文件)。每一个SolrCore提供独立的索引和搜索服务。

拷贝【资料\solr-4.10.3\example\solr】目录到【D:\solr】,重命名solr为solrhome

SolrHome目录:

SolrCore目录:

说明:

conf:SolrCore运行配置信息

data:SolrCore存放日志和索引文件的目录

core.properties:SolrCore的信息,比如:名称

配置SolrCore

配置SolrCore目录下的conf/solrconfig.xml文件:

说明:solrconfig.xml文件是配置SolrCore实例的相关信息。默认情况下可以不做修改。在企业项目中需要修改三个常用的标签:lib标签、datadir标签、requestHandler标签。

lib标签

在solrconfig.xml中可以加载一些扩展的jar,如果需要使用。(一般不需要使用)

solrconfig.xml文件75-85行,修改前:

datadir标签

配置SolrCore的data目录,data目录用来存放SolrCore的索引文件和tlog日志文件。solr.data.dir表示${SolrCore}目录,等价于${solr.install.dir}

说明:一般不需要修改。

requestHandler标签

requestHandler请求处理器,定义了索引和搜索的访问方式。

通过/select搜索索引,完成检索操作。

通过/update维护索引,可以完成索引的添加、修改、删除操作。

设置搜索参数完成搜索,搜索参数也可以设置一些默认值,如下:

<requestHandler name="/select" class="solr.SearchHandler">

    <!-- default values for query parameters can be specified, these

        will be overridden by parameters in the request

      -->

    <lst name="defaults">

        <str name="echoParams">explicit</str>

        <int name="rows">10</int> <!-- 每页大小 -->

        <str name="df">text</str> <!-- 默认域 -->

    </lst>

</requestHandler>

Solr部署


准备Tomcat

修改Tomcat访问端口

部署solr.war

第1步

拷贝solr-4.10.3\example\webapps目录中的solr.war到tomcat的webapps目录中

复制到:

第2步

解压solr.war包:

第3步

【删除solr.war包】

说明:删除为了防止tomcat启动的时候,再进行解压,覆盖已经解压配置好的solr。

第4步

【加入solr服务扩展jar包】

solr-4.10.3\example\lib\ext目录下的所有jar包copy到部署到Tomcat中的solr的/WEB-INF/lib

把solr解压包下solr-4.10.3\example\lib\ext目录下的所有jar包拷贝到Tomcat部署的solr的WEB-INF/lib文件夹

复制到:

第5步

【准备log4j.properties日志文件】

把solr解压包下solr-4.10.3\example\resources\log4j.properties文件,复制到Tomcat的webapps\solr\WEB-INF\classes目录下(如果没有classes目录,创建一个):

第6步

【配置web.xml】

修改web.xml,配置SolrHome

打开env-entry注释,修改env-entry-value为solrHome目录。

第7步

【启动tomcat服务】

运行tomcat安装bin目录中的startup.bat文件:

访问:http://localhost:8983/solr

Solr管理界面操作


Solr管理管理界面介绍


SolrCore介绍

选择一个SolrCore进行详细操作:

Analysis

通过此界面可以测试索引分析器和搜索分析器的执行情况

Dataimport

数据导入,从关系数据库将数据导入到Solr索引库中。默认没有配置,需要手工配置。

Documents

通过/update表示更新索引,solr默认根据id(唯一约束)域来更新Document的内容,如果根据id值搜索不到id域则会执行添加操作,如果找到则更新。

删除文档:

<delete>

<id>1</id>

</delete>

<commit/>

Solr使用


schema.xml


schema.xml文件在SolrCore/conf目录。主要用于配置域的类型(FieldType)和配置域(Field)。在solr中域要先配置再使用。

Field

作用:配置域。

<field name="id" type="string" indexed="true" stored="true"

required="true" multiValued="false"/>

name:域的名称

type:域的类型

indexed:是否索引

stored:是否存储

required:是否必须(如果为true添加索引的时候,文档对象中必须要包含该域)

mulitValued:是否多值(用于复制域)


DynamicField

作用:配置动态域

<dynamicField name="*_ik" type="text_ik" indexed="true" stored="true"/>

name:动态域的名称


UniqueKey

作用:指定唯一约束域,相当于关系数据库中的主键

<uniqueKey>id</uniqueKey>

说明:在solr中,唯一约束域是必须的。

CopyField

作用:配置复制域

<copyField source="name" dest="keywords"/>

<copyField source="title" dest="keywords"/>

source:源域的名称

dest:目标域的名称

说明:在solr中,允许将多个域的值复制给一个域,这样的目的是方便执行搜索。solr在创建索引的时候,会把源域的内容复制给目标域。(目标域的定义必须要包含一个属性:multiValued="true"

FieldType

作用:配置域的类型

<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">

    <analyzer type="index">

        <tokenizer class="solr.StandardTokenizerFactory"/>

        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />

        <!-- in this example, we will only use synonyms at query time

        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>

        -->

        <filter class="solr.LowerCaseFilterFactory"/>

    </analyzer>

    <analyzer type="query">

        <tokenizer class="solr.StandardTokenizerFactory"/>

        <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />

        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>

        <filter class="solr.LowerCaseFilterFactory"/>

    </analyzer>

</fieldType>

name:域的类型名称

class:域的类型,solr中的类型

analyzer:指定分词器,index是索引流程;query是检索流程

tokenizer:具体使用的分词器

filter:具体使用的过滤器

配置IK分词器


说明:配置ik中文分词器。

拷贝IK分词器jar包

拷贝Maven仓库中的jar包:

拷贝到solr/WEB-INF/lib目录:

拷贝IK分词器配置文件

拷贝IK解压目录中的配置文件

拷贝到solr/WEB-INF/classes目录:

配置schema.xml

通过FieldType标签,配置使用IK分词器的域类型

<!-- 配置使用IK分词器的域类型 -->

<fieldType name="aaa" class="solr.TextField" >

<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>

</fieldType>

通过Field标签,配置使用IK分词器的域

<!-- 配置使用IK分词器的域 -->

<field name="content_ik" type="aaa" indexed="false"

stored="true" multiValued="false"/>

重启tomcat

5.3配置业务域


说明:使用solr实现商品数据的搜索,需要把保存在关系数据库中的商品数据,导入solr索引库。

5.3.1准备商品数据

导入goods.sql

商品编号(商品编号是主键,作为唯一约束域uniqueKey):

<field name="id" type="string" indexed="true" stored="true"

required="true"/>

商品名称:

<field name="name" type="text_ik" indexed="true" stored="true"/>

商品标题:

<field name="title" type="text_ik" indexed="true" stored="true"/>

商品价格:

<field name="price" type="double" indexed="true" stored="true"/>

商品图片:

<field name="pic" type="string" indexed="false" stored="true"/>

定义商品复制域(在实际项目中复制域不需要存储,这里存储是为了给大家看到数据效果):

<field name="keywords" type="text_ik" indexed="true" stored="true" multiValued="true" />

把商品名称和商品标题添加到复制域

<copyField source="name" dest="keywords"/>

<copyField source="title" dest="keywords"/>


5.3.3配置schema.xml

<?xml version="1.0" encoding="UTF-8" ?>

<schema name="example" version="1.5">

    <!-- 版本号(必须) -->

<field name="_version_" type="long" indexed="true" stored="true"/>

<!-- 普通域 -->

<field name="id" type="string" indexed="true" stored="true" required="true"/>

<field name="name" type="text_ik" indexed="true" stored="true"/>

<field name="title" type="text_ik" indexed="true" stored="true"/>

<field name="pic" type="string" indexed="false" stored="true"/>

<field name="price" type="double" indexed="true" stored="true"/>


<field name="keywords" type="text_ik" indexed="true" stored="true"

      multiValued="true"/>

<!-- 复制域 -->

<copyField source="name" dest="keywords"/>

<copyField source="title" dest="keywords"/>

<!-- 唯一约束域 -->

<uniqueKey>id</uniqueKey>


<!-- 配置动态域 -->

<dynamicField name="*_ik"  type="text_ik" indexed="true"  stored="true"/>


<!-- 配置域类型 -->

    <fieldType name="string" class="solr.StrField"/>

    <fieldType name="boolean" class="solr.BoolField"/>

    <fieldType name="int" class="solr.TrieIntField"/>

    <fieldType name="float" class="solr.TrieFloatField"/>

    <fieldType name="long" class="solr.TrieLongField"/>

    <fieldType name="double" class="solr.TrieDoubleField"/>

    <fieldType name="date" class="solr.TrieDateField"/>


<!-- 配置使用IK分词器的域类型 -->

<fieldType name="text_ik" class="solr.TextField" >

<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>

</fieldType>

</schema>


5.3.4配置solrconfig.xml 默认域

更改默认域名称,改成我们的域名称:

5.4 配置数据导入


5.4.1拷贝数据导入jar包

拷贝solr解压目录dist目录中的jar包:

拷贝到solr/solrHome/lib(lib手动创建):

5.4.2拷贝数据库驱动jar包

拷贝到solr/solrhome/lib:

5.4.3配置solrconfig.xml

第1步

配置lib标签,加载jar包

<lib dir="${solr.install.dir:../}/lib" regex=".*\.jar" />

第2步

通过requestHandler标签,配置数据导入的请求处理器

<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">

    <lst name="defaults">

        <str name="config">import-config.xml</str>

    </lst>

</requestHandler>

第3步

创建import-config.xml

<?xmlversion="1.0" encoding="UTF-8"?>

<dataConfig>

<!-- 配置连接数据库信息 -->

<dataSourcetype="JdbcDataSource"

driver="com.mysql.jdbc.Driver"

url="jdbc:mysql://127.0.0.1:3306/lucene_db"

user="root"

password="admin"/>

<document>

<!-- 配置sql语句中的字段,与solr索引库的域的对应关系 -->

<entityname="goods"

query="SELECT id,name,title,price,pic FROM goods">

<fieldcolumn="id"name="id"/>

<fieldcolumn="name"name="name"/>

<fieldcolumn="title"name="title"/>

<fieldcolumn="price"name="price"/>

<fieldcolumn="pic"name="pic"/>


<!-- 测试动态域 -->

<fieldcolumn="pic"name="pic_ik"/>

<fieldcolumn="name"name="name_ik"/>

<fieldcolumn="title"name="title_ik"/>

</entity>

</document>

</dataConfig>

5.4.4重启tomcat,执行导入

查询数据

Solr管理界面搜索


q: 指定查询表达式

`*:*`:表示搜索全部

name:商务,表示搜索name域中包含有“商务”

fq:指定搜索的过滤条件

price:[2000 TO 8000],表示搜索2000 至8000之间的商品,包含2000 ,包含8000

price:{2000 TO 8000},表示搜索2000 至8000之间的商品,不包含2000 ,不包含8000

sort:指定搜索结果排序

price asc,表示按照商品价格升序排序

price desc,表示按照商品价格降序排序

start,rows:指定分页

start:分页开始记录数

rows:每页显示记录数

fl:指定搜索结果显示的域列表

pid,name,price,显示商品id、商品名称、商品价格

df:指定默认搜索域

注意事项:默认搜索域只能指定一个。

wt:指定搜索结果的响应格式

常用格式:json/xml/csv。

hl:指定高亮显示

hl.fl:指定高亮显示的域名称

hl.simple.pre:指定高亮显示的html标签的开始部分

hl.simple.post:指定高亮显示的html标签的结束部分

注意:高亮结果集的数据结构为:`Map<String,Map<String,List<String>>`


Solrj


Solrj是什么


SolrJ是一个使Java应用程序可以轻松与Solr对话的API。SolrJ隐藏了许多连接到Solr的细节,并允许您的应用程序通过简单的高级方法与Solr进行交互。

SolrJ的中心是org.apache.solr.client.solrj包,它只包含五个主要的类。首先创建一个SolrClient代表你想要使用的Solr实例。然后发送SolrRequests或SolrQuerys找回SolrResponses。

需求


使用solrj访问solr服务,完成索引的增删改查操作。

6.3 需求实现

6.3.1 创建Maven项目

配置依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dfbz</groupId>

    <artifactId>Solr</artifactId>

    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <!--solr核心包-->

        <dependency>

            <groupId>org.apache.solr</groupId>

            <artifactId>solr-core</artifactId>

            <version>4.10.3</version>

        </dependency>

        <!--solr依赖日志包-->

        <dependency>

            <groupId>commons-logging</groupId>

            <artifactId>commons-logging</artifactId>

            <version>1.1.3</version>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

            <version>4.12</version>

        </dependency>

    </dependencies>

</project>

代码实现

添加(更新)索引

说明:solr是根据pid域执行索引的更新。先根据pid域执行搜索,搜索到执行更新;搜索不到执行添加。

\bullet  创建HttpSolrServer对象,连接solr服务

\bullet  创建域相关的实体对象(Product)

\bullet  使用HttpSolrServer对象,执行添加(更新)

\bullet  提交事务

第一步:创建域相关的实体类

package com.dfbz.entity;

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

public class Goods {

    @Field("id")

    private String id;

    @Field

    private String name;

    @Field

    private String title;

    @Field

    private Double price;

    @Field

    private String pic;

}

package com.dfbz.solr;

import com.dfbz.entity.Goods;

import org.apache.solr.client.solrj.SolrQuery;

import org.apache.solr.client.solrj.SolrServerException;

import org.apache.solr.client.solrj.impl.HttpSolrServer;

import org.apache.solr.client.solrj.response.QueryResponse;

import org.junit.Test;

import java.io.IOException;

import java.util.List;

import java.util.Map;

public class Demo1 {

    private HttpSolrServer solrServer = new HttpSolrServer("http://localhost:8983/solr/collection1");

    @Test

    public void saveOrUpdate() throws IOException, SolrServerException {

        Goods goods = new Goods();

        goods.setId("888");

        goods.setName("东标方准牌手机");

        goods.setTitle("东标方准牌手机,好吃不上火!");

        goods.setPrice(9999.99);

        //如果没有则添加,如果有则修改

        solrServer.addBean(goods);

        //提交事务

        solrServer.commit();

    }

}

注意:在solr中,添加和修改是同一个方法,如果id存在则修改,如果id不存在则添加


删除索引

根据id删除:

\bullet  创建HttpSolrServer对象,连接solr服务

\bullet  使用HttpSolrServer对象执行删除

\bullet  提交事务

/** 根据id删除索引 */

@Test

public void deleteById() throws Exception{

    solrServer.deleteById("888");

    // 提交事务

    solrServer.commit();

}

根据条件删除

\bullet  创建HttpSolrServer对象,连接solr服务

\bullet  使用HttpSolrServer对象,执行删除

\bullet  提交事务

//根据条件删除

@Test

public void deleteByQuery() throws IOException, SolrServerException {

    solrServer.deleteByQuery("title:手机");

    solrServer.commit();

}

查询索引

\bullet  创建HttpSolrServer对象,连接solr服务

\bullet  创建查询对象(SolrQuery),封装查询条件

\bullet  使用HttpSolrServer对象执行搜索,返回QueryResposne对象

\bullet  通过QueryResponse对象中获取查询的结果

\bullet  处理结果

//条件查询

@Test

public void query() throws IOException, SolrServerException {

    SolrQuery solrQuery = new SolrQuery("title:商务");

    QueryResponse response = solrServer.query(solrQuery);

    System.out.println("数量: " + response.getResults().getNumFound());

    List<Goods> goodsList = response.getBeans(Goods.class);

    for (Goods goods : goodsList) {

        System.out.println(goods);

        System.out.println("-------------------");

    }

}


查询步骤:

\bullet  创建HttpSolrServer对象,连接solr服务

\bullet  创建查询对象(SolrQuery),封装查询条件

\bullet  使用HttpSolrServer对象执行搜索,返回QueryResponse

\bullet  通过QueryResponse获取搜索结果

\bullet  通过QueryResponse获取高亮内容

\bullet  处理搜索结果及高亮内容

//复杂条件查询

    @Test

    public void query2() throws IOException, SolrServerException {

        SolrQuery solrQuery = new SolrQuery("title:手机");

        //添加过滤条件

        solrQuery.addFilterQuery("price:[1000 TO 9000]");

        //排序

        solrQuery.addSort("price", SolrQuery.ORDER.desc);

        //分页

        solrQuery.setStart(0);

        solrQuery.setRows(10);

        //设置查询域

        solrQuery.setFields("id,price,name,title");

        //开启高亮

        solrQuery.setHighlight(true);

        //高亮域

        solrQuery.addHighlightField("title");

        solrQuery.setHighlightSimplePre("<font color='red'>");

        solrQuery.setHighlightSimplePost("</font>");

        QueryResponse response = solrServer.query(solrQuery);

        System.out.println("查询到的条数: " + response.getResults().getNumFound());

        //获取查询数据

        List<Goods> goodsList = response.getBeans(Goods.class);

        //获取高亮数据

        Map<String, Map<String, List<String>>> high = response.getHighlighting();

        for (Goods goods : goodsList) {

            //根据id查询到对应高亮数据(map);查询title的值

            System.out.println("id: " + goods.getId() + " title: " + high.get(goods.getId()).get("title"));

            ;

        }

    }

set复杂查询

//set查询

@Test

public void query3() throws IOException, SolrServerException {

    SolrQuery solrQuery = new SolrQuery();

    //设置全局查询

    solrQuery.set("q","小米");


    //设置FilterQuery

    solrQuery.set("fq", "price:[0 TO 8000]");

    //设置起始页数

    solrQuery.set("start", 0);

    //设置页大小

    solrQuery.set("rows", 5);

    //设置排序规则

    solrQuery.set("sort", "price desc");

    //设置FieldList

    solrQuery.set("fl", "id,name,price");


    //设置默认域

  solrQuery.set("df", "keywords");

    //开启高亮

    solrQuery.setHighlight(true);

    //设置高亮域

    solrQuery.addHighlightField("title");

    //设置高亮域

    solrQuery.addHighlightField("name");


    //高亮前缀

    solrQuery.setHighlightSimplePre("<font color='red'>");


    //高亮后缀

    solrQuery.setHighlightSimplePost("</font>");

    //执行查询

    QueryResponse response = solrServer.query(solrQuery);

    System.out.println("记录数: " + response.getResults().getNumFound());

    List<Goods> goodsList = response.getBeans(Goods.class);

    System.out.println("查询数据: ");

    for (Goods goods : goodsList) {

        System.out.println(goods);

    }

    System.out.println("----------------------");

    System.out.println("高亮数据: ");

    Map<String, Map<String, List<String>>> high = response.getHighlighting();

    for (Goods goods : goodsList) {

        String name = high.get(goods.getId()).get("title").get(0);

        String title = high.get(goods.getId()).get("name").get(0);

        System.out.println("name: " + name + ";title: " + title);

    }

}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,277评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,777评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,946评论 0 245
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,271评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,636评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,767评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,989评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,733评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,457评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,674评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,155评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,518评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,160评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,114评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,898评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,822评论 2 280
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,705评论 2 273