Xunsearch PHP-SDK 权威指南

  • 新手上路

    • 概述
    • 关于 Xunsearch PHP-SDK
    • 安装
    • 体验 demo 项目
    • 开发规范
    • 开发流程

  • 了解基础对象

    • 基础对象概述
    • XS 项目
    • XSException 异常
    • XSDocument 文档
    • XSIndex 索引管理
    • XSSearch 搜索
    • XSTokenizer 分词接口

  • 编写项目配置文件

    • 项目配置详解
    • 自定义分词器
    • 编写第一个配置文件

  • 管理索引

    • 索引概述
    • 添加文档
    • 更新、修改文档
    • 删除文档
    • 清空索引
    • 平滑重建索引
    • 使用索引缓冲区

  • 使用搜索

    • 搜索概述
    • 构建搜索语句
    • 获取搜索匹配结果
    • 获取搜索匹配数量
    • 获取热门搜索词
    • 获取相关搜索词
    • 搜索建议和纠错
    • 按字段值分面搜索

  • 使用辅助工具

    • RequiredCheck 运行检测
    • Indexer 索引管理器
    • Quest 搜索测试工具
    • SearchSkel 生成搜索代码
    • IniWizzard 配置文件向导
Copyright © 2008-2011 by 杭州云圣网络科技有限公司
All Rights Reserved.


Xunsearch PHP-SDK 权威指南

本教程旨在全面介绍 Xunsearch PHP-SDK 的用法，是学习掌握 xunsearch 搜索开发的 基础资料。具体的实例技巧，详见我们的[技巧实践分享]。

本教程在 xunsearch 文档授权条款 下发布。

Copyright (C) 2011, 著作权由杭州云圣网络科技有限公司所有。 著作权人保留一切相关权利。

$Id$
关于 Xunsearch PHP-SDK »


关于 Xunsearch PHP-SDK

Xunsearch （中文名：迅搜）是一套免费开源的专业中文全文检索解决方案，简单易用而且 功能强大、性能卓越能轻松处理海量数据的全文检索。它包含后端索引、搜索服务程序和前端 脚本语言编写的开发工具包(称之为 SDK) 。

Xunsearch 底层采用 C/C++ 编写，索引设计基于著名而悠久的 Xapian，分词采用 自主研发同样也是开源的 SCWS分词，两者完美结合，理论上单个搜索库支持 40 亿条 记录。可编译运行于 Linux/FreeBSD 等各种 UNIX 类型的系统。

Xunsearch PHP-SDK 是该项目的 PHP 语言开发工具包，面向普通开发者提供简要的 API ，在此基础上快速构建自己的全文检索应用。

1. 有哪些优势？?

  • 开源免费 Xunsearch 全面开源，并使用最流行的开源许可协议 GPL 发布。任何人均可以免费 获取本项目的全部源代码，并在许可条件下修改和再分发。

  • 分布式架构 搜索程序的前端和后端可以分离部署在不同服务器中，对于大规模的搜索应用还可以 对索引进行水平、垂直切割。

  • 开发难度极低 一般开发者只要在安装和设置完成后，通过提供的脚本 SDK 包，便可十分容易的进行 二次开发，打造出自己的全文搜索引擎。

  • 功能强大 支持字段检索、结果高亮、字段排序、布尔语法、区间检索、聚合搜索、相关搜索、 权重微调、拼音搜索、搜索建议等专业搜索引擎具备的各项功能。这也是本项目 相对于其它开源搜索软件的重要优势。

2. 适合哪些应用领域？?

适合具备独立服务器（需要Unix 类操作系统）的初、中型规模的网站，作为完整全文检索技术 解决方案。

支持 MySQL 数据库全文检索、Web站内/论坛搜索、行业门户/垂直搜索、企业级的站内搜索、 计算机文件搜索等，各种专业搜索引擎（如购物搜索，旅游搜索、游戏搜索）、文档/文献检索 等各种领域。

3. 运行环境与条件？?

您必须有一台（或多台）Unix 类的主机（推荐用各种 Linux、BSD 操作系统）用于安装 Xunsearch 后端服务程序。

Xunsearch PHP-SDK 则可以运行在包含 Windows 在内的服务器系统中，只要求 PHP 的 版本必须在 5.2.0 以上即可。具体运行时根据需求，PHP 还应包含一些扩展，参见本文档中的 RequiredCheck 运行条件检测 。

$Id$
« 概述
安装 »


安装

Xunsearch PHP-SDK 是与 xunsearch 后端服务协同工作的，所以后先必须先在您的服务器 上安装服务端，服务器操作系统要求必须是 Linux、BSD 或其它类 UNIX 系统，同时安装了 gcc、make 等基础编译环境。

1. 安装、升级 Xunsearch?

Tip: 即便您之前已经安装过 xunsearch，您也可以安装放心的使用该教程进行覆盖安装，会自动 完成升级操作，而不用担心会删除任何已存在的数据。我们制作的安装脚本包含了智能检测功能。

此外 xunsearch 的安装和运行都不需要 root 权限，强烈推荐单独设置一个系统用户用于安装 执行相关指令。

Xunsearch 全部开源，理论上也可以从官方提供的 git 仓库 获取源码自行构建，但是 这要求必须有较多的技术基础，在此不打算赘述，有这方面需求的技术人员可以到论坛去交流。 为方便用户安装，我们提供了一个包含安装所需求的全部组件的完整安装包（含 xapian/libevent/scws）， 您只要下载并安装此完整安装包即可，具体操作步骤如下：

  1. 运行下面指令下载、解压安装包

    wget http://www.xunsearch.com/download/xunsearch-full-latest.tar.bz2
tar -xjf xunsearch-full-latest.tar.bz2

  2. 执行安装脚本，根据提示进行操作，主要是输入 xunsearch 软件包的安装目录，强烈建议单独 规划一个目录，而不是混到别的软件目录中。

    cd xunsearch-full/
sh setup.sh

    第一次安装的话，过程可能会稍显漫长，请不必着急，您大可泡杯茶一边喝一边等待即可。

  3. 待命令运行结束后，如果没有出错中断，则表示顺利安装完成，然后就可以启动/重新启动 xunsearch 的后台服务，下面命令中的 $prefix 请务必替换为您的安装目录，而不是照抄。

    cd $prefix ; bin/xs-ctl.sh restart

    强烈建议您将此命令添加到开机启动脚本中，以便每次服务器重启后能自动启动搜索服务程序， 在 Linux 系统中您可以将脚本指令写进 /etc/rc.local 即可。

  4. 有必要指出的是，关于搜索项目的数据目录规划。搜索系统将所有数据保存在 $prefix/data 目录中。 如果您希望数据目录另行安排或转移至其它分区，请将 $prefix/data 作为软链接指向真实目录。

Info: 出于性能和多数需求考虑 xunsearch 服务端和 SDK API 通讯时没有加密和验证处理， 并且默认情况 xs-ctl.sh 启动的服务程序是绑定并监听在 127.0.0.1 上。

如果您的 SDK 调用和 xunsearch 服务端不在同一服务器，请使用 -b inet 方式启动脚本， 并注意借助类似 iptables 的防火墙来控制 xunsearch 的 8383/8384 两个端口的访问权限。 启动脚本用法举例如下，以下均为合法使用方式：

bin/xs-ctl.sh -b local start    // 监听在本地回环地址 127.0.0.1 上
bin/xs-ctl.sh -b inet start     // 监听在所有本地 IP 地址上
bin/xs-ctl.sh -b a.b.c.d start  // 监听在指定 IP 上
bin/xs-ctl.sh -b unix start     // 分别监听在 tmp/indexd.sock 和 tmp/searchd.sock

2. 安装 PHP-SDK?

PHP-SDK 的代码不需要另行下载，已经包含在 xunsearch 的安装结果中了，在此假设您将 xunsearch 安装在 $prefix 目录，那么 $prefix/sdk/php 即是 PHP-SDK 的代码目录。目录结构及功能逻列如下：

_
|- doc/                    离线 HTML 版相关文档
|- app/                    用于存放搜索项目的 ini 文件
|- lib/XS.php              入口文件，所有搜索功能必须且只需包含此文件
\- util/                   辅助工具程序目录
    |- RequireCheck.php    用于检测您的 PHP 环境是否符合运行条件
    |- IniWizzaard.php     用于帮助您编写 xunsearch 项目配置文件
    |- Quest.php           搜索测试工具
    \- Indexer.php         索引管理工具

如果您的搜索应用程序和 xunsearch 在同一台服务器，则无需复制任何代码，在开发的时候直接包含 入口文件 $prefix/sdk/php/lib/XS.php 即可。代码如下：

require_once '$prefix/sdk/php/lib/XS.php';

如果您在其它服务器部署前端搜索代码，请将 SDK 代码整个目录复制到相应的服务器上，但并不要求放到 web 可访问目录，考虑安全性也不推荐这么做。

Note: 对于前端和后端分离的用户来说，当 xunsearch 升级安装后必须重新复制安装后的 SDK 代码 去覆盖，否则可能造成版本不匹配导致错误。

3. 检测 PHP-SDK 的运行条件?

从现在开始的文档及示范代码中我们都假定您将 xunsearch 安装在 $prefix 目录中，而不再另行说明。

基础运行条件要求 PHP 最低版本为 5.2.0，随着功能需求的不同可能还会用到一些其它扩展，具体请在 命令行环境里运行我们提供的检测脚本。如果您的 php 可执行文件不在默认搜索路径中，假设是安装在 /path/to/bin/php 请使用第二种方式运行。运行方式如下：

1. $prefix/sdk/php/util/RequiredCheck.php
2. /path/to/bin/php $prefix/sdk/php/util/RequiredCheck.php
3. $prefix/sdk/php/util/RequiredCheck.php -c gbk

运行结果输出的中文编码默认为 UTF-8 ，如果您使用 GBK 环境请在运行命令最后加上 -c GBK 。 运行结果会给出一个可视化的表格说明检测结果，并在最终给出检测结论，告诉您是否符合运行的基础要求。

至此，安装和准备工作已经完成了，您可以开始使用 Xunsearch PHP-SDK 开发自己的搜索应用了。

4. 完全删除 Xunsearch?

首先欢迎对 xunsearch 提出各种修改、改进建议，我们都会认真对待和处理。如果有一天您真的不再需要 xunsearch 了，那么只要停止运行后端服务，然后完全删除它的安装目录 $prefix 即可，整个软件包 所有涉及的文件均在该目录下。

Note: 如果您的安装目录没有单独设置，而是使用公共目录（如：/usr/local），请勿直接删除整个目录。

$prefix/bin/xs-ctl.sh faststop
rm -fr $prefix

5. 从 Git 源码仓库安装?

Note: 在开始安装前，无论如何请先安装一次最新发布的完整版本，以便将所有的运行依赖库安装到位。

我们的源代码托管在 github 上。您可以直接浏览代码仓库，也可以通过以下方式之一取得源代码：

git clone git://github.com/hightman/xunsearch.git
git clone https://github.com/hightman/xunsearch.git

如果要编译安装这份源代码，您还必须在主机环境上安装相关的 autoconf, automake 工具，然后运行 下面指令完成准备工作。

cd xunsearch/
chmod +x acprep
./acprep

在完成上述的准备工作后，接下来编译安装 xunsearch 就和常见的开源软件类似，假设您的安装目录为 $prefix，然后按以下步骤操作即可：

./configure --prefix=$prefix
make
make install
$Id$
« 关于 Xunsearch PHP-SDK
体验 demo 项目 »


体验 DEMO 项目

为了让您对 xunsearch 有一个初步的印象和了解，我们在这个章节，借助辅助工具 带您体验一下搜索和索引的操作。这里可能有很多内容您尚不熟悉，请不必感到惶恐， 只要跟着说明完成操作即可，后面还有大量章节对各项进行深入讲解。

Note: 这些例子中的命令、代码均假定为默认的 UTF-8 环境，如果您使用的是 GBK 编码环境，请在运行所有 php 脚本时加入 -c gbk 参数。

1. 查看项目配置文件?

每一个 xunsearch 搜索项目都有一个独立的 INI 配置文件。DEMO 项目的配置文件 位于 $prefix/sdk/php/app/demo.ini 使用以下指令便可直接查看配置文件内容。

cat $prefix/sdk/php/app/demo.ini

没错，配置文件很简单、清晰，它默认连接本地的 8383、8384 端口并使用 UTF-8 编码，包含四个字段：pid，subject，message，chrono 。

2. 填充索引数据?

出于测试方便，我们采用 csv 格式来写入索引数据，请先按以下方式操：

cd $prefix/sdk/php
util/Indexer.php --source=csv --clean demo

然后脚本会给出如下提示：

初始化数据源 ... csv
WARNING: input file not specified, read data from <STDIN>
开始批量导入数据 ...

然后我们测试输入下面三条数据，最后一条数据打完后必须敲入回车，然后按 Ctrl-D 结束操作。

1,关于 xunsearch 的 DEMO 项目测试,项目测试是一个很有意思的行为！,1314336158
2,测试第二篇,这里是第二篇文章的内容,1314336160
3,项目测试第三篇,俗话说，无三不成礼，所以就有了第三篇,1314336168

这里屏幕会提示如下信息，表示完成数据提交：

INFO: reach end of file or error occured, total lines: 3
完成索引导入：成功 3 条，失败 0 条
刷新索引提交 ...

索引数据的提交是异步行为，所以录完数据并不是立刻就能检索到（当然，这个时间差也不会太大）。 所以为稳定起见请稍等几秒再进行下一步的搜索测试。

3. 测试搜索?

首先，我们体验一下正常的搜索，分别以关键词 项目、测试、项目测试、俗话说、莫须有 进行检索：

cd $prefix/sdk/php
util/Quest.php demo 项目
util/Quest.php demo 测试
util/Quest.php demo 项目测试
util/Quest.php demo 俗话说
util/Quest.php demo 莫须有

每个搜索后的关键词系统都会记录下来进行分析，并保存在日志中用于统计热门搜索、搜索建议、相关搜索等。 默认情况系统会每 2 小时进行一次更新日志，由于我们在进行测试，所以请使用以下指令进行强制刷新。

util/Indexer.php --flush-log demo

接下来我们测试一下稍微复杂一点的字段、布尔搜索功能：

util/Quest.php demo subject:测试
util/Quest.php demo pid:2
util/Quest.php demo "第三篇 OR pid:1"

最后我们再测试一下日志相关的搜索情况，其中 --suggest 是搜索建议，当用户敲入少许汉字或字母时给出 相应的关键词建议（常用于搜索输入框下拉自动提示），而 --related 表示相关搜索、--hot 表示热门搜索、 --correct 表示搜索纠错。

util/Quest.php --hot demo
util/Quest.php --related demo 项目
util/Quest.php --correct demo yunsearch
util/Quest.php --correct demo xmcs
util/Quest.php --correct demo xianmu
util/Quest.php --suggest demo x
util/Quest.php --suggest demo xm
util/Quest.php --suggest demo xia
util/Quest.php --suggest demo xiangmuc
util/Quest.php --suggest demo 项

4. 总结?

现在应该对 xunsearch 所提供的功能有了初步的印象。您可能会发现在这个体验过程中并 没有涉及到一行代码，因为我们目的只是先了解和接触一下 xunsearch 搜索，所以采用 了附加提供的脚本工具来完成所有操作，关于脚本工具的详细使用请阅读后面的专题章节。

$Id$
« 安装
开发规范 »


开发规范

在开始任何开发之前，有必要先强调一下开发规范。开发规范并不是强制性的要求，而只是 为了使项目及代码更利于维护、阅读而制定的一些习惯行为公约。

1. 目录设置?

无论您是否将 PHP-SDK 的代码拷出单独使用，强烈建议保留现有的目录结构。

开发的项目配置 ini 文件统一放入 $prefix/sdk/php/app 目录。

自定义或扩展的类库请统一放入 $prefix/sdk/php/lib 以便 XS 进行 autoload。

2. 保留字?

所有 Xunsearch PHP-SDK 的系统类库定义时均以 XS 开头，为避免冲突，请自行编写 代码和定义时不要也以 XS 开头。

3. 自定义分词?

Xunsearch 在项目配置时支持针对某些字段自定义分词。每个自定义分词功能单独使用一个 文件名，命名为 XSTokenizerXyz.class.php，其中 Xyz 是您自己的分词名称，文件统一 放入 $prefix/sdk/php/lib 目录，分词类名即为 XSTokenizerXyz，该类必须实现 XSTokenizer 接口。

4. 字段设计?

多数搜索项目可能都是和相关的数据库联动的，出于开发方便，强烈建议让搜索项目的字段命名 和实际数据库内的命名一致。在此基础下推荐以下命名规范：

  • 字段名都使用小写命名，您可以根据自己的偏好选择用单数或复数形式，但不要二者同时使用。
  • 名字中的单词应使用下划线分割 (例如 product_order)。
  • 只在搜索项目中设置搜索结果展示、搜索条件必须用到的字段，不要在搜索项目中保存无关字段。

5. 异常处理?

由于 Xunsearch PHP-SDK 全面采用面向对象技术，所有失败的操作均抛出类型为 XSException 的异常，请在执行主线中捕捉它。具体参见 XSException 异常 章节的有关介绍。

$Id$
« 体验 demo 项目
开发流程 »


开发流程

我们现在讲解一下使用 Xunsearch PHP-SDK 开发搜索应用时的一般开发流程。 此处的开发流程假设我们已经完成了对应用的需求分析和必要的设计分析、Xunsearch安装等操作。

  1. 编写项目配置文件，请先阅读编写第一个配置文件然后进行手工编写， 或使用在线辅助工具生成配置文件。

  2. 决定索引更新方式，如果项目搜索的实时要求比较高、而且采用 PHP 开发，则建议修改项目代码， 在数据变动时调用 XSIndex 的相关 API 进行索引同步；否则请另行编写索引管理工具， 选用定时或不定时重建的方式。

  3. 引入 PHP-SDK 的入口文件 $prefix/sdk/php/lib/XS.php 进行搜索功能和界面开发。

  4. 最终调整与正式部署。

Tip: 我们提供了一个默认的搜索功能生成工具，通过读取和分析配置文件智能生成搜索相关代码， 同时还包含了相应的搜索界面。强烈建议您先使用工具生成代码，然后在此基础上二次开发即可。 详情阅读 SearchSkel 生成搜索骨架代码。

项目配置文件中的字段设置直接影响着索引数据库的数据。所以如果您修改了一个已有数据的 的项目字段配置，那么您必须进行清空索引或平滑重建索引， 否则可能会引起搜索结果不正确。

$Id$
« 开发规范
基础对象概述 »


基础对象概述

Xunsearch PHP-SDK 全面采用面向对象（OOP）的开发方式。 本章节将简单介绍开发过程中要接触到的几个重要对象（类）。

  • XS 搜索项目的总对象，所有操作均基于此对象或其属性。
  • XSException 所有操作中出现的异常、错误均抛出此类型的异常，搜索代码中应该尝试捕捉该异常以确定操作是否成功。
  • XSDocument 文档用于描述检索/索引的基础对象，包含一组字段及其值，相当于常规SQL数据表中的一行记录。
  • XSIndex 提供索引添加/删除/修改功能，通常以 XS 对象的属性方式出现，参见 XS::index。
  • XSSearch 提供各种搜索功能，通常以 XS 对象的属性方式出现，参见 XS::search。
  • XSTokenizer 自定义字段词法分析器接口。

类对象中的魔术属性

通过 PHP 对象中的 __get 和 __set 技巧，我们针对所有 XSComponent 的子类实现了对象的模拟属性。 这类读取或写入属性值时实际上是隐含调用了相应的 getter/setter 方法，这类属性不区分大小写。

$a = $obj->text; // $a 值等于 $obj->getText() 的返回值
$obj->text = $a; // 等同事调用 $obj->setText($a)

支持这类属性的对象主要包括以下几个（不全，仅挑重要的列出）：

  • XS::index 项目索引对象：$xs->index
  • XS::search 项目搜索对象：$xs->search
  • XS::defaultCharset 项目默认字符集：$xs->defaultCharset
  • XSSearch::dbTotal 搜索数据库内的数据总量：$xs->search->dbTotal
  • XSSearch::lastCount 最近一次搜索的结果匹配总数估算值：$xs->search->lastCount
  • XSSearch::query 搜索语句：$xs->search->query
$Id$
« 开发流程
XS 项目 »


XS 项目

XS 是搜索项目的总对象，所有操作均基于此对象或其属性，在此我们只挑最常用的一些属性、方法进行介绍。

1. 创建 XS 对象?

创建 XS 对象的唯一参数是项目配置文件的可访问路径，或者项目名，也可以是项目配置文件的内容。如果给出 的参数是项目名则自动使用 $prefix/sdk/php/app/项目名.ini，例子代码如下：

require_once '$prefix/sdk/php/lib/XS.php';
$xs = new XS('demo');   // 自动使用 $prefix/sdk/php/app/demo.ini 作项目配置文件
$xs = new XS('/path/to/demo.ini');  // 使用 /path/to/demo.ini

2. 重要属性?

  • XS::defaultCharset 项目默认字符集 影响范围包括使用时的输入数据以及搜索结果的输出数据，该属性默认由配置文件中的 project.default_charset 指定，如有必要可以自行修改，但要确保在使用索引、搜索对象之前。

  • XS::index 索引管理对象 类型为 XSIndex，比如用下面代码可以清空项目索引内容：

    $xs->index->clean();

  • XS::search 搜索对象 类型为 XSSearch，比如用下面代码可以快速检索包含 hightman 的结果

    $docs = $xs->search->search('hightman');
print_r($docs);
$Id$
« 基础对象概述
XSException 异常 »


XSException 异常

所有操作中出现的异常、错误均抛出此类型的异常，搜索代码中应该尝试捕捉该异常以确定操作是否成功。

已通过重载魔术方法 XSException::__toString 对出错信息作了简要的美化和修饰，您可以直接输出该对象， 如需要查看堆栈追踪，通过继承来的 XSException::getTraceAsString 即可。

Note: 在系统内部将所有的错误处理也转换成为抛出 XSErrorException 对象，而 XSErrorException 继承自 XSException，所以实际代码中只要统一捕捉 XSException 即可。

一段典型的处理代码应类似下面的方式：

require '$prefix/sdk/php/lib/XS.php';
try
{
    $xs = new XS('demo');
    $docs = $xs->search->setQuery('hightman')->setLimit(5)->search();
    foreach ($docs as $doc)
    {
       echo $doc->rank() . ". " . $doc->subject . " [" . $doc->percent() . "%]\n";
       echo $doc->message . "\n";
    }
}
catch (XSException $e)
{
    echo $e;               // 直接输出异常描述
    if (defined('DEBUG'))  // 如果是 DEBUG 模式，则输出堆栈情况
        echo "\n" . $e->getTraceAsString() . "\n";
}
$Id$
« XS 项目
XSDocument 文档 »


XSDocument 文档

XSDocument 是用于描述检索/索引的基础对象，包含一组字段及其值。相当于常规 SQL 数据表中的一行记录。 通过魔术方法，每个字段名都是文档的虚拟属性，可直接赋值或取值，也支持数组下标方式访问文档字段。

$doc = new XSDocument;
$doc->name = 'value'; // 用对象属性方式进行赋值、取值
$doc['name'] = 'value'; // 用数组下标方式进行赋值、取值
$value = $doc->f('name'); // 用函数方式进行取值
$doc->setField('name', 'value'); // 用函数方式进行赋值
$doc->setFields(array('name' => 'value', 'name2' => 'value2')); // 用数组进行批量赋值

// 迭代方式取所有字段值
foreach($doc as $name => $value)
{
    echo "$name: $value\n";
}

用于索引更新的文档对象和搜索结果返回中略有不同，下面分开讲解。

1. 搜索结果文档?

搜索结果文档是指 XSSearch::search 返回值包含的文档，它由 PHP-SDK 内部代码创建。 除了普通文档的功能外，还支持以下魔术方法来访问搜索结果的元数据：

  • docid() 取得搜索结果文档的 docid 值 (实际数据库内的 id，一般用不到)
  • rank() 取得搜索结果文档的序号值 (第X条结果)
  • percent() 取得搜索结果文档的匹配百分比 (结果匹配度, 1~100)
  • weight() 取得搜索结果文档的权重值 (浮点数)
  • ccount() 取得搜索结果折叠的数量 (按字段折叠搜索时才有效)

访问结果文档的字符集默认是与 XSSearch::setCharset 所指定的字符集一致的，如未指定过则为 XS::defaultCharset。 虽然您可以通过 XSDocument::setCharset 来强制修改文档的字符集，但不推荐这样做，也没必要这样做。

2. 索引文档?

索引文档是指由用户创建，并用于提交更新到索引库中的文档，创建文档时允许传入字符集作为构造函数的参数， 如未传入，则自动视为项目的默认字符集 XS::defaultCharset，如果字符集不是 UTF-8 的系统内部会 自动进行转换，但要求 PHP 带有中文转码扩展 (iconv 或 mbstring)

$doc = new XSDocument;  // 使用默认字符集
$doc = new XSDocument('gbk'); // 指定文档为 GBK 编码
$doc = new XSDocument($data); // 数组 $data 为初始字段数据, 编码则为默认编码
$doc = new XSDocument($data, 'gbk'); // 数组 $data 为字段数据, 编码为 GBK

// 然后进行一系列赋值操作

// 提交到索引中
$xs->index->add($doc);

如果您希望在 XSDocument 提交到索引前、后进行一系列必要的操作，您可以对 XSDocument 进行扩展， 重写 XSDocument::beforeSubmit 和 XSDocument::afterSubmit 方法来实现，比如扩展代码为 $prefix/sdk/php/lib/MyDocument.class.php。

class MyDocument extends XSDocument
{
    public function beforeSubmit(XSIndex $index)
    {
        // 在此编写您的代码，如需要中断添加请提前返回 false
        // ...
        return parent::beforeSubmit($index);
    }

    public function beforeSubmit(XSIndex $index)
    {
        // 在此编写您的代码，只有加入索引成功后才会执行
        // ...
    }
}

$doc = new MyDocument('gbk');
$doc->pid = 123;
$doc->subject = 'Hello, 测试';
$doc->message = '测试的内容在此';
$xs->index->add($doc);
$Id$
« XSException 异常
XSIndex 索引管理 »


XSIndex 索引管理

通过 XSIndex 对象实现索引数据库的增、删、改等相关操作。实现使用过程中并不需要 自行创建该对象，而是直接访问 XS::index 即可。

require '$prefix/sdk/php/lib/XS.php';
$xs = new XS('demo');
$index = $xs->index; // 索引对象来自 XS 的属性

关于索引对象的详细用法剖析请阅读后面的专题。

$Id$
« XSDocument 文档
XSSearch 搜索 »


XSSearch 搜索对象

通过 XSSearch 对象实现各种搜索相关操作。实现使用过程中并不需要自行创建该对象， 而是直接访问 XS::search 即可。

require '$prefix/sdk/php/lib/XS.php';
$xs = new XS('demo');
$search = $xs->search; // 搜索对象来自 XS 的属性

默认情况，搜索对象操作时对于用户输入的参数、搜索结果的输出编码视为默认编码，即 XS::defaultCharset。 如果您需要指定不同编码，请调用 XSSearch::setCharset 进行设置。

$search->setCharset('gbk');

关于搜索对象的详细用法剖析请阅读后面的专题。

$Id$
« XSIndex 索引管理
XSTokenizer 分词接口 »


XSTokenizer 分词接口

XSTokenizer 是搜索字段的分词接口定义，整个接口只要求实现一个方法 XSTokenizer::getTokens， 自定义分词器用于 INI 配置文件中的 tokenizer 选项。

关于自定义分词器的详细用法剖析请阅读后面的专题。

$Id$
« XSSearch 搜索
项目配置详解 »


项目配置文件详解

1. 基础说明?

  • 项目配置是一个 Xunsearch 项目的核心灵魂，非常重要，通常保存为 .ini 文件
  • 以分号开头的行表示注释，空行直接被忽略不起任何作用
  • 中括号包围的每个分区均为字段配置，字段个数根据项目的实际需求设定
  • 每个项目必须有并且只能有一个类型类 ID 的主键字段，ID 字段值的字母不区分大小写

2. 项目设置?

  1. 项目名称

    要求用纯小写字母和小划线组成，长度控制在 2-31 个字符，这也是 xunsearch 服务器内用于保存索引数据的目录名称, 所以同一 xunsearch 服务器内的不同项目名称不可重复。默认为不含后缀的配置文件名， 如：xyz.ini 则项目名默认为 xyz 。

    project.name = AppName

  2. 默认字符集

    这里的默认字符集范围涵盖服务端交互时的输入数据、输出数据，实际使用时索引文档、 检索服务器仍可重新指定字符集

    project.default_charset = UTF-8

  3. 服务器连接参数

    服务端连接参数的格式包含 3 种格式：

    1. 端口号(数字)，连接 localhost 的该端口号 (例：8383)
    2. 地址:端口号，冒号连接地址（域名、IP地址）和端口 (例：127.0.0.1:8383)
    3. 文件路径，本机的 unix socket 连接路径 (例：/tmp/index.sock)
    ; 索引服务端配置，默认值为 8383
server.index = 8383
; 搜索服务端配置，默认值为 8384
server.search = 8384

3. 项目字段设计?

每个搜索项目均可以简单地理解为单表检索，凡是涉及关联表的， 请将关联记录转换为搜索项目的新字段并设置对应的分词规则。字段设计很重要， 请仔细根据需求创建，字段名建议和您的实际数据库字段一致。

  1. 定义字段

    每个字段用一个区段配置来表示，中括号内的名字即为字段名。每个项目包含若干个字段， 具体由项目搜索需求决定，并不需要一味的与实际源数据库 (如 MYSQL) 一致， 而只需要设计搜索功能所涉及的字段即可。

    [field_name]

  2. 字段选项

    每个字段根据实际情况指定字段选项，所有选项均有默认值，所以即便不指定任何选项而只有中括号定义的字段， 那也是一个合法的字段，字段选项包括以下几种：

    type 字段类型

    • string 字符型，适用多数情况，也是默认值
    • numeric 数值型，包含整型和浮点数，仅当字段需用于以排序或区间检索时才设为该类型，否则请使用 string　即可
    • date 日期型，形式为 YYYYmmdd 这样固定的 8 字节，如果没有区间检索或排序需求不建议使用
    • id 主键型，确保每条数据具备唯一值，是索引更新和删除的凭据，每个搜索项目必须有且仅有一个 id 字段，该字段的值不区分大小写
    • title 标题型，标题或名称字段，至多有一个该类型的字段
    • body 内容型，主内容字段, 即本搜索项目中内容最长的字段，至多只有一个该类型字段，本字段不支持字段检索
    type = string

    index 索引方式

    xunsearch 的索引有 2 种模式：其一是不标明字段的检索，称之为“混合区检索”；其二是标明特定字段的“字段检索”。 例如：搜索 XXX YYY 表示在混合区检索，返回的结果可能是 title 也有可能是 body 字段符合匹配； 而搜索 title:XXX 则表示仅检索 title 匹配 XXX 的数据。每个字段可以指定的索引方式的值如下：

    • none 不做索引，所有的搜索匹配均与本字段无关，这个字段只用于排序或搜索结果展示用到。
    • self 字段索引，可以在搜索时用 field:XXX 来检索本字段
    • mixed 混合区索引，不标明字段的默认搜索也可以检索本字段
    • both 相当于 self + mixed，两种情况均索引

    通常情况默认值为 none ，但 id 型字段默认是 self ，title 型字段是 both ，body 型字段则固定为 mixed 。

    index = none

    tokenizer 分词器

    默认为 default 采用内置的功能强大的 scws 分词，适合绝大多数字符串字段。也可以指定自定义分词器， 格式为 name 或 name(arg) 两种形式，其中 name 是分词器名称，arg 则是传递给分词器构造函数的参数。 自定义分词器需要在 lib/ 目录下编写名为 XSTokenizerName 的分词类并实现接口 XSTokenizer， 内置支持的分词器有以下几种：

    • full 表示本字段的值整体作为一个检索词，像各种 ID 都适合这种情况
    • none 表示本字段没有任何词汇用于索引
    • split([ ]) 表示根据参数分割内容，默认参数为空格，若参数以 / 开头并以 / 结尾则 内部调用 preg_split(arg, ..) 来分割取词，以支持正则或其它特殊字符分割
    • xlen([2]) 表示根据指定参数长度分段取词，如 ABCDEF => AB + CD + EF
    • xstep([2]) 表示根据指定参数步长逐段取词，如 ABCDEF => AB + ABCD + ABCDEF
    tokenizer = default

    cutlen 搜索结果摘要截取长度

    默认值为 0 表示不截取。主要是针对某些内容特别长的字段在返回结果时自动剪取包含关键词的一小段文字。 典型的是 body 型字段默认为 300 。长度单位是字节，通常 UTF-8 编码的一个汉字为 3 个字节。

    cutlen = 0

    weight 混合区检索时的概率权重

    在混合检索时，可以对标题和内容等不同字段进行权重计算，如果你不想该字段参与计算权重可设为 0 。 通常默认值为 1 ，但 title 型默认为 5 而 body 型则固定为 1 。

    weight = 1

    phrase 是否支持精确检索

    即当给搜索语句加上引号时，则要求匹配的结果必须严格按照搜索词的顺序匹配，此外还支持用 NEAR 之类的语法来做精确检索，具体参见：搜索技巧 。通常默认值为 no 但是 title 和 body 型字段默认则为 yes 。值得注意的是该功能仅支持默认分词器，如非必要请勿开启此项， 因为这会增加索引数据的大小。

    phrase = no

4. 配置示例文件?

下面是 discuz 搜索项目的配置示范文件，包含 12 个字段。其中可以看到 tid 和 fid 虽然从内容上讲它们都是数字型，但没有排序需求所以仍应为 string 类型，而 dateline 由于有排序需求，所以必须指定为 numeric 类型。

    project.name = sample
    project.default_charset = GBK
    ;server.index = 8383
    ;server.search = 8384

    [pid]
    type = id

    [subject]
    type = title

    [message]
    type = body

    [dateline]
    type = numeric

    [author]
    index = both

    [authorid]

    [tid]
    index = self
    tokenizer = full

    [fid]
    index = self
    tokenizer = full

    [flag]
$Id$
« XSTokenizer 分词接口
自定义分词器 »


自定义分词器

Xunsearch 默认内置了功能强大的 SCWS 分词系统，也附加提供了一些简单常见的分词规则， 但考虑到用户的个性需求，特意提供了自定义分词器的功能。

Note: 自定义分词器存在一个缺陷，它不支持存储位置信息，也就是不能按短语检索、以及 NEAR 之类的 语法检索。相当于该字段配置中的 phrase 值恒为 no，通常把自定义分词用于一些带有一定规则的简要 字段，而不是更多的考虑语义关系。

1. 编写自定义分词器?

自定义分词器必须实现 XSTokenizer 接口。假定您要编写一个名为 xyz 的分词器，则您要编写的代码 文件为 XSTokenizerXyz.class.php，请将文件统一放入 $prefix/sdk/php/lib 目录。

通常来讲，您只需要实现 XSTokenizer::getTokens 即可。该函数接受 2个参数，分别为要分词的值以及 当前的文档对象(可选)；返回值为分好的词汇组成的数组。下面以按 - 分割字段为例：

class XSTokenizerXyz implements XSTokenizer
{
    public function getTokens($value, XSDocument $doc = null)
    {
        $ret = array();
        if (!empty($value))
            $ret = explode('-', $value);
        return $ret;
    }
}

Note: XSTokenizer::getTokens 的参数 $value 的编码始终为 UTF-8 。

如果您需要编写带有参数支持的分词器，比如让用户传入按什么字符分割，请参照下面写法编写构造函数：

class XSTokenizerXyz implements XSTokenizer
{
    private $delim = '-'; // 默认按 - 分割

    public function __construct($arg = null)
    {
        if ($arg !== null && $arg !== '')
            $this->delim = $arg;
    }

    public function getTokens($value, XSDocument $doc)
    {
        $ret = array();
        if (!empty($value))
            $ret = explode($this->delim, $value);
        return $ret;
    }
}

2. 使用自定义分词器?

编写完了自定义分词器的代码后，您就可以在项目配置文件中使用它了，在需要用这个分词器的字段中 指定 tokenizer 选项的值，例子中省略了字段的其它选项，实际编写时可能还包括其它选项。

而在搜索语句中，如果指明了字段搜索前缀 field:XXX 那么搜索引擎内部也会 对这个搜索语句执行自定义分词。

[some_field]
; 不带参数的用法
tokenizer = xyz
; 带参数的用法，表示把 _ 作为参数传递给构造函数
tokenizer = xyz(_)
$Id$
« 项目配置详解
编写第一个配置文件 »


编写第一个配置文件

Xunsearch 的配置文件是纯文本的 INI 格式，用任何文本编辑器均可编写，在 Unix 类型的系统下推荐 用 vi，而 Windows 下可以用记事本或 EditPlus 进行编写。我们也正在计划不久的将来制作一个 Web 界面的编辑工具，以便进行可视化的设计以及约束检测。

Tip: 目前我们针对新手用户开发了一个 WEB 版本的在线配置文件编辑辅助工具，点击试用。

如果您是从头阅读到这儿，应该对配置文件也有了基本的了解，下面我们以传统的 discuz 论坛贴子为例子， 来学习编写配置文件。

1. Discuz 的论坛贴子 MySQL 表结构?

由于我们只是对贴子进行搜索，所以只需要关心它的贴子表即可，表结构如下：

--
-- 表的结构 `cdb_posts`
--
CREATE TABLE `cdb_posts` (
  `pid` int(10) unsigned NOT NULL auto_increment,
  `fid` smallint(6) unsigned NOT NULL default '0',
  `tid` mediumint(8) unsigned NOT NULL default '0',
  `first` tinyint(1) NOT NULL default '0',
  `author` varchar(15) NOT NULL default '',
  `authorid` mediumint(8) unsigned NOT NULL default '0',
  `subject` varchar(80) NOT NULL default '',
  `dateline` int(10) unsigned NOT NULL default '0',
  `message` mediumtext NOT NULL,
  `useip` varchar(15) NOT NULL default '',
  `invisible` tinyint(1) NOT NULL default '0',
  `anonymous` tinyint(1) NOT NULL default '0',
  `usesig` tinyint(1) NOT NULL default '0',
  `htmlon` tinyint(1) NOT NULL default '0',
  `bbcodeoff` tinyint(1) NOT NULL default '0',
  `smileyoff` tinyint(1) NOT NULL default '0',
  `parseurloff` tinyint(1) NOT NULL default '0',
  `attachment` tinyint(1) NOT NULL default '0',
  `rate` smallint(6) NOT NULL default '0',
  `ratetimes` tinyint(3) unsigned NOT NULL default '0',
  `status` tinyint(1) NOT NULL default '0',
  PRIMARY KEY  (`pid`),
  KEY `fid` (`fid`),
  KEY `authorid` (`authorid`),
  KEY `dateline` (`dateline`),
  KEY `invisible` (`invisible`),
  KEY `displayorder` (`tid`,`invisible`,`dateline`),
  KEY `first` (`tid`,`first`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8;

2. 搜索的设计与分析?

首先得搞明白我们要得到什么样的搜索效果，进行针对性的设计和分析才能确定需要哪些字段。在 这个案例中我们的需求是要对所有的论坛贴子进行标题、内容、作者全文检索，并希望搜索结果能 过滤回贴、按发表时间排序、能按版块过滤。明确了意图之后我们就只需要从数据表中提取必要的 字段即可。

  • pid 主键，必须的
  • fid 按版块检索时需要用到
  • tid 搜索结果阅读链接都是按 tid 来的，必备
  • first 表示该贴子是否为楼主，过滤回贴时用到
  • author, authorid 按作者检索用得到
  • subject 标题
  • message 贴子内容
  • dateline 发表时间，要用到

其它的有些字段，比如 invisible, anonymous 表面看上去跟搜索结果也是有关系的，但 实际上这些可以在索引入库时进行处理和排除，完全没有必要放到搜索数据中去，所以不列入。

3. 字段设计与分析?

得到字段后，进一步分析各个字段的用途与写法。

  • pid 非常明显，它是主键，所以它的类型必须是 id
  • fid 内容为数字的版块ID，但由于我们不需要对它进行排序或区间查找，所以用 string 类型即可， 为了按版块检索，我们必须对它进行索引，而它也不需要分词，所以分词器用内置的 full。
  • tid 内容也是数字的主题ID，我们暂不考虑按主题搜索，所以不需要索引，也没有排序等要求，所以 只要当作普通字段存储下来即可。
  • first 用 1/0 不同值来表示是否为楼主，考虑到回贴过滤需求，它也需要索引，整个字段转换成一个 词即可，分词器当然选用 full。
  • author 作者名字检索，考虑中文名支持，所以建议这个字段保留使用默认内置的 scws 分词器。 由于希望在默认不指明字段的情况下也能检索作者字段，所以它的过索引方式应为 both。
  • authorid 作者 ID，我们只用于搜索结果的作者链接，不需要索引。
  • subject 标题类型为 title。
  • message 主内容，类型为 body。
  • dateline 时间戳记，由于需要排序，所以该字段不能存为 string，必须用 numeric，但不需要 进行任何索引。

4. 配置文件结果?

自此字段分析与设计已然明了，下面给出完整的配置文件内容，实际使用时可以将文件保存至 $prefix/sdk/php/app/discuz.ini。

project.name = discuz
project.charset = GBK
;服务端用默认值
;server.index = 8383
;server.search = 8384

[pid]
type = id

[fid]
index = self
tokenizer = full

[tid]

[first]
index = self
tokenizer = full

[author]
index = both

[authorid]

[subject]
type = title

[message]
type = body

[dateline]
type = numeric
$Id$
« 自定义分词器
索引概述 »


索引概述

1. 什么是索引？?

索引在 Xunsearch 中泛指用于检索的数据库文件总称。每一次检索数据均为访问和读取 索引文件，每一次更新数据则是对索引文件的修改。

Xunsearch 每个搜索项目的索引数据是分开单独存放的，索引本质上是一系列预设计文件， 内部都是特别设计的 Tree 结构，但这些东西不是这份指南所关注的。假设一个项目的名称 为 demo (即 INI 文件中 project.name 的值)，那么它在服务端的索引数据的存储 路径为：

$prefix/data/demo

该目录底下包含 2 个主要索引数据库，分别是

  • db 默认的主索引数据库，各种检索数据均存放在此
  • log_db 搜索日志数据库，用于存放搜索日志相关，用于实现相关搜索、搜索建议、拼音搜索等

2. 如何开始使用索引？?

在 PHP-SDK 中，索引管理由类型为 XSIndex 的对象所维护。在 XS 项目中，通过读取 XS::index 属性来获取索引管理对象，然后展开各种操作，而不是自行创建对象。后面章节中 的相关测试代码如果没有特别编写，其中的 $index 均为通过类似以下的方式获取的索引对象：

require '$prefix/sdk/php/lib/XS.php';
$xs = new XS('demo'); // 建立 XS 对象，项目名称为：demo
$index = $xs->index; // 获取 索引对象

索引的维护包括数据修改/更新、数据删除、添加数据等操作，由后面的篇幅分开详细讲解。

3. 如何更换索引库？?

默认使用的索引库名称为 db，对应到服务端的 $prefix/data/$name/db，一般开发时不需要 关注这个本质。但如果您的索引数据库相当大，希望对索引库进行适当的分割，可以自命名新的索引库。 在搜索时可以使用 XSSearch::addDb 进行多库并行搜索。

// 比如把当前索引更新到名称为 db2 的库中
$index->setDb('db2');

// 在检索时同时搜索 db, db2 的作法，具体参考搜索的有关章节
...
$search->addDb('db2')->setQuery(...)->search();

4. 索引更新是异步行为?

有必要指出的是，出于性能优化设计，所有的索引操作（包含添加、删除、修改文档）均是异步的 行为。也就是说在 PHP-SDK 的相关 API 返回后，只是说明已经将索引变动提交到操作队列中， 而并不是已经立即更新到磁盘上的索引数据库文件。因此，搜索结果将不能立即体现出您的变动。

但请放心，这个时间差我们已经控制在非常合理的范围内，通常是若干秒。

Note: 特别说明，索引清空是同步操作，立即生效。

$Id$
« 编写第一个配置文件
添加文档 »


添加文档

搜索数据库在创建之初或运行过程中，都免不了要往索引数据库添加新文档用于检索。

首先您必须创建类型为 XSDocument 的文档对象，关于文档对象的创建请参阅 XSDocument 文档 中关于索引文档的部分。特别要指出 的是，文档中的主键字段值必须明确指定（即类型为 ID 的字段），否则在添加、 更新过程中会抛出异常，其余字段若未指定则相当于忽略这些字段。

索引文档创建后，再调用 XSIndex::add 方法将文档加入索引数据库中即可。

Note: 如果索引数据库中已存在主键值相同的文档，并不会有冲突提示，文档仍会 添加成功并且不会有任何冲突提醒。所以除非您明确知道此为新文档，否则建议使用 更新文档。

此外，如果您自行扩展了 XSDocument，请使用您自定定义的文档类创建对象。

参考代码如下：

$data = array(
    'pid' => 234, // 此字段为主键，必须指定
    'subject' => '测试文档的标题',
    'message' => '测试文档的内容部分',
    'chrono' => time()
);

// 创建文档对象
$doc = new XSDocument;
$doc->setFields($data);

// 添加到索引数据库中
$index->add($doc);
$Id$
« 索引概述
更新、修改文档 »


更新、修改文档

在 Xunsearch PHP-SDK 中，更新、修改文档和添加文档的做法非常的类似， 只不过调用的是 XSIndex::update，并且在内部处理上有所区别。

如果索引数据库中已存在主键值相同的文档，那么相当于先删除原有的文档，再用当前文档替换它。 如果未存在主键值相同的文档，则效果和添加文档完全一致。

Info: 有人可能会想，既然如此为什么还要有 XSIndex:add 呢，因为添加文档少了一个判断 过程，具有更高一些的效率。因此，如果您在使用 API 时能明确知道当前文档是新增的，那么 还是建议使用 add 这个 API。

示范代码如下：

$data = array(
    'pid' => 234, // 此字段为主键，是进行文档替换的唯一标识
    'subject' => '测试文档的标题',
    'message' => '测试文档的内容部分',
    'chrono' => time()
);

// 创建文档对象
$doc = new XSDocument;
$doc->setFields($data);

// 更新到索引数据库中
$index->update($doc);
$Id$
« 添加文档
删除文档 »


删除文档

当某一条数据（文档）已经过期或失效，则必须将它从索引数据库中移除，才能从搜索结果中消失。 在 Xunsearch PHP-SDK 中删除文档有下面两种方式。

1. 按主键删除?

主键是指项目中类型为 id 的字段，删除是调用的是 XSIndex::del 方法，传入参数必须是 要删除的文档的主键值，或一系列主键值组成的数组。

$index->del('123');  // 删除主键值为 123 的记录
$index->del(array('123', '789', '456')); // 同时删除主键值为 123, 789, 456 的记录

2. 按特定字段上的索引词删除?

在项目中，除了主键之外我们通常也会对其它字段进行索引。如果该字段的索引方式（即配置文件中 index 选项的值）为 self 或 both，那么也可以根据该字段上的索引词进行删除。

特别注意是根据索引词删除而不是该字段的值，索引词是指该字段值经过分词器处理后得到的词汇。 对于索引方式为 mixed 的，如需删除，请把字段名设为类型为 body 的字段的名称。

$index->del('abc', 'subject'); // 删除字段 subject 上带有索引词 abc 的所有记录
$index->del(array('abc', 'def'), 'subject'); // 删除字段 subject 上带有索引词 abc 或 def 的所有记录

Note: 如果您删除时指定的主键或字段索引词包含中文字符，则它的编码必须与整个项目的 默认字符集 XS::defaultCharset 一致。

此外删除操作和添加文档一样，也是一个异步行为。

$Id$
« 更新、修改文档
清空索引 »


清空索引

当您的索引数据库发生了重大改变时必须清空索引。比如搜索项目的字段配置修改了， 或者是数据进行了大模迁移、导入或批量删除导致真实数据库和搜索数据库发生了较大的偏差。

Tip: 在我们附带的Indexer 索引管理工具中提供了 --clean 选项用于清空项目的索引数据库，用起来更简洁。

清空数据很简单，直接调用 XSIndex::clean 即可，代码如下：

// 执行清空操作
$index->clean();

// 重新加入文档数据
...
$index->add($doc);
...
$index->add($doc2);
...

Note: 清空索引是一个同步操作，一旦执行立即生效，并且不可恢复。如果采用这种 方式重建索引，由于原有索引被立即清空了，可能会有一小段时间无法搜索到数据。 因此，如果您对线上项目操作，建议参见下一章节中介绍的平滑方式重建索引。

$Id$
« 删除文档
平滑重建索引 »


平滑重建索引

前一章讲到有些情况不得不需要重建索引，可以用 XSIndex::clean 立即全部清空所有数据， 然后再把现有数据全部添加到索引数据库中。

但这种方式的缺点对于线上项目是难以容忍的，因此可以选择使用平滑重建方式。它的内部实现 相当于在一个临时区域开辟新库，把所有的添加操作全部更新到新库，直到您完成重建，完成后 再用新库替代旧库用于搜索。

在编代码时，把所有的 XSIndex:add 操作都放进 XSIndex::beginRebuild 和 XSIndex::endRebuild 之间即可。写法参考如下：

// 宣布开始重建索引
$index->beginRebuild();

// 然后在此开始添加数据
...
$index->add($doc);
...

// 告诉服务器重建完比
$index->endRebuild();

Note: 为确保重建的顺利完成，请在重建时不要对同一个项目开启多个进程、 连接同时交替重建以免发生错乱。

$Id$
« 清空索引
使用索引缓冲区 »


使用索引缓冲区

前面分开讲解了文档的添加、删除、修改操作，它们的调用都是立即传送到服务器的。 如果您进行文档批量处理，如果每一次都进行服务器交互则显得效率低下。

因此，对于大量索引更新操作时，请使用以下方式开启缓冲区进行操作，缓冲区的默认 大小为 4MB，如需修改请传入数字作为 XSIndex::openBuffer 的参数。

$index->openBuffer(); // 开启缓冲区，默认 4MB，如 $index->openBuffer(8) 则表示 8MB

// 在此进行批量的文档添加、修改、删除操作
...
$index->add($doc);
...
$index->del($doc);
...
$index->update($doc);
...

$index->closeBuffer(); // 关闭缓冲区，必须和 openBuffer 成对使用
$Id$
« 平滑重建索引
搜索概述 »


搜索概述

在索引库建立完成后，现在开始学习使用搜索功能，这也是最核心的部分。

1. 如何开始使用搜索？?

在 PHP-SDK 中，搜索功能由类型为 XSSearch 的对象所维护。在 XS 项目中，通过读取 XS::search 属性来获取搜索操作对象，然后展开使用，而不是自行创建对象。后面章节中的 相关测试代码如果没有特别编写，其中的 $search 均为通过类似以下的方式获取的索引对象：

require '$prefix/sdk/php/lib/XS.php';
$xs = new XS('demo'); // 建立 XS 对象，项目名称为：demo
$search = $xs->search; // 获取 搜索对象

Info: 搜索对象内置了字符集智能转换，如果您使用的字符集和项目默认的字符集 XS::defaultCharset 不一致，请调用 XSSearch::setCharset 在开始其它搜索前设置正确的字符集。

2. 典型搜索做法?

一个典型的搜索基本流程是把构建好的搜索语句，通过合适的 API 进行必要的修饰， 再传递给底层的搜索服务器进行处理，然后把匹配的结果返回。具体包括以下步骤：

  • 构建搜索查询语句 query，然后调用 XSSearch::setQuery 设定它
  • 根据需要设置附加的查询条件：通过 XSSearch::addWeight 干扰排名权重，
通过 XSSearch::addRange 添加字段搜索区间或范围，
通过 XSSearch::setFuzzy 开启模糊匹配，以获取更多搜索结果
  • 进行必要的搜索结果限定：通过 XSSearch::setLimit 设置搜索结果数量和偏移， 通过 XSSearch::setSort 设置搜索结果的排序方式，等等
  • 执行搜索，并获取搜索结果，关于搜索结果的处理详见后面的章节

代码如下：

$query = '项目测试'; // 这里的搜索语句很简单，就一个短语

$search->setQuery($query); // 设置搜索语句
$search->addWeight('subject', 'xunsearch'); // 增加附加条件：提升标题中包含 'xunsearch' 的记录的权重
$search->setLimit(5, 10); // 设置返回结果最多为 5 条，并跳过前 10 条

$docs = $search->search(); // 执行搜索，将搜索结果文档保存在 $docs 数组中
$count = $search->count(); // 获取搜索结果的匹配总数估算值

Tip: 除了调用 XSSearch::search 获取搜索结果外，在某些情况我们可能只想知道结果的命中数量， 那么可以直接调用 XSSearch::count 来获取。但要指出的是，这个结果计数只是一个估算值。

3. 关于快捷搜索?

除了上述的典型搜索方式外，我们还提供一种称为快捷搜索的方式。其实就是直接将 query 语句作为参数传递给相应的 API 调用 XSSearch::count 和 XSSSearch::search。由于不经过 setQuery 因此有些其它辅助的功能受到 限制，比如不能进行结果高亮、不能通过 addWeight、addRange 增加辅助搜索条件。

$count = $search->count('项目测试');
$docs = $search->search('项目测试');

4. 搜索中的串接操作?

由于 Xunsearch PHP-SDK 全面采用面向对象的编程思想，在搜索对象中对部分搜索语句构建、 搜索结果修饰加入了串接操作支持。支持串接操作的方法有：

  • addDB($name) - 用于多库搜索，添加数据库名称
  • addRange($field, $from, $to) - 添加搜索过滤区间或范围
  • addWeight($field, $term) - 添加权重索引词
  • setCharset($charset) - 设置字符集
  • setCollapse($field, $num = 1) - 设置搜索结果按字段值折叠
  • setDb($name) - 设置搜索库名称，默认则为 db
  • setFuzzy() - 设置开启模糊搜索, 传入参数 false 可关闭模糊搜索
  • setLimit($limit, $offset = 0) - 设置搜索结果返回的数量和偏移
  • setQuery($query) - 设置搜索语句
  • setSort($field, $asc = false) - 设置搜索结果按字段值排序

如果采用串接操作，那么上面的搜索语句可以改写如下，有种一气呵成的感觉：

$docs = $search->setQuery('项目测试')->addWeight('subject', 'xunsearch')->setLimit(5, 10)->search();

5. 搜索日志?

在每一次正常搜索之后，系统内部均对相应的关键词做了记录和一并分析。但这个行为并不是实时的， 而是积累一定的量后再统一分析和处理。

搜索日志保存在 $prefix/项目名/log_db 中，它是一个独立的索引库，通过它实现了包括相关搜索、 拼音搜索、纠错建议等功能。

Tip: 如果您需要强制同步搜索日志库，请参见 Indexer 索引管理工具 的 --flush-log 选项。

此外，只有当您的搜索代码中调用了 XSSearch::getRelatedQuery 时才会记录本次搜索关键词。

6. 如何进行多库搜索？?

在索引概述中我们曾经提到，如果您的索引数据量非常大，那么应当适当 考虑分割数据，在服务端采用多个库来保存索引数据。您可以调用 XSSearch::addDb 添加 其它搜索库。

关于超大数据量的多库搜索及分布式设计，由于涉及的知识和范围比较广。我们提供了专门的商业支持方案， 在论坛中也会开辟专门的讨论，在此略过不述。

$Id$
« 使用索引缓冲区
构建搜索语句 »


构建搜索语句

搜索查询力争做到简单明了，和目前最流行的搜索引擎做法极为相似，并不需要过多额外的修饰或语法。

您只要把您打算搜索的短语或关键词列出来即可，建议用空格连接多个词；在此基础上还支持字段搜索、 布尔搜索、精确搜索、相邻位置搜索等。

1. 典型搜索语句?

正如上面所讲，仅仅用空格把搜索词、名子连接起来的方式最为常用。内部默认采用 AND 规则， 也就是说要求列出来的关键词（分词之后）全部匹配，如果您通过 XSSearch::setFuzzy 开启了模糊匹配，那么相当于采用 OR 规则，只要其中一个关键词匹配即可。

刚刚说的这种搜索的是索引数据库的“混合区”，即类型为 body 的字段以及索引方式为 mixed 或 both 的字段。如果某个字段的索引方式为 self 或 both，那么还可以指定为字段检索， 也就是要求这个字段匹配指定的关键词，用法为格式为 “字段名:关键词”。

搜索语句举例如下：

$search->search('上海人民公园'); // 检索 body 型字段及混合区
$search->search('上海 人民公园'); // 用空格连接 2 个关键词, 这种情况比上面的用法更明确
$search->search('subject:上海 人民公园'); // 特别要求 subject 字段包含 "上海"
$search->setFuzzy()->search('上海公园'); // 开启模糊搜索，搜索 "上海" 或 "公园"

2. 布尔搜索?

出于高级搜索的需求，我们的搜索语句还全面支持布尔操作，并且像数学运算一样支持括号操作以明确关系。

多个词语之间的“并且”、“或者”、“异或”关系

默认情况，系统采用“并且”关系，即您输入的多个词语（空格分开或系统自动切分以后）必须全部包含。 您也可以通过 AND 将这些关键词显式的强制要求全部出现。相应的，如果您需要只包括其中一个词即可， 那么用 OR 将它们连接起来。更有趣的是，如果有些情况您只想要其中的一个词，并且不要两者同时出现， 那么就用 XOR 连接。参看下面的例子：

Note: 表达布尔关系的关键字必须全部大写，即：XOR, OR, AND

$search->search('杭州 西湖');
$search->search('杭州 AND 西湖'); // 这两者效果是相同的

$search->setFuzzy()->search('杭州 西湖');
$search->search('杭州 OR 西湖');  // 这两者效果相同，都是只要求匹配其中一个

$search->search('杭州 XOR 西湖'); // 表示必须包含其中一个词，并且不允许同时包含这两个词。

要求搜索结果中不含特定关键词

如果您发现搜索结果中，有某一类信息是您不希望看见的，而且这些信息都包含特定的关键词，那么请用减号或 NOT 语法，就可以去除所有这些含有特定关键词的网页。例如，搜索 “神雕侠侣”，希望是关于武侠小说方面 的内容，却发现很多关于电视剧方面的网页。这时希望排除所有包含“电视剧”的搜索结果。

Note: 前一个关键词和减号之间必须有空格分开，否则会被当连字符处理，减号和后一个关键词之间不能有空格。

$search->search('神雕侠侣 -电视剧'); // 正确用法1
$search->search('神雕侠侣 NOT 电视剧'); // 正确用法2

$search->search('神雕侠侣-电视剧'); // 不符合预期目的
$search->search('神雕侠侣 - 电视剧'); // 不符合预期目的

组合关系的括号表达式

就像数学表达式中，括号具有显式的优先级，如果您需要非常复杂的搜索条件，就采用括号表达式来组合上述的条件。

// 要求结果包含 “杭州+西湖” 或 “杭州 + 西溪湿地”，并且不能包含 “汽车 火车”
$search->search('((杭州 AND 西湖) OR (杭州 AND 西溪湿地)) NOT (汽车 火车)');

Tip: 在组合关键中允许嵌入字段搜索的语法。如：subject:上海 OR subject:公园

3. 精确搜索?

在项目字段设计中有一个选项 phrase 如果设为 yes 将在索引数据库中保存关键词的位置信息， 因而我们可以在搜索时利用这些位置信息实现精确搜索。

Info: 项目中类型为 title、body 的字段默认已开启此项，此外这个选项也仅针对使用内置分词器的字段有效。 对于 phrase 设为 no 或用其它分词器的字段来说这项功能无效。

使用双引号进行精确匹配

如果输入的查询词较长，在经过系统词法分析后，给出的搜索结果中的查询词，可能是拆分的。 如果您对这种情况不满意，可以尝试给查询短句加上双引号，搜索结果就会要求词按顺序匹配。

$search->search('上海科技大学');
// 这时内部把搜索语句切割为：上海+科技+大学，要求搜索结果同时包含这三个词即可，
// 但不要求它们的出现顺序，有可能先出现”科技“再出现”上海“，也有可能这三个词离得很远。

$search->search('"上海科技大学"');
// 加上引号后，要求搜索结果必须依次出现”上海+科技+大学“，要显得精准很多。

索引词之间的距离查询

如果您希望输入的多个关键词之间的在匹配结果中的位置距离不要太远，那么可以使用 NEAR 语法来进行搜索，默认要求它们的距离不超过 10。您还可以在 NEAR 后加上 /n 表示 具体的距离要求，n 是整数，这儿的距离指的是分词后的序号距离。

$search->search('杭州 NEAR 西湖'); // 要求结果中出现 "杭州" 和 "西湖"，并且距离不超过 10 个词
$search->search('杭州 NEAR/5 西湖'); // 要求它们距离不超过 5个词

如果您还希望检索结果中词汇出现的顺序与您的输入顺序一致，只要将 NEAR 换成 ADJ 即可， 注意这些语法词和布尔搜索一样，均必须全部大写。

$search->search('杭州 ADJ 西湖'); // 要求结果中先出现 "杭州" 再出现 "西湖"，并且距离不超过 10 个词
$search->search('杭州 ADJ/3 西湖'); // 要求它们距离不超过 3个词

4. 其它搜索语句修饰?

前面还有提到一些附加的搜索语句修饰，这些操作不能直接写进 Query 语句，只能过过相应的 API 来完成。

字段值区间搜索

通过 XSSearch::addRange 我们可以在搜索中，针对某个字段的值增加区间条件过滤。该方法接受三个参数， 第一参数 $field 为字段名称，后面两个参数依次是 $from 起始值，$to 结束值。假设该字段的值为 $value，那么要求 $value > $from && $value <= $to 才符合条件，其中如果有一个设为 null， 则会自动将区间转换为单向比较。

以前面提到的 demo 项目中的 chrono 字段为例，参看下面代码及注释：

$from = strtotime("2011-1-1"); // 起始时间 2011年1月1日
$to = strtotime("2011-9-1"); // 结束时间 2011年9月1日

// 1. <= $to: 要求结果的时间必须在 2011-9-1 之前，并且包含 2011-9-1
$docs = $search->setQuery('杭州')->addRange('chrono', null, $to)->search();

// 2. >= $from: 要求结果的时间必须在 2011-1-1 之后，并且包含 2011-1-1
$docs = $search->setQuery('杭州')->addRange('chrono', $from, null)->search();

// 3. > $from && <= $to: 要求结果的时间必须在 2011-1-1 之后 并且 2011-9-1 之前
$docs = $search->setQuery('杭州')->addRange('chrono', $from, $to)->search();

按字段索引词加权

通过 XSSearch::addWeight 我们可以针对某个字段添加权重索引词。该字段是否包含这个词 都不影响搜索匹配结果，但如果包含会参与计算结果权重，使结果的相关度更高。常用于针对某一类 数据提升搜索结果排序位置。

// 搜索包含 "杭州" 的结果，并且提升 subject 字段包含 "西湖" 的数据的排序
$docs = $search->setQuery('杭州')->addWeight('subject', '西湖')->search();

Note: 这些修饰不能应用到快捷搜索中，也就是说不能直接把 Query 语句传递给 XSSearch::search。 您必须用 XSSearch::setQuery 设置搜索语句，再用不带参数的调用 search 或 count 方法。

区间搜索默认用的是字节序比较，如果您希望将字段值以数字方式比较大小，请将字段的类型设置为 numeric。

5. 查看搜索语句的分析结果?

通过 XSSearch::setQuery 设置了搜索语句后，您可以调用 XSSearch::getQuery 查看您的搜索语句 在内部的分析结果，常用于 DEBUG。此外这两个方法也可以通过魔术属性 XSSearch::query 来访问。

// 设置搜索语句方法1:
$search->setQuery('神雕侠侣 -电视剧');

// 设置搜索语句方法2:
$search->query = '神雕侠侣 -电视剧';

// 查看搜索语句解析结果方法1:
// 不带参数，输出：Xapian::Query((神雕侠侣:(pos=1) AND_NOT 电视剧:(pos=2)))
echo $search->getQuery();

// 带参数，则解析新语句，输出：Xapian::Query((杭州:(pos=1) AND 西湖:(pos=2)))
echo $search->getQuery('杭州 西湖');

// 查看搜索语句解析结果方法2:
// 相当于不带参数的调用 getQuery()
echo $search->query;
$Id$
« 搜索概述
获取搜索匹配结果 »


获取搜索结果

获取搜索结果是进行搜索的最主要目的。通过 XSSearch::search 获得搜索结果文档， 如果没有任何符合条件的匹配则会返回一个空数组。

在获取搜索结果前您必须用上一章学到的知识先构造好相应的搜索语句 $query。

1. 设置分页、数量?

默认情况下，每次返回搜索结果的前 10 条数据，您可以通过 XSSearch::setLimit 指定返回的条数及偏移量， 这样就可以实现分页搜索的效果。特别注意，每一次调用 XSSearch::search 后均会把这些设置恢复到默认值状态。

$search->setLimit(5); // 设置返回结果为前 5 条
$search->setLimit(5, 15); // 设置返回结果为 5 条，但要先跳过 15 条，即第 16～20 条。

2. 设置排序方式?

默认情况，搜索结果根据内部算法计算相关度进行排序，采用著名的 BM25 算法，这在信息检索概率模型中表现 非常优秀，通常只要使用默认的算法即可。

通过 XSSearch::setSort 我们可以指定让搜索结果按照某一个字段的值进行正序或倒序排列，参考代码：

$search->setSort('chrono'); // 按 chrono 字段的值倒序
$search->setSort('chrono', true); // 按 chrono 字段的值正序排列

自从 1.1.0 版本起，通过 XSSearch::setMultiSort 可以实现按照多字段的值排序， 它接受一个数组参数用于表述排序的方式。以字段名称为键，用 true/false 值来表示是否需要正序排列， 默认为 false 表示逆序排列。参见以下代码：

// 表示先以 chrono 正序、再以 pid 逆序(pid 是字符串并不是数值所以 12 会排在 3 之后)
$sorts = array('chrono' => true, 'pid' => false);

// 如果直接把字段名作为数组的值，默认对该字段采用逆序，因此以上用法和下面用法是完全一样的
$sorts = array('chrono', 'pid' => false);

// 设置搜索排序
$search->setMultiSort($sorts);

Note: 按字段值排序是一个低效能的行为，如非必要，强烈建议使用默认排序方式。 排序默认也是按照字节序比较，对于数字型的字段请将字段类型设为 numeric。

为了兼容，您也可以把多字段排序的参数直接传递给 XSSearch::setSort 即可。

3. 读取搜索结果文档?

调用 XSSearch::search 后返回的是搜索结果文档对象组成的数组， 您直接使用这些文档对象的属性即可。

// 以 demo 项目的配置为例
$docs = $search->setQuery('测试')->setLimit(5)->search();
foreach ($docs as $doc)
{
   // 其中常用魔术方法：percent() 表示匹配度百分比, rank() 表示匹配结果序号
   echo $doc->rank() . '. ' . $doc->subject . " [" . $doc->percent() . "%] - ";
   echo date("Y-m-d", $doc->chrono) . "\n" . $doc->message . "\n";
}

4. 搜索结果高亮处理?

根据搜索的习惯，通会希望让搜索结果中匹配关键词的部分进行飘红或加粗等高亮处理， 由于其中涉及到了分词等细节处理比较麻烦。因此，我们统一提供了 XSSearch::highlight 方法，可以对搜索结果文档中的字段值直接进行处理，匹配关键词部分会自动套上 em 标签。

您只要在 CSS 中定义它即可实现自己的高亮代码。

$docs = $search->setQuery('测试')->setLimit(5)->search();
foreach ($docs as $doc)
{
   $subject = $search->highlight($doc->subject); // 高亮处理 subject 字段
   $message = $search->highlight($doc->message); // 高亮处理 message 字段
   echo $doc->rank() . '. ' . $subject . " [" . $doc->percent() . "%] - ";
   echo date("Y-m-d", $doc->chrono) . "\n" . $message . "\n";
}

Note: 这个方法不适合于快捷搜索，也就是说必须使用 setQuery 以及不带参数的 search 才有效。

5. 按字段值折叠搜索结果?

有时我们也反折叠搜索称为归并搜索，就像 Google 上通常搜索结果中对于某一个网站只会显示 2 条最匹配的结果， 其余的归并折叠起来。

在 Xunsearch 中，通过 XSSearch::setCollapse 可以设置按照指定字段的值归并搜索结果，其中第二参数可以 指定归并后返回数量，默认为 1。对于这种情况，请在搜索结果文档中调用 $doc->ccount() 获取展开的全部匹配数。

// 表示搜索结果按 tid 字段的值归并，至多返回 1 条最匹配的数据
$search->setCollapse('tid');

// 然后正常进行搜索后得到的搜索结果文档
$docs = $search->search();

foreach ($docs as $doc)
{
    // 输出 $doc 的有关信息

    // 得出相同 tid 下还有多少条匹配信息
    echo '该主是下还有 ' . ($doc->ccount() - 1) . ' 条匹配结果。';
}
$Id$
« 构建搜索语句
获取搜索匹配数量 »


获取搜索匹配数量

出于性能考虑，搜索结果的匹配数量均被设计为估算值，并非准确值。

1. 索引库内的数据总数?

这个数量是真实准确的，并非估算，获取总量有以下两种做法，至于喜欢用哪种您自己决定。

  • 调用方法 XSSearch::getDbTotal
  • 读取魔术属性 XSSearch::dbTotal，内部也是调用上述方法
$total = $search->dbTotal;
$total = $search->getDbTotal();

2. 最近一次搜索的匹配数?

通过 XSSearch::getLastCount 或 XSSearch::lastCount 可以快速获取到最近一次调用 XSSearch::search 进行搜索时得到的匹配数量。

除非您对搜索结果完全不关心，否则强烈建议用这种方法获取匹配数目以减少系统开销。

// 假设曾经有一次 search 调用
$docs = $search->setQuery('测试')->setLimit(5)->search();

// 必须在 search() 之后调用
$count = $search->lastCount;
$count = $search->getLastCount();

3. 直接获取搜索匹配数?

通过 XSSearch::count 调用可以直接获取搜索的匹配数量，该方法和 XSSearch::search 类似，在调用前可以做一系列的搜索查询语句构建，也可以直接接受查询语句做参数。

返回值是一个整型数字，是估算值。

// 先设置 Query 再获取数量
$count = $search->setQuery('神雕侠侣 -电视剧')->count();

// 直接把 Query 语句传入
$count = $search->count('杭州 西湖');

Tip: 对于不带参数的 count 调用建议放在 search 之后，内部会进行优化，减少一次查询。

$Id$
« 获取搜索匹配结果
获取热门搜索词 »


获取热门搜索词

在您的搜索功能进行一段时间后，系统会记录并分析所有的搜索词汇， 提供热门搜索词列表以辅助网站运营、引导用户搜索。

热门搜索词使用 XSSearch::getHotQuery 方法，返回值是以搜索词为键、 搜索指数为值的关联数组。接受 2 个可选参数如下：

  • $limit 整数值，设置要返回的词数量上限，默认为 6，最大值为 50
  • $type 指定排序类型，默认为 total(总量)，可选值还有：lastnum(上周) 和 currnum(本周)
$words = $search->getHotQuery(); // 获取前 6 个总热门搜索词
$words = $search->getHotQuery(10, 'lastnum'); // 获取前 10 个上周热门词

有关热门搜索的深度挖掘

搜索日志也是一个完整的索引库，可以进行更深入的分析。由于涉及的知识和基础较为复杂， 这块内容这儿暂时略过。

$Id$
« 获取搜索匹配数量
获取相关搜索词 »


获取相关搜索词

相关搜索通常是作为当前搜索词的搜索建议出现在搜索结果的下方，引导用户发现其它 更具体、更符合要求的关键词。

相关搜索词使用 XSSearch::getRelatedQuery 方法，返回值是搜索词组成的数组。 接受 2 个可选参数如下：

  • $query 搜索语句，返回跟这个搜索语句相关的搜索词，默认为 NULL 使用最近那次 setQuery 的语句
  • $limit 整数值，设置要返回的词数量上限，默认为 6，最大值为 20
$search->setQuery('西湖');
// 获取前 6 个和默认搜索语句 "西湖" 相关搜索词
$words = $search->getRelatedQuery();

// 获取 10 个和 "杭州" 相关的搜索词
$words = $search->getRelatedQuery('杭州', 10);

Note: 获取相关搜索内部会重置 XSSearch::query ，建议放在搜索的最后调用。

$Id$
« 获取热门搜索词
搜索建议和纠错 »


搜索建议和纠错

这项功能也是建立在搜索日志的基础上。

1. 拼写纠错?

对于英文，由于打字速度过快或各种原因都很容易造成一两个字母出错。对于中文来说，古代就动不动 出现同音”通假字“，绝大多数现代人也使用拼音输入法，加上方言口音，乱用同音字现象非常普遍。

所以系统在综合分析索引库内的词汇、用户搜索日志基础上建立了一个庞大的纠错体系，支持英文拼写 纠错、中文同音字纠错、拼音转换等。

使用这些功能都非常简单，直接调用 XSSearch::getCorrectedQuery 即可，该方法接受搜索语句 作为参数，如省略参数则直接使用最近一次 setQuery 的语句，返回值是修正词汇组成的数组，若 没有更合适的修正方案则返回空数组。

// 假设在本意是在 demo 项目中搜索 "测试"，但不小心打成了 "侧试"
$search->setQuery('侧试');
$docs = $search->search();

// 由于拼写错误，这种情况返回的数据量可能极少甚至没有，因此调用下面方法试图进行修正
$corrected = $search->getCorrectedQuery();
if (count($corrected) !== 0)
{
   // 有纠错建议，列出来看看；此情况就会得到 "测试" 这一建议
   echo "您是不是要找：\n";
   foreach ($corrected as $word)
   {
      echo $word . "\n";
   }
}

/**
 * 以下拼写示例则简化，并直接传入 Query 语句进行测试
 * 您也可以例句用 `util/Quest.php demo --correct <word>` 进行测试
 */

$search->getCorrectedQuery('cs');  // 通过声母缩写得到建议: "测试"
$search->getCorrectedQuery('ceshi');  // 通过全拼缩写得到建议: "测试"
$search->getCorrectedQuery('yunsearch'); // 通过拼写纠错得到: xunsearch
$search->getCorrectedQuery('xunseach 侧试'); // 混合纠错得到: xunsearch测试

Tip: 建议在搜索结果数量过少或没有时再尝试进行拼写纠错，而不是每次搜索都进行。

2. 搜索建议?

搜索建议是指类似百度那样，当用户在搜索框输入少量的字、拼音、声母时提示用户一些相关的 热门关键词列表下拉框供用户选择。

这样做非常有利于节省用户的打字时间、提升用户体验。

我们通过 XSSearch::getExpandedQuery 来读取展开的搜索词，该方法返回展开的搜索词组成的 数组，如果没有任何可用词则返回空数组。接受 2 个参数：

  • $query 要展开的搜索词，返回结果是以这个搜索词为前缀、拼音前缀展开，此为必要参数
  • $limit 整数值，设置要返回的词数量上限，默认为 10，最大值为 20
/**
 * 以下例子也可以用 `util/Quest.php demo --suggest <word>` 进行测试
 */
$search->getExpandedQuery('x'); // 返回：项目, xunsearch, 行为, 项目测试
$search->getExpandedQuery('xm'); // 返回：项目, 项目测试
$search->getExpandedQuery('项'); // 返回：项目, 项目测试
$search->getExpandedQuery('项目'); // 返回：项目测试

Tip: 实际使用过程中，搜索建议通常单独设计一个入口脚本，再在主搜索界面的搜索框中通过 ajax、AutoComplete 等 JavaScript 技术来根据用户的输入动态载入建议词列表。

$Id$
« 获取相关搜索词
按字段值分面搜索 »


按字段值分面搜索

分面搜索在英文中通常表述为 Facets Search，被检索的数据通具有多维度属性。 例如一本书包含主题、作者、年代等，而分面搜索是指通过事物的这些属性不断筛选、 过滤搜索结果的方法，可以将分面搜索看成搜索和浏览的结合。

如果您还是不清楚它的概念，敬请参阅这篇文章。

1. 适用情况?

在 xunsearch 中，分面搜索是按照字段的值来实现的，为了结合条件做筛选搜索， 支持分面的字段需要进行索引，而它的分词方式最好必须是 full 即整值索引。

2. 用法详细示范?

以论坛文章搜索为例，每篇文章均属于不同版块（字段为 fid）、不同的发表年份 （字段为 year），我们需要在搜索结果中按年份、版块显示匹配的文章数。

那么在构建搜索语句时使用 XSSearch::setFacets 方法来指定需要分面的字段， 该方法接受两个参数，第一参数为要分面的字段名称（多个字段请用数组作参数）， 第二参数是可选的布尔类型，true 表示需要准确统计，默认 false 则为估算。

执行搜索之后再调用 XSSearch::getFacets 方法可取回分面搜索结果。

// 在搜索时加入分面设置
$docs = $search->setQuery('关键词')->setFacets(array('fid', 'year'))->search();

// 读取分面结果
$fid_counts = $search->getFacets('fid'); // 返回数组，以 fid 为键，匹配数量为值
$year_counts = $search->getFacets('year'); // 返回数组，以 year 为键，匹配数量为值

// 遍历 $fid_counts, $year_counts 变量即可得到各自筛选条件下的匹配数量
foreach ($fid_counts as $fid => $count)
{
    echo "其中版块ID为 $fid 的匹配数为: $count\n";
}

// ...
// 通常可以循环生成新的搜索链接，在搜索链接中加入字段搜索条件即可
// ...

// 然后像往常一样使用 $docs 显示文档

Tip: 出于性能考虑，分面搜索返回的匹配数量默认是估算的，比实际数量可能会偏小。 如果您确实需要得到精确数字，请将 XSSearch::setFacets 的第二参数设为 true 。

此外，分面搜索仅针对字段值较短的情况，当字段值的长度超过 255 字节时会被忽略而不参与统计。

每次搜索最多同时支持 8 个字段进行分面搜索。

$Id$
« 搜索建议和纠错
RequiredCheck 运行检测 »


运行条件检测

这里的运行条件是指 Xunsearch PHP-SDK 对于 PHP 及其扩展模块的基础要求。

该工具程序路径为如下，请直接运行或调用 php 运行它。

$prefix/sdk/php/util/RequiredCheck.php

典型运行结果如下，表格式说明各项的情况，最底下还给出最终裁定意见。

Tip: 默认输出的字符集为 UTF-8，如果您的运行结果乱码， 请尝试在运行命令末尾加上 -c gbk 。

Xunsearch PHP-SDK 运行需求检查
==============================

检查内容
--------

本程序用于确认您的服务器配置是否能满足运行 Xunsearch PHP-SDK 的要求。
它将检查服务器所运行的 PHP 版本，查看是否安装了合适的PHP扩展模块，以及
确认 php.ini 文件是否正确设置。

+------------+------------+--------------------------+--------------------------------+
| 项目       | 结果       | 用于                     | 备注                           |
+------------+------------+--------------------------+--------------------------------+
| PHP 版本   | 5.3.6      | XS(core)                 | PHP 5.2.0 或更高版本是必须的。 |
| SPL 扩展   | OK         | XS(core)                 | SPL 扩展用于自动加载和对象戏法 |
| PCRE 扩展  | OK         | XSDocument, XSSearch     | 用于字符串切割、判断           |
| 编码转换   | mbstring   | XSDocument, XSSearch     | 用于支持非 UTF-8 字符集        |
| 缓存模块   | apc        | XS                       | 用于缓存项目配置文件的解析结果 |
| JSON 扩展  | OK         | util.Quest, util.Indexer | 用于读取或输出 JSON 格式的数据 |
| XML 扩展   | OK         | util.Indexer             | 用于读取导入 XML 格式的数据    |
| MySQL 扩展 | mysql      | util.Indexer             | 用于读取导入 MySQL 的数据库    |
| SQLite 扩  | sqlite     | util.Indexer             | 用于读取导入 SQLite 的数据库   |
+------------+------------+--------------------------+--------------------------------+

检查结果
--------

共计 9 项通过，0 项警告，0 项错误。

您的服务器配置符合 Xunsearch/PHP-SDK 的最低要求。
$Id$
« 按字段值分面搜索
Indexer 索引管理器 »


索引管理器

Xunsearch PHP-SDK v1.1.0b 权威指南
  1. 主要参数和选项
  2. 经典用法示例
  3. 导入 SQL 数据库
  4. 导入 CSV 数据
  5. 导入 JSON 数据
  6. 清空、重建索引
  7. 自定义数据源

索引管理器

Indexer 作为索引管理工具，提供了批量索引导入、清空索引、刷新索引队列、日志等各项功能， 导入索引支持数据源包括：csv, json, mysql, sqlite 等，也可以自定义数据源。

运行脚本工具的 --help 选项可查看内置的帮助和说明，如乱码可在选项后加入 -c gbk 试试。

$prefix/sdk/php/util/Indexer.php --help

1. 主要参数和选项?

要使用索引工具，必须先指定的项目名称或配置文件，所有的操作都将是作用于该项目，主要选项如下：

  • -p|--project <name|file> 指定项目名称或配置文件路径，参数名可以省略不写， 如果仅指定项目名称，那么将使用 $prefix/sdk/php/app/.ini 文件。

  • -c|--charset <gbk|utf-8> 指定当前环境、数据源的字符集，默认情况下， 索引工具输出的字符集为 utf-8，并把数据源字符集视为项目的默认字符集。

  • --source <..source..> 用于指定数据源，数据源有以下 2 种情况。
    凡是包含冒号的数据源均视为 SQL 数据源，自动将 --sql 选项的值作为参数传递给数据源；
    其它情况则为文件数据源，自动将 --file 选项的值作为参数传递给数据源对象。

  • --flush、--flush-log、--info 是一些功能选项，与 --source 分开使用。

  • -d|--db <..name..> 指定要更新的索引数据库名称，默认是名为 db 的库。

2. 经典用法示例?

以下是一些经典用法举例：

# 清空 demo 项目的索引数据
util/Indexer.php --clean demo

# 导入 JSON 数据文件 file.json 到 demo 项目
util/Indexer.php --source=json demo file.json

# 导入 MySQL 数据库的 dbname.tbl_post 表到 demo 项目中，并且平滑重建
util/Indexer.php --rebuild --source=mysql://root:pass@localhost/dbname --sql="SELECT * FROM tbl_post" --project=demo

# 查看 demo 项目在服务端的相关信息
util/Indexer.php --info -p demo

# 强制刷新 demo 项目的搜索日志
util/Indexer.php --flush-log --project demo

3. 导入 SQL 数据库?

要导入 SQL 类的数据库，必须使用 --source 指定数据源，视情况用 --sql 选项指定查询语句。 其中数据源的格式如下：

dbtype://[user[:passwd]@]host/dbname[/table]
dbtype://dbpath

dbtype 就是相应的数据源名称，目前支持的有：mysql、sqlite、sqlite3、mysqli、pdo.mysql、pdo.sqlite 。 如果您还需要更多的数据库类型，请参见后面的自定义数据源自行扩充，对于嵌入式的数据库， 仅支持在数据源中设置路径。

数据源仅仅指定了数据库连接的有关参数，您应当通过 --sql 选项指定查询语句， 查询得到的每行数据就会被转换为关联数组，并作为一条完整的文档数据提交到索引库中。 查询语句中允许使用表连接和 LIMIT, OFFSET 等行为，即便数据量很大，内部会作出相应优化。

Info: 如果您在数据源中指定了 table，那么可以省略 --sql 选项，系统自动把该表的数据导入索引库。
相当于指定了这样一条 SQL 语句：SELECT * FROM table

如果您的数据表过于庞大和复杂，强烈建建议您编写 SQL 语句，仅 SELECT 搜索相关的字段即可， 如果字段名称有变动，请用 AS 修改它。

用法示例：

# 导入 mysql 数据源
util/Indexer.php --source=mysql://root@localhost/test --sql="select * from tbl_post"

# 导入 sqlite 数据源
util/Indexer.php --source=sqlite:///tmp/test.db --sql="select * from tbl_post"

4. 导入 CSV 数据?

要导入 CSV 数据库文件，必须使用 --source=csv 来指定数据源，然后使用 --file 指定数据文件的路径，如果没有指定则自动从标准输入读取数据。

对于 CSV 文件，要求必须每行一条数据，字段之间用半角的逗号分开。可以在首行指定字段名称列表， 但要求所有字段均必须是项目中的有效字段；如果没有指定字段列表，则自动按照默认的所有字段顺序读取。 这里所指的行是以 \n (ASCII: 0x0a) 换行符界定的。

Info: 如果您的 CSV 文件字段分割符不是逗号，您可以使用 --csv-delimiter 选项来指定分割符，制表符使用 \t 表示，而如果是 | 这种引起 shell 解析冲突的， 请使用引号将它包起来。

util/Indexer.php --source=csv --csv-delimiter="\t" demo   # 使用 \t
util/Indexer.php --source=csv --csv-delimiter="\\\\" demo   # 使用 \ 分割

5. 导入 JSON 数据?

要导入 JSON 数据库文件，必须使用 --source=json 来指定数据源，然后使用 --file 指定数据文件的路径，如果没有指定则自动从标准输入读取数据。

对于 JSON 文件，要求必须每行一条数据完整的 JSON 数据记录，将自动被转换为文档添加到索引中。 这里所指的行是以 \n (ASCII: 0x0a) 换行符界定的。

6. 清空、重建索引?

在批量导入各种数据源时，您可以加入 --clean 选项，该工具就会先清空现有索引数据库。

Info: 清空数据库可以单独使用，不一定要搭配导入操作。

由于清空立即生效会导致搜索中断或不可用一段时间，对于线上服务，建议使用 --rebuild 选项实现平滑重建，在导入完成后再将新数据库替换为原数据库。

7. 自定义数据源?

目前数据源有两种类型，一种是文件数据源（如 JSON、CSV），另一种是 SQL 数据库源（如 MySQL，SQLite）。

自定义数据源均要求必须是抽象类 XSDataSource 的子类，建议单独编写相应的类定义文件并放入 $prefix/sdk/php/lib 目录，以便脚本在使用时自动加载。

数据源对象中包含两个 protected 属性，可以在初始化时使用它们：

  • XSDataSource::type 对应命令行中 --source 选项的值
  • XSDataSource::arg 则为相应的 --sql 或 --file 选项的值。

文件数据源

对于文件数据源，请直接扩展 XSDataSource，对于名称为 Xyz 的数据源，请命名为 XSXyzDataSource 并重载以下方法：

  • protected XSDataSource::init 开始读取数据时调用，用于初始化数据源的相关资源。
  • protected XSDataSource::deinit 读取数据结束时调用，释放相关资源。
  • protected XSDataSource::getDataList 读取一批数据，可以是一条或若干条，组成数组返回，没有更多数据时返回 false 。
  • public XSDataSource::getCharset 返回精准的数据源字符集，如果不能确定请返回 false 或不重载。

数据库数据源

对于数据库数据源，请直接扩展 XSDatabase，对于名称为 Xyz 的数据源，请命名为 XSDatabaseXyz 并重载以下方法：

  • protected XSDatabase::connect 连接数据库，参数是一个数组，包含连接相关的参数(host,user,pass,dbname,table)、数据库路径(path)
  • protected XSDatabase::close 关闭数据库连接
  • protected XSDatabase::query 执行数据库查询，对于 SELECT 类的操作请将搜索结果以数组方式返回，其它请直接返回 true/false 代表成功/失败
  • protected XSDatabase::setUtf8 尝试将数据库输出字符集强制设为 UTF-8 ，如数据库不支持此功能请返回 false 或不重载。

PDO 数据库数据源

对于 PDO 扩展的数据源，请直接扩展 XSDatabasePDO，对于名称为 Xyz 的数据源，请命名为 XSDatabasePDO_Xyz 并重载以下方法：

  • protected XSDatabasePDO::makeDsn 生成 PDO 连接字符串，参数和 XSDatabase::connect 一样。
  • protected XSDatabase::setUtf8 尝试将数据库输出字符集强制设为 UTF-8 ，如数据库不支持此功能请返回 false 或不重载。

Note: 由于我们使用 parse_url 解析 SQL 数据源连接参数，它并不支持用下划线作 scheme 。 如果要直接使用 PDO 数据源，请把 dbtype 设为 pdo.xxx 而不是 pdo_xxx 。

$Id$
« RequiredCheck 运行检测
Quest 搜索测试工具 »


搜索测试工具

Quest 是搜索测试工具，提供了搜索相关的各项功能，非常适合用于前期测试比较和纠错。 运行脚本工具的 --help 选项可查看内置的帮助和说明，如乱码可在选项后加入 -c gbk 试试。

$prefix/sdk/php/util/Quest.php --help

1. 主要参数和选项?

和索引工具类似，使用前必须先指定的项目名称或配置文件，所有的操作都将是作用于该项目， 主要选项如下：

  • -p|--project <name|file> 指定项目名称或配置文件路径，参数名可以省略不写， 如果仅指定项目名称，那么将使用 $prefix/sdk/php/app/.ini 文件。

  • -c|--charset <gbk|utf-8> 指定当前环境、查询语句的字符集，默认情况下， 搜索工具输出的字符集为 utf-8，并把搜索语句字符集视为项目的默认字符集。

  • -q|--query 指定要搜索的查询语句，如果语句中包含空格请用使用双引号包围起来。

  • --hot[=total|last|cur] 用于显示指定项目的热门搜索词，此时 query 参数无意义。 选项值含义分别表示总搜索量、上周搜索量、本周搜索量，默认为总搜索量。

  • --suggest、--correct、--related 是搭配 query 的一些功能选项。

  • --limit= 用于设置各种检索功能的返回数量最大上限，默认值均为 10 个。 对于普通搜索模式，还支持用 --limit=offset,num 来同时指定偏移量和上限。

  • -d|--db <name[,name2 ...]> 指定要搜索的数据库名称，默认是名为 db 的库， 多个库之间用逗号分隔。

2. 经典用法示例?

以下是一些经典用法举例：

# 搜索 demo 中包含 "测试" 的数据
util/Quest.php demo 测试

# 搜索 demo 中包含 "测试" 的数据，并限制最多返回 2 条结果
util/Quest.php -p demo -q 测试 --limit=2

# 列出 demo 项目中以 "x" 开头的搜索建议
util/Quest.php --suggest demo x

# 在 demo 项目中修正搜索词 "xm"
util/Quest.php --correct demo xm

# 在 demo 中列出和 "测试" 相关的搜索词
util/Quest.php --related demo 测试

# 查看 demo 项目上周的 5 条最热门搜索词
util/Quest.php --hot=last --limit=5 demo
$Id$
« Indexer 索引管理器
SearchSkel 生成搜索代码 »


生成搜索骨架代码

前面已经把 Xunsearch PHP-SDK 相关的基础文档全面讲了一通，您完全可以根据文档和 自动生成的 API 文档进行搜索开发了。

但根据我们研发 Xungle 项目 的实践经验，绝大多数搜索项目的前端代码都类似， 不同的均为细节显示、字段使用等。因此为了更快的加速用户开发速度，我们特地推出这个代码生成工具。

这个工具读取并分析项目配置文件(*.ini)，生成通用的搜索骨架代码，即使您不做任何修改，也能使用它并看到 基础的搜索效果、界面。强烈建议所有的搜索项目都在生成的骨架代码上进行二次开发，节省大量宝贵时间。

运行脚本工具的 --help 选项可查看内置的帮助和说明，输出文字编码强制为 UTF-8。

$prefix/sdk/php/util/SearchSkel.php --help

1. 主要参数和选项?

要使用本工具，必须先指定的项目名称或配置文件，它将根据该项目的情况生成代码，主要选项如下：

  • -p|--project <name|file> 指定项目名称或配置文件路径，参数名可以省略不写， 如果仅指定项目名称，那么将使用 $prefix/sdk/php/app/.ini 文件。

  • -o|--output <..dir..> 指定生成的代码目录的存放位置，默认放在当前目录中， 生成的代码本身包含一层以项目名称命名的目录。建议直接将目录指定到 web 可访问目录。

2. 经典用法示例?

# 在当前目录下生成 demo 项目的搜索代码
util/SearchSkel.php demo

# 在指定的 /path/to/web 目录生成 demo 搜索代码，代码目录为：/path/to/web/demo
util/SearchSkel.php demo /path/to/web

# 使用选项指定方式
util/SearchSkel.php -p demo -o /path/to/web

3. 生成的代码介绍?

工具运行完毕后，立即生成以下三个文件于目录中，如果文件已经存在，则会把旧有文件保存为 .bak 文件用于备份。

[demo]
  |- search.php     # 搜索功能入口
  |- search.tpl     # 搜索结果输出模板文件
  \- suggest.php    # 提取搜索输入框下拉建议，通过 autocomplete 组件自动调用

您可以直接通过浏览器访问：search.php 试用搜索。

Note: 生成的代码中关于搜索建议的部分，采用 jQuery-UI 的 autocomplete 并且从 Google 提供的代码库 直接加载，如果您的不能连网将可能无法看到部分效果。您可以将相关的 js/css 下载到本地并替换模板代码。

$Id$
« Quest 搜索测试工具
IniWizzard 配置文件向导 »


INI 配置文件工具

在 Xunsearch 中项目的 INI 配置文件至关重要，内容本身明文可以直接查看、 可以直接修改。但其中还是有一些约定的规则和限制，为了更方便用户，我们打算提供 这样的工具辅助用户创建、修改配置文件。

INI 在线编辑工具 (WEB 版)

为方便新手初次接触 xunsearch 时的困惑，我们特别制作了 INI 在线编辑工具， 使用非常简单，类似 PHPMyAdmin 的界面，可以在 INI 文件和表单之间来回切换。

访问网址：http://www.xunsearch.com/tools/iniconfig

Note: 在线编辑器暂时不支持指定自定义分词器和分词器参数，这个部分需要用户自行手动修改。

$Id$
« SearchSkel 生成搜索代码


Xunsearch PHP-SDK API 文档

Xunsearch PHP-SDK v1.1.0b API 参考文档

API 参考文档

包 | 类对象 | 描述
XS | XS | XS 搜索项目主类
 | XSCommand | XSCommand 命令对象
 | XSComponent | XS 组件基类
 | XSDocument | 文档用于描述检索/索引的基础对象, 包含一组字段及其值, 相当于常规SQL数据表中的一行记录.
 | XSErrorException | XS 错误异常类定义, XS 所有操作过程发生错误均抛出该实例
 | XSException | XS 异常类定义, XS 所有操作过程发生异常均抛出该实例
 | XSFieldMeta | 数据字段结构元数据
 | XSFieldScheme | XS 数据字段方案
 | XSIndex | XS 索引管理
 | XSSearch | XS 搜索类, 执行搜索功能
 | XSServer | XSServer 服务器操作对象
XS.tokenizer | XSTokenizer | 自定义字段词法分析器接口
 | XSTokenizerFull | 内置整值分词器
 | XSTokenizerNone | 内置空分词器
 | XSTokenizerSplit | 内置的分割分词器
 | XSTokenizerXlen | 内置的定长分词器
 | XSTokenizerXstep | 内置的步长分词器
XS.util | XSCsvDataSource | CSV 数据源
 | XSDataSource | 索引数据源抽象基类
 | XSDatabaseDataSource | SQL 数据库源
 | XSJsonDataSource | JSON 数据源
 | XSUtil | XSUtil 工具程序通用代码
XS.util.db | XSDatabase | 数据库操作基类
 | XSDatabaseMySQL | 使用传统 MySQL 扩展
 | XSDatabaseMySQLI | 面向对象的 MySQLI 扩展
 | XSDatabasePDO | 面向对象的 PDO 扩展基类
 | XSDatabasePDO_MySQL | PDO.MySQL 实现
 | XSDatabasePDO_SQLite | PDO.SQLite 实现
 | XSDatabaseSQLite | 使用传统的 SQLite 扩展
 | XSDatabaseSQLite3 | 面向对象的 SQLite3 扩展


XS

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XS » XSComponent
版本 | 1.0.0
源代码 | sdk/php/lib/XS.class.php
XS 搜索项目主类

Public 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
allFields | XSFieldMeta[] | 获取项目所有字段结构设置 | XS
defaultCharset | string | 获取项目的默认字符集 | XS
fieldBody | XSFieldMeta | 获取当前内容字段 | XS
fieldId | XSFieldMeta | 获取当前主键字段 | XS
fieldTitle | XSFieldMeta | 获取当前标题字段 | XS
index | XSIndex | 获取索引操作对象 | XS
name | string | 获取当前项目名称 | XS
scheme | XSFieldScheme | 获取当前在用的字段方案 | XS
search | XSSearch | 获取搜索操作对象 | XS

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XS
__destruct() | 析构函数 | XS
__get() | 魔术方法 __get | XSComponent
__isset() | 魔术方法 __isset | XSComponent
__set() | 魔术方法 __set | XSComponent
__unset() | 魔术方法 __unset | XSComponent
autoload() | 智能加载类库文件 | XS
convert() | 字符集转换 | XS
getAllFields() | 获取项目所有字段结构设置 | XS
getDefaultCharset() | 获取项目的默认字符集 | XS
getField() | 获取项目字段元数据 | XS
getFieldBody() | 获取当前内容字段 | XS
getFieldId() | 获取当前主键字段 | XS
getFieldTitle() | 获取当前标题字段 | XS
getIndex() | 获取索引操作对象 | XS
getName() | 获取当前项目名称 | XS
getScheme() | 获取当前在用的字段方案 | XS
getSearch() | 获取搜索操作对象 | XS
restoreScheme() | 还原字段方案为项目绑定方案 | XS
setDefaultCharset() | 改变项目的默认字符集 | XS
setName() | 修改当前项目名称 | XS
setScheme() | 设置当前在用的字段方案 | XS

属性明细

allFields 属性 只读
public XSFieldMeta[] getAllFields()

获取项目所有字段结构设置

defaultCharset 属性
public string getDefaultCharset()
public void setDefaultCharset(string $charset)

获取项目的默认字符集

fieldBody 属性 只读
public XSFieldMeta getFieldBody()

获取当前内容字段

参见

  • XSFieldScheme::getFieldBody
fieldId 属性 只读
public XSFieldMeta getFieldId()

获取当前主键字段

参见

  • XSFieldScheme::getFieldId
fieldTitle 属性 只读
public XSFieldMeta getFieldTitle()

获取当前标题字段

参见

  • XSFieldScheme::getFieldTitle
index 属性 只读
public XSIndex getIndex()

获取索引操作对象

name 属性
public string getName()
public void setName(string $name)

获取当前项目名称

scheme 属性
public XSFieldScheme getScheme()
public void setScheme(XSFieldScheme $fs)

获取当前在用的字段方案 通用于搜索结果文档和修改、添加的索引文档

search 属性 只读
public XSSearch getSearch()

获取搜索操作对象

方法明细

__construct() 方法
public void __construct(string $file)
$file string 要加载的项目配置文件
源码: sdk/php/lib/XS.class.php#L272 (显示)
public function __construct($file)
{
    if (strlen($file) < 255 && !is_file($file))
    {
        $file2 = XS_LIB_ROOT . '/../app/' . $file . '.ini';
        if (is_file($file2))
            $file = $file2;
    }
    $this->loadIniFile($file);
}

构造函数 特别说明一个小技巧, 参数 $file 可以直接是配置文件的内容, 还可以是仅仅是文件名, 如果只是文件名会自动查找 XS_LIB_ROOT/../app/$file.ini

__destruct() 方法
public void __destruct()
源码: sdk/php/lib/XS.class.php#L287 (显示)
public function __destruct()
{
    $this->_index = null;
    $this->_search = null;
}

析构函数 由于对象交叉引用, 如需提前销毁对象, 请强制调用该函数

autoload() 方法
public static void autoload(string $name)
$name string 类的名称
源码: sdk/php/lib/XS.class.php#L453 (显示)
public static function autoload($name)
{
    $file = XS_LIB_ROOT . '/' . $name . '.class.php';
    if (file_exists($file))
        require_once $file;
}

智能加载类库文件 要求以 Name.class.php 命名并与本文件存放在同一目录, 如: XSTokenizerXxx.class.php

convert() 方法
public static mixed convert(mixed $data, string $to, string $from)
$data | mixed | 需要转换的数据, 支持 string 和 array, 数组会自动递归转换
$to | string | 转换后的字符集
$from | string | 转换前的字符集
{return} | mixed | 转换后的数据
源码: sdk/php/lib/XS.class.php#L469 (显示)
public static function convert($data, $to, $from)
{
    // need not convert
    if ($to == $from)
        return $data;
    // array traverse
    if (is_array($data))
    {
        foreach ($data as $key => $value)
        {
            $data[$key] = self::convert($value, $to, $from);
        }
        return $data;
    }
    // string contain 8bit characters
    if (is_string($data) && preg_match('/[\x81-\xfe]/', $data))
    {
        // mbstring, iconv, throw ...
        if (function_exists('mb_convert_encoding'))
            return mb_convert_encoding($data, $to, $from);
        else if (function_exists('iconv'))
            return iconv($from, $to . '//TRANSLIT', $data);
        else
            throw new XSException('Cann\'t find the mbstring or iconv extension to convert encoding');
    }
    return $data;
}

字符集转换 要求安装有 mbstring, iconv 中的一种

getAllFields() 方法
public XSFieldMeta[] getAllFields()
{return} XSFieldMeta[]
源码: sdk/php/lib/XS.class.php#L443 (显示)
public function getAllFields()
{
    return $this->_scheme->getAllFields();
}

获取项目所有字段结构设置

getDefaultCharset() 方法
public string getDefaultCharset()
{return} string 默认字符集(已大写)
源码: sdk/php/lib/XS.class.php#L352 (显示)
public function getDefaultCharset()
{
    return isset($this->_config['project.default_charset']) ?
        strtoupper($this->_config['project.default_charset']) : 'UTF-8';
}

获取项目的默认字符集

getField() 方法
public XSFieldMeta getField(mixed $name, bool $throw=true)
$name | mixed | 字段名称(string) 或字段序号(vno, int)
$throw | bool | 当字段不存在时是否抛出异常, 默认为 true
{return} | XSFieldMeta | 字段元数据对象
源码: sdk/php/lib/XS.class.php#L434 (显示)
public function getField($name, $throw = true)
{
    return $this->_scheme->getField($name, $throw);
}

获取项目字段元数据

参见

  • XSFieldScheme::getField
getFieldBody() 方法
public XSFieldMeta getFieldBody()
{return} XSFieldMeta 类型为 BODY 的字段
源码: sdk/php/lib/XS.class.php#L421 (显示)
public function getFieldBody()
{
    return $this->_scheme->getFieldBody();
}

获取当前内容字段

参见

  • XSFieldScheme::getFieldBody
getFieldId() 方法
public XSFieldMeta getFieldId()
{return} XSFieldMeta 类型为 ID 的字段
源码: sdk/php/lib/XS.class.php#L401 (显示)
public function getFieldId()
{
    return $this->_scheme->getFieldId();
}

获取当前主键字段

参见

  • XSFieldScheme::getFieldId
getFieldTitle() 方法
public XSFieldMeta getFieldTitle()
{return} XSFieldMeta 类型为 TITLE 的字段
源码: sdk/php/lib/XS.class.php#L411 (显示)
public function getFieldTitle()
{
    return $this->_scheme->getFieldTitle();
}

获取当前标题字段

参见

  • XSFieldScheme::getFieldTitle
getIndex() 方法
public XSIndex getIndex()
{return} XSIndex 索引操作对象
源码: sdk/php/lib/XS.class.php#L371 (显示)
public function getIndex()
{
    if ($this->_index === null)
    {
        $conn = isset($this->_config['server.index']) ? $this->_config['server.index'] : 8383;
        $this->_index = new XSIndex($conn, $this);
    }
    return $this->_index;
}

获取索引操作对象

getName() 方法
public string getName()
{return} string 当前项目名称
源码: sdk/php/lib/XS.class.php#L333 (显示)
public function getName()
{
    return $this->_config['project.name'];
}

获取当前项目名称

getScheme() 方法
public XSFieldScheme getScheme()
{return} XSFieldScheme 当前字段方案
源码: sdk/php/lib/XS.class.php#L298 (显示)
public function getScheme()
{
    return $this->_scheme;
}

获取当前在用的字段方案 通用于搜索结果文档和修改、添加的索引文档

getSearch() 方法
public XSSearch getSearch()
{return} XSSearch 搜索操作对象
源码: sdk/php/lib/XS.class.php#L385 (显示)
public function getSearch()
{
    if ($this->_search === null)
    {
        $conn = isset($this->_config['server.search']) ? $this->_config['server.search'] : 8384;
        $this->_search = new XSSearch($conn, $this);
        $this->_search->setCharset($this->getDefaultCharset());
    }
    return $this->_search;
}

获取搜索操作对象

restoreScheme() 方法
public void restoreScheme()
源码: sdk/php/lib/XS.class.php#L319 (显示)
public function restoreScheme()
{
    if ($this->_scheme !== $this->_bindScheme)
    {
        $this->_scheme = $this->_bindScheme;
        if ($this->_search !== null)
            $this->_search->markResetScheme(true);
    }
}

还原字段方案为项目绑定方案

setDefaultCharset() 方法
public void setDefaultCharset(string $charset)
$charset string 修改后的字符集
源码: sdk/php/lib/XS.class.php#L362 (显示)
public function setDefaultCharset($charset)
{
    $this->_config['project.default_charset'] = strtoupper($charset);
}

改变项目的默认字符集

setName() 方法
public void setName(string $name)
$name string 项目名称
源码: sdk/php/lib/XS.class.php#L343 (显示)
public function setName($name)
{
    $this->_config['project.name'] = $name;
}

修改当前项目名称 注意，必须在 getSearch 和 getIndex 前调用才能起作用

setScheme() 方法
public void setScheme(XSFieldScheme $fs)
$fs XSFieldScheme 一个有效的字段方案对象
源码: sdk/php/lib/XS.class.php#L308 (显示)
public function setScheme(XSFieldScheme $fs)
{
    $fs->checkValid(true);
    $this->_scheme = $fs;
    if ($this->_search !== null)
        $this->_search->markResetScheme();
}

设置当前在用的字段方案


XSCommand

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSCommand » XSComponent
版本 | 1.0.0
源代码 | sdk/php/lib/XSServer.class.php
XSCommand 命令对象 是与服务端交互的最基本单位, 命令对象可自动转换为通讯字符串, 命令结构参见 C 代码中的 struct xs_cmd 定义, 头部长度为 8字节.

Public 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
arg | int | 获取属性 arg 的值 | XSCommand
arg1 | int | 参数1 取值范围 0~255, 具体含义根据不同的 CMD 而变化 | XSCommand
arg2 | int | 参数2 取值范围 0~255, 常用于存储 value no, 具体参照不同 CMD 而确定 | XSCommand
buf | string | 主数据内容, 最长 2GB | XSCommand
buf1 | string | 辅数据内容, 最长 255字节 | XSCommand
cmd | int | 命令代码 通常是预定义常量 CMD_xxx, 取值范围 0~255 | XSCommand

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSCommand
__get() | 魔术方法 __get | XSComponent
__isset() | 魔术方法 __isset | XSComponent
__set() | 魔术方法 __set | XSComponent
__toString() | 转换为封包字符串 | XSCommand
__unset() | 魔术方法 __unset | XSComponent
getArg() | 获取属性 arg 的值 | XSCommand
setArg() | 设置属性 arg 的值 | XSCommand

属性明细

arg 属性
public int getArg()
public void setArg(int $arg)

获取属性 arg 的值

arg1 属性
public int $arg1;

参数1 取值范围 0~255, 具体含义根据不同的 CMD 而变化

arg2 属性
public int $arg2;

参数2 取值范围 0~255, 常用于存储 value no, 具体参照不同 CMD 而确定

buf 属性
public string $buf;

主数据内容, 最长 2GB

buf1 属性
public string $buf1;

辅数据内容, 最长 255字节

cmd 属性
public int $cmd;

命令代码 通常是预定义常量 CMD_xxx, 取值范围 0~255

方法明细

__construct() 方法
public void __construct(mixed $cmd, int $arg1=0, int $arg2=0, string $buf='', string $buf1='')
$cmd | mixed | 命令类型或命令数组 当类型为 int 表示命令代码, 范围是 1~255, 参见 xs_cmd.inc.php 里的定义 当类型为 array 时忽略其它参数, 可包含 cmd, arg1, arg2, buf, buf1 这些键值
$arg1 | int | 参数1, 其值为 0~255, 具体含义视不同 CMD 而确定
$arg2 | int | 参数2, 其值为 0~255, 具体含义视不同 CMD 而确定, 常用于存储 value no
$buf | string | 字符串内容, 最大长度为 2GB
$buf1 | string | 字符串内容1, 最大长度为 255字节
源码: sdk/php/lib/XSServer.class.php#L58 (显示)
public function __construct($cmd, $arg1 = 0, $arg2 = 0, $buf = '', $buf1 = '')
{
    if (is_array($cmd))
    {
        foreach ($cmd as $key => $value)
        {
            if ($key === 'arg' || property_exists($this, $key))
                $this->$key = $value;
        }
    }
    else
    {
        $this->cmd = $cmd;
        $this->arg1 = $arg1;
        $this->arg2 = $arg2;
        $this->buf = $buf;
        $this->buf1 = $buf1;
    }
}

构造函数

__toString() 方法
public string __toString()
{return} string 用于服务端交互的字符串
源码: sdk/php/lib/XSServer.class.php#L82 (显示)
public function __toString()
{
    if (strlen($this->buf1) > 0xff)
        $this->buf1 = substr($this->buf1, 0, 0xff);
    return pack('CCCCI', $this->cmd, $this->arg1, $this->arg2, strlen($this->buf1), strlen($this->buf)) . $this->buf . $this->buf1;
}

转换为封包字符串

getArg() 方法
public int getArg()
{return} int 参数值
源码: sdk/php/lib/XSServer.class.php#L93 (显示)
public function getArg()
{
    return $this->arg2 | ($this->arg1 << 8);
}

获取属性 arg 的值

setArg() 方法
public void setArg(int $arg)
$arg int 参数值
源码: sdk/php/lib/XSServer.class.php#L102 (显示)
public function setArg($arg)
{
    $this->arg1 = ($arg >> 8) & 0xff;
    $this->arg2 = $arg & 0xff;
}

设置属性 arg 的值


XSComponent

All Packages | 方法(函数)
包 | XS
继承关系 | class XSComponent
子类 | XS, XSCommand, XSServer
版本 | 1.0.0
源代码 | sdk/php/lib/XS.class.php
XS 组件基类 封装一些魔术方法, 以实现支持模拟属性

模拟属性通过定义读取函数, 写入函数来实现, 允许两者缺少其中一个 这类属性可以跟正常定义的属性一样存取, 但是这类属性名称不区分大小写. 例:
$a = $obj->text; // $a 值等于 $obj->getText() 的返回值
$obj->text = $a; // 等同事调用 $obj->setText($a)

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__get() | 魔术方法 __get | XSComponent
__isset() | 魔术方法 __isset | XSComponent
__set() | 魔术方法 __set | XSComponent
__unset() | 魔术方法 __unset | XSComponent

方法明细

__get() 方法
public mixed __get(string $name)
$name | string | 属性名称
{return} | mixed | 属性值
源码: sdk/php/lib/XS.class.php#L184 (显示)
public function __get($name)
{
    $getter = 'get' . $name;
    if (method_exists($this, $getter))
        return $this->$getter();

    // throw exception
    $msg = method_exists($this, 'set' . $name) ? 'Write-only' : 'Undefined';
    $msg .= ' property: ' . get_class($this) . '::$' . $name;
    throw new XSException($msg);
}

魔术方法 __get 取得模拟属性的值, 内部实际调用 getXxx 方法的返回值

__isset() 方法
public bool __isset(string $name)
$name | string | 属性名称
{return} | bool | 若存在为 true, 反之为 false
源码: sdk/php/lib/XS.class.php#L221 (显示)
public function __isset($name)
{
    return method_exists($this, 'get' . $name);
}

魔术方法 __isset 判断模拟属性是否存在并可读取

__set() 方法
public void __set(string $name, mixed $value)
$name | string | 属性名称
$value | mixed | 属性值
源码: sdk/php/lib/XS.class.php#L203 (显示)
public function __set($name, $value)
{
    $setter = 'set' . $name;
    if (method_exists($this, $setter))
        return $this->$setter($value);

    // throw exception
    $msg = method_exists($this, 'get' . $name) ? 'Read-only' : 'Undefined';
    $msg .= ' property: ' . get_class($this) . '::$' . $name;
    throw new XSException($msg);
}

魔术方法 __set 设置模拟属性的值, 内部实际是调用 setXxx 方法

__unset() 方法
public void __unset(string $name)
$name string 属性名称
源码: sdk/php/lib/XS.class.php#L231 (显示)
public function __unset($name)
{
    $this->__set($name, null);
}

魔术方法 __unset 删除、取消模拟属性, 相当于设置属性值为 null


XSDocument

All Packages | 方法(函数)
包 | XS
继承关系 | class XSDocument
实现接口 | ArrayAccess, IteratorAggregate, Traversable
版本 | 1.0.0
源代码 | sdk/php/lib/XSDocument.class.php
文档用于描述检索/索引的基础对象, 包含一组字段及其值, 相当于常规SQL数据表中的一行记录. 通过魔术方法, 每个字段名都是文档的虚拟属性, 可直接赋值或取值, 也支持数组方式访问文档字段.
$doc = new XSDocument;
$doc->name = 'value'; // 用对象属性方式进行赋值、取值
$doc['name'] = 'value'; // 用数组下标方式进行赋值、取值
$value = $doc->f('name'); // 用函数方式进行取值
$doc->setField('name', 'value'); // 用函数方式进行赋值
$doc->setFields(array('name' => 'value', 'name2' => 'value2')); // 用数组进行批量赋值

// 迭代方式取所有字段值
foreach($doc as $name => $value)
{
    echo "$name: $value\n";
}
如果有特殊需求, 可以自行扩展本类, 重写 beforeSubmit() 及 afterSubmit() 方法以定义在索引 提交前后的行为

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__call() | 魔术方法 __call | XSDocument
__construct() | 构造函数 | XSDocument
__get() | 魔术方法 __get | XSDocument
__set() | 魔术方法 __set | XSDocument
addIndex() | 给字段增加索引文本 (仅限索引文档) | XSDocument
addTerm() | 给字段增加索引词 (仅限索引文档) | XSDocument
afterSubmit() | 重写接口, 在文档成功提交到索引服务器后调用 | XSDocument
beforeSubmit() | 重写接口, 在文档提交到索引服务器前调用 | XSDocument
f() | 获取文档字段的值 | XSDocument
getAddIndex() | 获取字段的附加索引文本 (仅限索引文档) | XSDocument
getAddTerms() | 获取字段的附加索引词列表 (仅限索引文档) | XSDocument
getCharset() | 获取文档字符集 | XSDocument
getIterator() | IteratorAggregate 接口, 以支持 foreach 遍历访问字段列表 | XSDocument
offsetExists() | ArrayAccess 接口, 判断字段是否存在, 勿直接调用 | XSDocument
offsetGet() | ArrayAccess 接口, 取得字段值, 勿直接调用 | XSDocument
offsetSet() | ArrayAccess 接口, 设置字段值, 勿直接调用 | XSDocument
offsetUnset() | ArrayAccess 接口, 删除字段值, 勿直接调用 | XSDocument
setCharset() | 设置文档字符集 | XSDocument
setField() | 设置某个字段的值 | XSDocument
setFields() | 批量设置字段值 | XSDocument

方法明细

__call() 方法
public void __call(string $name, array $args)
$name | string | 方法名称
$args | array | 调用时的参数列表 (此处无用)
源码: sdk/php/lib/XSDocument.class.php#L106 (显示)
public function __call($name, $args)
{
    if ($this->_meta !== null)
    {
        $name = strtolower($name);
        if (isset($this->_meta[$name]))
            return $this->_meta[$name];
    }
    throw new XSException('Call to undefined method `' . get_class($this) . '::' . $name . '()\'');
}

魔术方法 __call 实现以函数调用访问搜索结果元数据, 支持: docid, rank, percent, weight, ccount

__construct() 方法
public void __construct(mixed $p=NULL, string $d=NULL)
$p | mixed | 字符串表示索引文档的编码或搜索结果文档的 meta 数据, 数组则表示或索引文档的初始字段数据
$d | string | 可选参数, 当 $p 不为编码时, 本参数表示数据编码
源码: sdk/php/lib/XSDocument.class.php#L55 (显示)
public function __construct($p = null, $d = null)
{
    $this->_data = array();
    if (is_array($p))
        $this->_data = $p;
    else if (is_string($p))
    {
        if (strlen($p) !== self::$_resSize)
        {
            $this->setCharset($p);
            return;
        }
        $this->_meta = unpack(self::$_resFormat, $p);
    }
    if ($d !== null && is_string($d))
        $this->setCharset($d);
}

构造函数

__get() 方法
public mixed __get(string $name)
$name | string | 字段名称
{return} | mixed | 字段值, 若不存在返回 null
源码: sdk/php/lib/XSDocument.class.php#L79 (显示)
public function __get($name)
{
    if (!isset($this->_data[$name]))
        return null;
    return $this->autoConvert($this->_data[$name]);
}

魔术方法 __get 实现以对象属性方式获取文档字段值

__set() 方法
public void __set(string $name, mixed $value)
$name | string | 字段名称
$value | mixed | 字段值
源码: sdk/php/lib/XSDocument.class.php#L92 (显示)
public function __set($name, $value)
{
    if ($this->_meta !== null)
        throw new XSException('Magick property of result document is read-only');
    $this->setField($name, $value);
}

魔术方法 __set 实现以对象属性方式设置文档字段值

addIndex() 方法
public void addIndex(string $field, string $text)
$field | string | 文本所属的字段名称
$text | string | 文本内容
源码: sdk/php/lib/XSDocument.class.php#L229 (显示)
public function addIndex($field, $text)
{
    $field = strval($field);
    if (!is_array($this->_texts))
        $this->_texts = array();
    if (!isset($this->_texts[$field]))
        $this->_texts[$field] = strval($text);
    else
        $this->_texts[$field] .= "\n" . strval($text);
}

给字段增加索引文本 (仅限索引文档)

addTerm() 方法
public void addTerm(string $field, string $term, int $weight=1)
$field | string | 词条所属字段名称
$term | string | 词条内容, 不超过 255字节
$weight | int | 词重, 默认为 1
源码: sdk/php/lib/XSDocument.class.php#L211 (显示)
public function addTerm($field, $term, $weight = 1)
{
    $field = strval($field);
    if (!is_array($this->_terms))
        $this->_terms = array();
    if (!isset($this->_terms[$field]))
        $this->_terms[$field] = array($term => $weight);
    else if (!isset($this->_terms[$field][$term]))
        $this->_terms[$field][$term] = $weight;
    else
        $this->_terms[$field][$term] += $weight;
}

给字段增加索引词 (仅限索引文档)

afterSubmit() 方法
public void afterSubmit(XSIndex $index)
$index XSIndex 索引操作对象
源码: sdk/php/lib/XSDocument.class.php#L314 (显示)
public function afterSubmit($index)
{

}

重写接口, 在文档成功提交到索引服务器后调用 继承此类进行重写该方法时, 强烈建议要调用 parent::afterSave($index) 以确保完整.

beforeSubmit() 方法
public bool beforeSubmit(XSIndex $index)
$index | XSIndex | 索引操作对象
{return} | bool | 默认返回 true, 若返回 false 将阻止该文档提交到索引服务器
源码: sdk/php/lib/XSDocument.class.php#L302 (显示)
public function beforeSubmit(XSIndex $index)
{
    if ($this->_charset === null)
        $this->_charset = $index->xs->getDefaultCharset();
    return true;
}

重写接口, 在文档提交到索引服务器前调用 继承此类进行重写该方法时, 必须调用 parent::beforeSave($index) 以确保正确

f() 方法
public mixed f(string $name)
$name | string | 字段名称
{return} | mixed | 字段值, 若不存在则返回 null
源码: sdk/php/lib/XSDocument.class.php#L168 (显示)
public function f($name)
{
    return $this->__get(strval($name));
}

获取文档字段的值

getAddIndex() 方法
public string getAddIndex(string $field)
$field | string | 字段名称
{return} | string | 文本内容, 若无则返回 null
源码: sdk/php/lib/XSDocument.class.php#L197 (显示)
public function getAddIndex($field)
{
    $field = strval($field);
    if ($this->_texts === null || !isset($this->_texts[$field]))
        return null;
    return $this->autoConvert($this->_texts[$field]);
}

获取字段的附加索引文本 (仅限索引文档)

getAddTerms() 方法
public array getAddTerms(string $field)
$field | string | 字段名称
{return} | array | 索引词列表(词为键, 词重为值), 若无则返回 null
源码: sdk/php/lib/XSDocument.class.php#L178 (显示)
public function getAddTerms($field)
{
    $field = strval($field);
    if ($this->_terms === null || !isset($this->_terms[$field]))
        return null;
    $terms = array();
    foreach ($this->_terms[$field] as $term => $weight)
    {
        $term = $this->autoConvert($term);
        $terms[$term] = $weight;
    }
    return $terms;
}

获取字段的附加索引词列表 (仅限索引文档)

getCharset() 方法
public string getCharset()
{return} string 当前设定的字符集(已大写), 若未曾设置则返回 null
源码: sdk/php/lib/XSDocument.class.php#L121 (显示)
public function getCharset()
{
    return $this->_charset;
}

获取文档字符集

getIterator() 方法
public void getIterator()
源码: sdk/php/lib/XSDocument.class.php#L243 (显示)
public function getIterator()
{
    if ($this->_charset !== null && $this->_charset !== 'UTF-8')
    {
        $from = $this->_meta === null ? $this->_charset : 'UTF-8';
        $to = $this->_meta === null ? 'UTF-8' : $this->_charset;
        return new ArrayIterator(XS::convert($this->_data, $to, $from));
    }
    return new ArrayIterator($this->_data);
}

IteratorAggregate 接口, 以支持 foreach 遍历访问字段列表

offsetExists() 方法
public bool offsetExists(string $name)
$name | string | 字段名称
{return} | bool | 存在返回 true, 若不存在返回 false
源码: sdk/php/lib/XSDocument.class.php#L259 (显示)
public function offsetExists($name)
{
    return isset($this->_data[$name]);
}

ArrayAccess 接口, 判断字段是否存在, 勿直接调用

offsetGet() 方法
public mixed offsetGet(string $name)
$name | string | 字段名称
{return} | mixed | 字段值, 若不存在返回 null
源码: sdk/php/lib/XSDocument.class.php#L270 (显示)
public function offsetGet($name)
{
    return $this->__get($name);
}

ArrayAccess 接口, 取得字段值, 勿直接调用

参见

  • __get
offsetSet() 方法
public void offsetSet(string $name, mixed $value)
$name | string | 字段名称
$value | mixed | 字段值
源码: sdk/php/lib/XSDocument.class.php#L281 (显示)
public function offsetSet($name, $value)
{
    if (!is_null($name))
        $this->__set(strval($name), $value);
}

ArrayAccess 接口, 设置字段值, 勿直接调用

参见

  • __set
offsetUnset() 方法
public void offsetUnset(string $name)
$name string 字段名称
源码: sdk/php/lib/XSDocument.class.php#L291 (显示)
public function offsetUnset($name)
{
    unset($this->_data[$name]);
}

ArrayAccess 接口, 删除字段值, 勿直接调用

setCharset() 方法
public void setCharset(string $charset)
$charset string 设置文档字符集
源码: sdk/php/lib/XSDocument.class.php#L130 (显示)
public function setCharset($charset)
{
    $this->_charset = strtoupper($charset);
    if ($this->_charset == 'UTF8')
        $this->_charset = 'UTF-8';
}

设置文档字符集

setField() 方法
public void setField(string $name, mixed $value)
$name | string | 字段名称
$value | mixed | 字段值
源码: sdk/php/lib/XSDocument.class.php#L155 (显示)
public function setField($name, $value)
{
    if ($value === null)
        unset($this->_data[$name]);
    else
        $this->_data[$name] = $value;
}

设置某个字段的值

setFields() 方法
public void setFields(array $data)
$data array 字段名及其值组成的数组
源码: sdk/php/lib/XSDocument.class.php#L142 (显示)
public function setFields($data)
{
    if ($data === null)
        $this->_data = array();
    else
        $this->_data = array_merge($this->_data, $data);
}

批量设置字段值 这里是以合并方式赋值, 即不会清空已赋值并且不在参数中的字段.


XSErrorException

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSErrorException » XSException » Exception
版本 | 1.0.0
源代码 | sdk/php/lib/XS.class.php
XS 错误异常类定义, XS 所有操作过程发生错误均抛出该实例

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
code |  |  | Exception
file |  |  | Exception
line |  |  | Exception
message |  |  | Exception

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSErrorException
__toString() | 将类对象转换成字符串 | XSErrorException
getCode() |  | Exception
getFile() |  | Exception
getLine() |  | Exception
getMessage() |  | Exception
getPrevious() |  | Exception
getRelPath() | 取得相对当前的文件路径 | XSException
getTrace() |  | Exception
getTraceAsString() |  | Exception

方法明细

__construct() 方法
public void __construct(int $code, string $message, string $file, int $line, Exception $previous=NULL)
$code | int | 出错代码
$message | string | 出错信息
$file | string | 出错所在文件
$line | int | 出错所在的行数
$previous | Exception |
源码: sdk/php/lib/XS.class.php#L137 (显示)
public function __construct($code, $message, $file, $line, $previous = null)
{
    $this->_file = $file;
    $this->_line = $line;
    if (version_compare(PHP_VERSION, '5.3.0', '>='))
        parent::__construct($message, $code, $previous);
    else
        parent::__construct($message, $code);
}

构造函数 将 $file, $line 记录到私有属性在 __toString 中使用

__toString() 方法
public string __toString()
{return} string 异常的简要描述信息
源码: sdk/php/lib/XS.class.php#L151 (显示)
public function __toString()
{
    $string = '[' . __CLASS__ . '] ' . $this->getRelPath($this->_file) . '(' . $this->_line . '): ';
    $string .= $this->getMessage() . '(' . $this->getCode() . ')';
    return $string;
}

将类对象转换成字符串


XSException

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSException » Exception
子类 | XSErrorException
版本 | 1.0.0
源代码 | sdk/php/lib/XS.class.php
XS 异常类定义, XS 所有操作过程发生异常均抛出该实例

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
code |  |  | Exception
file |  |  | Exception
line |  |  | Exception
message |  |  | Exception

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() |  | Exception
__toString() | 将类对象转换成字符串 | XSException
getCode() |  | Exception
getFile() |  | Exception
getLine() |  | Exception
getMessage() |  | Exception
getPrevious() |  | Exception
getRelPath() | 取得相对当前的文件路径 | XSException
getTrace() |  | Exception
getTraceAsString() |  | Exception

方法明细

__toString() 方法
public string __toString()
{return} string 异常的简要描述信息
源码: sdk/php/lib/XS.class.php#L67 (显示)
public function __toString()
{
    $string = '[' . __CLASS__ . '] ' . $this->getRelPath($this->getFile()) . '(' . $this->getLine() . '): ';
    $string .= $this->getMessage() . ($this->getCode() > 0 ? '(S#' . $this->getCode() . ')' : '');
    return $string;
}

将类对象转换成字符串

getRelPath() 方法
public static string getRelPath(string $file)
$file | string | 需要转换的绝对路径
{return} | string | 转换后的相对路径
源码: sdk/php/lib/XS.class.php#L79 (显示)
public static function getRelPath($file)
{
    $from = getcwd();
    $file = realpath($file);
    if (is_dir($file))
    {
        $pos = false;
        $to = $file;
    }
    else
    {
        $pos = strrpos($file, '/');
        $to = substr($file, 0, $pos);
    }
    for ($rel = '';; $rel .= '../')
    {
        if ($from === $to)
            break;
        if ($from === dirname($from))
        {
            $rel .= substr($to, 1);
            break;
        }
        if (!strncmp($from . '/', $to, strlen($from) + 1))
        {
            $rel .= substr($to, strlen($from) + 1);
            break;
        }
        $from = dirname($from);
    }
    if (substr($rel, -1, 1) === '/')
        $rel = substr($rel, 0, -1);
    if ($pos !== false)
        $rel .= substr($file, $pos);
    return $rel;
}

取得相对当前的文件路径


XSFieldMeta

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSFieldMeta
版本 | 1.0.0
源代码 | sdk/php/lib/XSFieldScheme.class.php
数据字段结构元数据 每个搜索项目包含若干个字段, 字段元数据保存在项目的 ini 配置文件中

Public 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
cutlen | int | 剪取长度 (单位:字节) 用于在返回搜索结果自动剪取较长内容的字段, 默认为 0表示不截取, body 型字段默认为 300 字节 | XSFieldMeta
name | string | 字段名称 理论上支持各种可视字符, 推荐字符范围:[0-9A-Za-z-_], 长度控制在 1~32 字节为宜 | XSFieldMeta
type | int | 字段类型 | XSFieldMeta
vno | int | 字段序号 取值为 0~255, 同一字段方案内不能重复, 由 XSFieldScheme::addField 进行确定 | XSFieldMeta
weight | int | 混合区检索时的相对权重 取值范围: 1~63, title 类型的字段默认为 5, 其它字段默认为 1 | XSFieldMeta

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSFieldMeta
__toString() | 将对象转换为字符串 | XSFieldMeta
fromConfig() | 解析字段对象属性 | XSFieldMeta
getCustomTokenizer() | 获取自定义词法分析器 | XSFieldMeta
hasCustomTokenizer() | 判断当前字段是否采用自定义分词器 | XSFieldMeta
hasIndex() | 判断当前字段是否需要索引 | XSFieldMeta
hasIndexMixed() | 判断当前字段是否需要在混合区索引 | XSFieldMeta
hasIndexSelf() | 判断当前字段是否需要在字段区索引 | XSFieldMeta
isBoolIndex() | 判断当前字段的索引是否为布尔型 | XSFieldMeta
isNumeric() | 判断当前字段是否为数字型 | XSFieldMeta
isSpeical() | 判断当前字段是否为特殊类型 | XSFieldMeta
toConfig() | 将对象转换为配置文件字符串 | XSFieldMeta
val() | 把给定的值转换为符合这个字段的数据格式 | XSFieldMeta
withPos() | 判断当前字段索引是否支持短语搜索 | XSFieldMeta

属性明细

cutlen 属性
public int $cutlen;

剪取长度 (单位:字节) 用于在返回搜索结果自动剪取较长内容的字段, 默认为 0表示不截取, body 型字段默认为 300 字节

name 属性
public string $name;

字段名称 理论上支持各种可视字符, 推荐字符范围:[0-9A-Za-z-_], 长度控制在 1~32 字节为宜

type 属性
public int $type;

字段类型

vno 属性
public int $vno;

字段序号 取值为 0~255, 同一字段方案内不能重复, 由 XSFieldScheme::addField 进行确定

weight 属性
public int $weight;

混合区检索时的相对权重 取值范围: 1~63, title 类型的字段默认为 5, 其它字段默认为 1

方法明细

__construct() 方法
public void __construct(string $name, array $config=NULL)
$name | string | 字段名称
$config | array | 可选参数, 初始化字段各项配置
源码: sdk/php/lib/XSFieldScheme.class.php#L290 (显示)
public function __construct($name, $config = null)
{
    $this->name = strval($name);
    if (is_array($config))
        $this->fromConfig($config);
}

构造函数

__toString() 方法
public string __toString()
{return} string 字段名称
源码: sdk/php/lib/XSFieldScheme.class.php#L301 (显示)
public function __toString()
{
    return $this->name;
}

将对象转换为字符串

fromConfig() 方法
public void fromConfig(array $config)
$config array 原始配置属性数组
源码: sdk/php/lib/XSFieldScheme.class.php#L492 (显示)
public function fromConfig($config)
{
    // type & default setting
    if (isset($config['type']))
    {
        $predef = 'self::TYPE_' . strtoupper($config['type']);
        if (defined($predef))
        {
            $this->type = constant($predef);
            if ($this->type == self::TYPE_ID)
            {
                $this->flag = self::FLAG_INDEX_SELF;
                $this->tokenizer = 'full';
            }
            else if ($this->type == self::TYPE_TITLE)
            {
                $this->flag = self::FLAG_INDEX_BOTH | self::FLAG_WITH_POSITION;
                $this->weight = 5;
            }
            else if ($this->type == self::TYPE_BODY)
            {
                $this->vno = XSFieldScheme::MIXED_VNO;
                $this->flag = self::FLAG_INDEX_SELF | self::FLAG_WITH_POSITION;
                $this->cutlen = 300;
            }
        }
    }
    // index flag
    if (isset($config['index']) && $this->type != self::TYPE_BODY)
    {
        $predef = 'self::FLAG_INDEX_' . strtoupper($config['index']);
        if (defined($predef))
        {
            $this->flag &= ~ self::FLAG_INDEX_BOTH;
            $this->flag |= constant($predef);
        }
        if ($this->type == self::TYPE_ID)
            $this->flag |= self::FLAG_INDEX_SELF;
    }
    // others
    if (isset($config['cutlen']))
        $this->cutlen = intval($config['cutlen']);
    if (isset($config['weight']) && $this->type != self::TYPE_BODY)
        $this->weight = intval($config['weight']) & self::MAX_WDF;
    if (isset($config['phrase']))
    {
        if (!strcasecmp($config['phrase'], 'yes'))
            $this->flag |= self::FLAG_WITH_POSITION;
        else if (!strcasecmp($config['phrase'], 'no'))
            $this->flag &= ~ self::FLAG_WITH_POSITION;
    }
    if (isset($config['tokenizer']) && $this->type != self::TYPE_ID
        && $config['tokenizer'] != 'default')
    {
        $this->tokenizer = $config['tokenizer'];
    }
}

解析字段对象属性

getCustomTokenizer() 方法
public XSTokenizer getCustomTokenizer()
{return} XSTokenizer 获取当前字段的自定义词法分析器
源码: sdk/php/lib/XSFieldScheme.class.php#L401 (显示)
public function getCustomTokenizer()
{
    if (isset(self::$_tokenizers[$this->tokenizer]))
        return self::$_tokenizers[$this->tokenizer];
    else
    {
        if (($pos1 = strpos($this->tokenizer, '(')) !== false
            && ($pos2 = strrpos($this->tokenizer, ')', $pos1 + 1)))
        {
            $name = 'XSTokenizer' . ucfirst(trim(substr($this->tokenizer, 0, $pos1)));
            $arg = substr($this->tokenizer, $pos1 + 1, $pos2 - $pos1 - 1);
        }
        else
        {
            $name = 'XSTokenizer' . ucfirst($this->tokenizer);
            $arg = null;
        }
        if (!class_exists($name))
            throw new XSException('Undefined custom tokenizer `' . $this->tokenizer . '\' for field `' . $this->name . '\'');

        $obj = $arg === null ? new $name : new $name($arg);
        if (!$obj instanceof XSTokenizer)
            throw new XSException($name . ' for field `' . $this->name . '\' dose not implement the interface: XSTokenizer');
        self::$_tokenizers[$this->tokenizer] = $obj;
        return $obj;
    }
}

获取自定义词法分析器

hasCustomTokenizer() 方法
public bool hasCustomTokenizer()
{return} bool 是返回 true, 不是返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L391 (显示)
public function hasCustomTokenizer()
{
    return ($this->tokenizer !== XSTokenizer::DFL);
}

判断当前字段是否采用自定义分词器

hasIndex() 方法
public bool hasIndex()
{return} bool 若需要返回 true, 不需要则返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L364 (显示)
public function hasIndex()
{
    return ($this->flag & self::FLAG_INDEX_BOTH) ? true : false;
}

判断当前字段是否需要索引

hasIndexMixed() 方法
public bool hasIndexMixed()
{return} bool 若需要返回 true, 不需要则返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L373 (显示)
public function hasIndexMixed()
{
    return ($this->flag & self::FLAG_INDEX_MIXED) ? true : false;
}

判断当前字段是否需要在混合区索引

hasIndexSelf() 方法
public bool hasIndexSelf()
{return} bool 若需要返回 true, 不需要则返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L382 (显示)
public function hasIndexSelf()
{
    return ($this->flag & self::FLAG_INDEX_SELF) ? true : false;
}

判断当前字段是否需要在字段区索引

isBoolIndex() 方法
public bool isBoolIndex()
{return} bool 是返回 true, 不是返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L336 (显示)
public function isBoolIndex()
{
    return (!$this->hasIndex() || $this->tokenizer !== XSTokenizer::DFL);
}

判断当前字段的索引是否为布尔型 目前只有内置分词器支持语法型索引, 自 1.0.1 版本起把非索引字段也视为布尔便于判断

isNumeric() 方法
public bool isNumeric()
{return} bool 是返回 true, 不是返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L345 (显示)
public function isNumeric()
{
    return ($this->type == self::TYPE_NUMERIC);
}

判断当前字段是否为数字型

isSpeical() 方法
public bool isSpeical()
{return} bool 是返回 true, 不是返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L355 (显示)
public function isSpeical()
{
    return ($this->type == self::TYPE_ID || $this->type == self::TYPE_TITLE || $this->type == self::TYPE_BODY);
}

判断当前字段是否为特殊类型 特殊类型的字段是指 id, title, body, 每个项目至多只能有一个这种类型的字段

toConfig() 方法
public string toConfig()
{return} string 转换后的配置文件字符串
源码: sdk/php/lib/XSFieldScheme.class.php#L433 (显示)
public function toConfig()
{
    // type
    $str = "[" . $this->name . "]\n";
    if ($this->type === self::TYPE_NUMERIC)
        $str .= "type = numeric\n";
    else if ($this->type === self::TYPE_DATE)
        $str .= "type = date\n";
    else if ($this->type === self::TYPE_ID)
        $str .= "type = id\n";
    else if ($this->type === self::TYPE_TITLE)
        $str .= "type = title\n";
    else if ($this->type === self::TYPE_BODY)
        $str .= "type = body\n";
    // index
    if ($this->type !== self::TYPE_BODY && ($index = ($this->flag & self::FLAG_INDEX_BOTH)))
    {
        if ($index === self::FLAG_INDEX_BOTH)
        {
            if ($this->type !== self::TYPE_TITLE)
                $str .= "index = both\n";
        }
        else if ($index === self::FLAG_INDEX_MIXED)
        {
            $str .= "index = mixed\n";
        }
        else
        {
            if ($this->type != self::TYPE_ID)
                $str .= "index = self\n";
        }
    }
    // tokenizer
    if ($this->type !== self::TYPE_ID && $this->tokenizer !== XSTokenizer::DFL)
        $str .= "tokenizer = " . $this->tokenizer . "\n";
    // cutlen
    if ($this->cutlen > 0 && !($this->cutlen === 300 && $this->type === self::TYPE_BODY))
        $str .= "cutlen = " . $this->cutlen . "\n";
    // weight
    if ($this->weight !== 1 && !($this->weight === 5 && $this->type === self::TYPE_TITLE))
        $str .= "weight = " . $this->weight . "\n";
    // phrase
    if ($this->flag & self::FLAG_WITH_POSITION)
    {
        if ($this->type !== self::TYPE_BODY && $this->type !== self::TYPE_TITLE)
            $str .= "phrase = yes\n";
    }
    else
    {
        if ($this->type === self::TYPE_BODY || $this->type === self::TYPE_TITLE)
            $str .= "phrase = no\n";
    }
    return $str;
}

将对象转换为配置文件字符串

val() 方法
public mixed val(mixed $value)
$value | mixed | 原值
{return} | mixed | 转换后的值
源码: sdk/php/lib/XSFieldScheme.class.php#L311 (显示)
public function val($value)
{
    if ($this->type == self::TYPE_DATE)
    {
        // 日期类型: 转换成专用的 YYYYmmdd 格式
        if (!is_numeric($value) || strlen($value) != 8)
            $value = date('Ymd', is_numeric($value) ? $value : strtotime($value));
    }
    return $value;
}

把给定的值转换为符合这个字段的数据格式

withPos() 方法
public bool withPos()
{return} bool 是返回 true, 不是返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L326 (显示)
public function withPos()
{
    return ($this->flag & self::FLAG_WITH_POSITION) ? true : false;
}

判断当前字段索引是否支持短语搜索


XSFieldScheme

All Packages | 方法(函数)
包 | XS
继承关系 | class XSFieldScheme
实现接口 | IteratorAggregate, Traversable
版本 | 1.0.0
源代码 | sdk/php/lib/XSFieldScheme.class.php
XS 数据字段方案 每个方案包含若干个字段结构对象 XSFieldMeta 每个方案必须并且只能包含一个类型为 ID 的字段, 支持 foreach 遍历所有字段

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__toString() | 将对象转换为配置文件字符串 | XSFieldScheme
addField() | 添加字段到方案中 | XSFieldScheme
checkValid() | 判断该字段方案是否有效、可用 | XSFieldScheme
getAllFields() | 获取项目所有字段结构设置 | XSFieldScheme
getField() | 获取项目字段元数据 | XSFieldScheme
getFieldBody() | 获取内容字段元数据 | XSFieldScheme
getFieldId() | 获取主键字段元数据 | XSFieldScheme
getFieldTitle() | 获取标题字段元数据 | XSFieldScheme
getIterator() | IteratorAggregate 接口, 以支持 foreach 遍历访问所有字段 | XSFieldScheme
getVnoMap() | 获取所有字段的vno与名称映映射关系 | XSFieldScheme
logger() | 获取搜索日志的字段方案 | XSFieldScheme

方法明细

__toString() 方法
public void __toString()
源码: sdk/php/lib/XSFieldScheme.class.php#L33 (显示)
public function __toString()
{
    $str = '';
    foreach ($this->_fields as $field)
    {
        $str .= $field->toConfig() . "\n";
    }
    return $str;
}

将对象转换为配置文件字符串

addField() 方法
public void addField(mixed $field, array $config=NULL)
$field | mixed | 若类型为 XSFieldMeta 表示要添加的字段对象, 若类型为 string 表示字段名称, 连同 $config 参数一起创建字段对象
$config | array | 当 $field 参数为 string 时作为新建字段的配置内容
源码: sdk/php/lib/XSFieldScheme.class.php#L139 (显示)
public function addField($field, $config = null)
{
    if (!$field instanceof XSFieldMeta)
        $field = new XSFieldMeta($field, $config);

    if (isset($this->_fields[$field->name]))
        throw new XSException('Duplicated field name: `' . $field->name . '\'');

    if ($field->isSpeical())
    {
        if (isset($this->_typeMap[$field->type]))
        {
            $prev = $this->_typeMap[$field->type];
            throw new XSException('Duplicated ' . strtoupper($config['type']) . ' field: `' . $field->name . '\' and `' . $prev . '\'');
        }
        $this->_typeMap[$field->type] = $field->name;
    }

    $field->vno = ($field->type == XSFieldMeta::TYPE_BODY) ? self::MIXED_VNO : count($this->_vnoMap);
    $this->_vnoMap[$field->vno] = $field->name;

    // save field, ensure ID is the first field
    if ($field->type == XSFieldMeta::TYPE_ID)
        $this->_fields = array_merge(array($field->name => $field), $this->_fields);
    else
        $this->_fields[$field->name] = $field;
}

添加字段到方案中 每个方案中的特殊类型字段都不能重复出现

checkValid() 方法
public bool checkValid(bool $throw=false)
$throw | bool | 当没有通过检测时是否抛出异常, 默认为 false
{return} | bool | 有效返回 true, 无效则返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L174 (显示)
public function checkValid($throw = false)
{
    if (!isset($this->_typeMap[XSFieldMeta::TYPE_ID]))
    {
        if ($throw)
            throw new XSException('Missing field of type ID');
        return false;
    }
    return true;
}

判断该字段方案是否有效、可用 每个方案必须并且只能包含一个类型为 ID 的字段

getAllFields() 方法
public XSFieldMeta[] getAllFields()
{return} XSFieldMeta[]
源码: sdk/php/lib/XSFieldScheme.class.php#L117 (显示)
public function getAllFields()
{
    return $this->_fields;
}

获取项目所有字段结构设置

getField() 方法
public XSFieldMeta getField(mixed $name, bool $throw=true)
$name | mixed | 字段名称(string) 或字段序号(vno, int)
$throw | bool | 当字段不存在时是否抛出异常, 默认为 true
{return} | XSFieldMeta | 字段元数据对象, 若不存在则返回 false
源码: sdk/php/lib/XSFieldScheme.class.php#L92 (显示)
public function getField($name, $throw = true)
{
    if (is_int($name))
    {
        if (!isset($this->_vnoMap[$name]))
        {
            if ($throw === true)
                throw new XSException('Not exists field with vno: `' . $name . '\'');
            return false;
        }
        $name = $this->_vnoMap[$name];
    }
    if (!isset($this->_fields[$name]))
    {
        if ($throw === true)
            throw new XSException('Not exists field with name: `' . $name . '\'');
        return false;
    }
    return $this->_fields[$name];
}

获取项目字段元数据

getFieldBody() 方法
public XSFieldMeta getFieldBody()
{return} XSFieldMeta 类型为 BODY 的字段
源码: sdk/php/lib/XSFieldScheme.class.php#L75 (显示)
public function getFieldBody()
{
    if (isset($this->_typeMap[XSFieldMeta::TYPE_BODY]))
    {
        $name = $this->_typeMap[XSFieldMeta::TYPE_BODY];
        return $this->_fields[$name];
    }
    return false;
}

获取内容字段元数据

getFieldId() 方法
public XSFieldMeta getFieldId()
{return} XSFieldMeta 类型为 ID 的字段
源码: sdk/php/lib/XSFieldScheme.class.php#L47 (显示)
public function getFieldId()
{
    if (isset($this->_typeMap[XSFieldMeta::TYPE_ID]))
    {
        $name = $this->_typeMap[XSFieldMeta::TYPE_ID];
        return $this->_fields[$name];
    }
    return false;
}

获取主键字段元数据

getFieldTitle() 方法
public XSFieldMeta getFieldTitle()
{return} XSFieldMeta 类型为 TITLE 的字段
源码: sdk/php/lib/XSFieldScheme.class.php#L61 (显示)
public function getFieldTitle()
{
    if (isset($this->_typeMap[XSFieldMeta::TYPE_TITLE]))
    {
        $name = $this->_typeMap[XSFieldMeta::TYPE_TITLE];
        return $this->_fields[$name];
    }
    return false;
}

获取标题字段元数据

getIterator() 方法
public void getIterator()
源码: sdk/php/lib/XSFieldScheme.class.php#L188 (显示)
public function getIterator()
{
    return new ArrayIterator($this->_fields);
}

IteratorAggregate 接口, 以支持 foreach 遍历访问所有字段

getVnoMap() 方法
public array getVnoMap()
{return} array vno为键, 字段名为值的数组
源码: sdk/php/lib/XSFieldScheme.class.php#L126 (显示)
public function getVnoMap()
{
    return $this->_vnoMap;
}

获取所有字段的vno与名称映映射关系

logger() 方法
public static XSFieldScheme logger()
{return} XSFieldScheme 搜索日志字段方案
源码: sdk/php/lib/XSFieldScheme.class.php#L197 (显示)
public static function logger()
{
    if (self::$_logger === null)
    {
        $scheme = new self;
        $scheme->addField('id', array('type' => 'id'));
        $scheme->addField('pinyin');
        $scheme->addField('partial');
        $scheme->addField('total', array('type' => 'numeric'));
        $scheme->addField('lastnum', array('type' => 'numeric'));
        $scheme->addField('currnum', array('type' => 'numeric'));
        $scheme->addField('currtag', array('type' => 'numeric'));
        $scheme->addField('body', array('type' => 'body'));
        self::$_logger = $scheme;
    }
    return self::$_logger;
}

获取搜索日志的字段方案


XSIndex

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSIndex » XSServer » XSComponent
版本 | 1.0.0
源代码 | sdk/php/lib/XSIndex.class.php
XS 索引管理 添加/删除/修改索引数据

Public 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
project | string | 获取当前项目名称 | XSServer
respond | XSCommand | 从服务器读取响应指令 | XSServer
socket | mixed | 获取连接资源描述符 | XSServer
xs | XS | 服务端关联的 XS 对象 | XSServer

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数, 打开连接 | XSServer
__destruct() | 析构函数, 关闭连接 | XSServer
__get() | 魔术方法 __get | XSComponent
__isset() | 魔术方法 __isset | XSComponent
__set() | 魔术方法 __set | XSComponent
__unset() | 魔术方法 __unset | XSComponent
add() | 添加文档到索引中 | XSIndex
addExdata() | 批量提交索引命令封包数据 | XSIndex
beginRebuild() | 开始重建索引 | XSIndex
clean() | 完全清空索引数据 | XSIndex
close() | 关闭索引服务端连接 | XSIndex
closeBuffer() | 提交所有指令并关闭缓冲区 | XSIndex
del() | 删除索引中的数据 | XSIndex
endRebuild() | 完成并关闭重建索引 | XSIndex
execCommand() | 执行服务端指令并获取返回值 | XSServer
flushIndex() | 强制刷新服务端的当前库的索引缓存 | XSIndex
flushLogging() | 强制刷新服务端当前项目的搜索日志 | XSIndex
getProject() | 获取当前项目名称 | XSServer
getRespond() | 从服务器读取响应指令 | XSServer
getSocket() | 获取连接资源描述符 | XSServer
hasRespond() | 判断服务端是否有可读数据 | XSServer
open() | 打开服务端连接 | XSServer
openBuffer() | 开启索引命令提交缓冲区 | XSIndex
reopen() | 重新打开连接 | XSServer
sendCommand() | 往服务器直接发送指令 (无缓存) | XSServer
setDb() | 更改存放索引数据的目录 | XSIndex
setProject() | 设置当前项目 | XSServer
setTimeout() | 设置服务端超时秒数 | XSServer
update() | 更新索引文档 | XSIndex

方法明细

add() 方法
public void add(XSDocument $doc)
$doc XSDocument
源码: sdk/php/lib/XSIndex.class.php#L41 (显示)
public function add(XSDocument $doc)
{
    $this->update($doc, true);
}

添加文档到索引中 特别要注意的是: 系统不会自动检测主键是否冲突, 即便已存在相同主键也会添加进去

参见

  • update
addExdata() 方法
public void addExdata(string $data)
$data string 要提交的命令封包数据, 或存储命令封包的文件路径, 编码必须已经是 UTF-8
源码: sdk/php/lib/XSIndex.class.php#L223 (显示)
public function addExdata($data)
{
    if (strlen($data) < 255 && file_exists($data) && ($data = file_get_contents($data) === false))
        throw new XSException('Failed to read exdata from file');

    // try to check allowed (BUG: check the first cmd only):
    // CMD_IMPORT_HEADER, CMD_INDEX_REQUEST, CMD_INDEX_REMOVE, CMD_INDEX_EXDATA
    $first = ord(substr($data, 0, 1));
    if ($first != CMD_IMPORT_HEADER && $first != CMD_INDEX_REQUEST
        && $first != CMD_INDEX_REMOVE && $first != CMD_INDEX_EXDATA)
    {
        throw new XSException('Invalid start command of exdata (CMD:' . $first . ')');
    }

    // create cmd & execute it
    $cmd = array('cmd' => CMD_INDEX_EXDATA, 'buf' => $data);
    $this->execCommand($cmd, CMD_OK_RQST_FINISHED);
}

批量提交索引命令封包数据 把多个命令封包内容连续保存为文件或变量, 然后一次性提交以减少网络开销提升性能

beginRebuild() 方法
public void beginRebuild()
源码: sdk/php/lib/XSIndex.class.php#L274 (显示)
public function beginRebuild()
{
    $this->execCommand(array('cmd' => CMD_INDEX_REBUILD, 'arg1' => 0), CMD_OK_DB_REBUILD);
}

开始重建索引 此后所有的索引更新指令将写到临时库, 而不是当前搜索库, 重建完成后调用 endRebuild 实现平滑重建索引, 重建过程仍可搜索旧的索引库, 如直接用 clean 清空数据, 则会导致重建过程搜索到不全的数据

参见

  • endRebuild
clean() 方法
public void clean()
源码: sdk/php/lib/XSIndex.class.php#L30 (显示)
public function clean()
{
    $this->execCommand(CMD_INDEX_CLEAN_DB, CMD_OK_DB_CLEAN);
}

完全清空索引数据 如果当前数据库处于重建过程中将禁止清空

参见

  • beginRebuild
close() 方法
public void close($ioerr=false)
$ioerr
源码: sdk/php/lib/XSIndex.class.php#L340 (显示)
public function close($ioerr = false)
{
    $this->closeBuffer();
    parent::close($ioerr);
}

关闭索引服务端连接

closeBuffer() 方法
public void closeBuffer()
源码: sdk/php/lib/XSIndex.class.php#L262 (显示)
public function closeBuffer()
{
    $this->openBuffer(0);
}

提交所有指令并关闭缓冲区 若未曾打开缓冲区, 调用本方法是无意义的

参见

  • openBuffer
del() 方法
public void del(mixed $term, string $field=NULL)
$term | mixed | 单个主键或指定字段的索引词, 或多个组成的数组, 编码与 xs 默认字符集一致
$field | string | 索引词所属的字段名称, 默认不指定则为主键字段 (类型为ID)
源码: sdk/php/lib/XSIndex.class.php#L191 (显示)
public function del($term, $field = null)
{
    // get field
    $field = $field === null ? $this->xs->getFieldId() : $this->xs->getField($field);

    // get commands
    $cmds = array();
    $terms = is_array($term) ? array_unique($term) : array($term);
    $terms = XS::convert($terms, 'UTF-8', $this->xs->getDefaultCharset());
    foreach ($terms as $term)
    {
        $cmds[] = new XSCommand(CMD_INDEX_REMOVE, 0, $field->vno, strtolower($term));
    }

    // combine multi commands into exdata
    if ($this->_bufSize > 0)
        $this->appendBuffer(implode('', $cmds));
    else if (count($cmds) == 1)
        $this->execCommand($cmds[0], CMD_OK_RQST_FINISHED);
    else
    {
        $cmd = array('cmd' => CMD_INDEX_EXDATA, 'buf' => implode('', $cmds));
        $this->execCommand($cmd, CMD_OK_RQST_FINISHED);
    }
}

删除索引中的数据

$index->del('123');	// 删除主键为 123 的记录
$index->del(array('123', '789', '456')); // 删除主键为 123, 789, 456 的记录
$index->del('abc', 'field'); // 删除字段 field 上带有索引词 abc 的所有记录
$index->del(array('abc', 'def'), 'field'); // 删除字段 field 上带有索引词 abc 或 def 的所有记录

endRebuild() 方法
public void endRebuild()
源码: sdk/php/lib/XSIndex.class.php#L284 (显示)
public function endRebuild()
{
    $this->execCommand(array('cmd' => CMD_INDEX_REBUILD, 'arg1' => 1), CMD_OK_DB_REBUILD);
}

完成并关闭重建索引 重建完成后调用, 用重建好的索引数据代替旧的索引数据

参见

  • beginRebuild
flushIndex() 方法
public bool flushIndex()
{return} bool 刷新成功返回 true, 失败则返回 false
源码: sdk/php/lib/XSIndex.class.php#L322 (显示)
public function flushIndex()
{
    try
    {
        $this->execCommand(CMD_INDEX_COMMIT, CMD_OK_DB_COMMITED);
    }
    catch (XSException $e)
    {
        if ($e->getCode() === CMD_ERR_BUSY || $e->getCode() === CMD_ERR_RUNNING)
            return false;
        throw $e;
    }
    return true;
}

强制刷新服务端的当前库的索引缓存

flushLogging() 方法
public bool flushLogging()
{return} bool 刷新成功返回 true, 失败则返回 false
源码: sdk/php/lib/XSIndex.class.php#L303 (显示)
public function flushLogging()
{
    try
    {
        $this->execCommand(CMD_FLUSH_LOGGING, CMD_OK_LOG_FLUSHED);
    }
    catch (XSException $e)
    {
        if ($e->getCode() === CMD_ERR_BUSY)
            return false;
        throw $e;
    }
    return true;
}

强制刷新服务端当前项目的搜索日志

openBuffer() 方法
public void openBuffer(int $size=4)
$size int 缓冲区大小, 单位: MB 默认为 4MB
源码: sdk/php/lib/XSIndex.class.php#L249 (显示)
public function openBuffer($size = 4)
{
    if ($this->_buf !== '')
        $this->addExdata($this->_buf);
    $this->_bufSize = intval($size) << 20;
    $this->_buf = '';
}

开启索引命令提交缓冲区 为优化网络性能, 有必要先将本地提交的 add/update/del 等索引变动指令缓存下来, 当总大小达到参数指定的 size 时或调用 closeBuffer 时再真正提交到服务器 注意: 此举常用于需要大批量更新索引时, 此外重复调用本函数是无必要的

setDb() 方法
public void setDb(string $name)
$name string 数据库名称
源码: sdk/php/lib/XSIndex.class.php#L294 (显示)
public function setDb($name)
{
    $this->execCommand(array('cmd' => CMD_INDEX_SET_DB, 'buf' => $name), CMD_OK_DB_CHANGED);
}

更改存放索引数据的目录 默认索引数据保存到服务器上的 db 目录, 通过此方法修改数据目录名

update() 方法
public void update(XSDocument $doc, bool $add=false)
$doc | XSDocument |
$add | bool | 是否为新增文档, 已有数据中不存在同一主键的其它数据
源码: sdk/php/lib/XSIndex.class.php#L53 (显示)
public function update(XSDocument $doc, $add = false)
{
    // before submit
    if ($doc->beforeSubmit($this) === false)
        return;

    // check primary key of document
    $fid = $this->xs->getFieldId();
    $key = $doc->f($fid);
    if ($key === null || $key === '')
        throw new XSException('Missing value of primarky key (FIELD:' . $fid . ')');

    // request cmd
    $cmd = new XSCommand(CMD_INDEX_REQUEST, CMD_INDEX_REQUEST_ADD);
    if ($add !== true)
    {
        $cmd->arg1 = CMD_INDEX_REQUEST_UPDATE;
        $cmd->arg2 = $fid->vno;
        $cmd->buf = $key;
    }
    $cmds = array($cmd);

    // document cmds
    foreach ($this->xs->getAllFields() as $field) /* @var $field XSFieldMeta */
    {
        // value
        if (($value = $doc->f($field)) !== null)
        {
            $varg = $field->isNumeric() ? CMD_VALUE_FLAG_NUMERIC : 0;
            $value = $field->val($value);
            if (!$field->hasCustomTokenizer())
            {
                // internal tokenizer
                $wdf = $field->weight | ($field->withPos() ? CMD_INDEX_FLAG_WITHPOS : 0);
                if ($field->hasIndexMixed())
                    $cmds[] = new XSCommand(CMD_DOC_INDEX, $wdf, XSFieldScheme::MIXED_VNO, $value);
                if ($field->hasIndexSelf())
                {
                    $wdf |= $field->isNumeric() ? 0 : CMD_INDEX_FLAG_SAVEVALUE;
                    $cmds[] = new XSCommand(CMD_DOC_INDEX, $wdf, $field->vno, $value);
                }
                // add value
                if (!$field->hasIndexSelf() || $field->isNumeric())
                    $cmds[] = new XSCommand(CMD_DOC_VALUE, $varg, $field->vno, $value);
            }
            else
            {
                // add index
                if ($field->hasIndex())
                {
                    $terms = $field->getCustomTokenizer()->getTokens($value, $doc);
                    // self: [bool term, NOT weight, NOT stem, NOT pos]
                    if ($field->hasIndexSelf())
                    {
                        foreach ($terms as $term)
                        {
                            $term = strtolower($term);
                            $cmds[] = new XSCommand(CMD_DOC_TERM, 0, $field->vno, $term);
                        }
                    }
                    // mixed: [use default tokenizer]
                    if ($field->hasIndexMixed())
                    {
                        $mtext = implode(' ', $terms);
                        $cmds[] = new XSCommand(CMD_DOC_INDEX, $field->weight, XSFieldScheme::MIXED_VNO, $mtext);
                    }
                }
                // add value
                $cmds[] = new XSCommand(CMD_DOC_VALUE, $varg, $field->vno, $value);
            }
        }
        // process add terms
        if (($terms = $doc->getAddTerms($field)) !== null)
        {
            // ignore weight for bool index
            $wdf1 = $field->isBoolIndex() ? 0 : CMD_INDEX_FLAG_CHECKSTEM;
            foreach ($terms as $term => $wdf)
            {
                $term = strtolower($term);
                $wdf2 = $field->isBoolIndex() ? 0 : $wdf * $field->weight;
                while ($wdf2 > XSFieldMeta::MAX_WDF)
                {
                    $cmds[] = new XSCommand(CMD_DOC_TERM, $wdf1 | XSFieldMeta::MAX_WDF, $field->vno, $term);
                    $wdf2 -= XSFieldMeta::MAX_WDF;
                }
                $cmds[] = new XSCommand(CMD_DOC_TERM, $wdf1 | $wdf2, $field->vno, $term);
            }
        }
        // process add text
        if (($text = $doc->getAddIndex($field)) !== null)
        {
            if (!$field->hasCustomTokenizer())
            {
                $wdf = $field->weight | ($field->withPos() ? CMD_INDEX_FLAG_WITHPOS : 0);
                $cmds[] = new XSCommand(CMD_DOC_INDEX, $arg1, $field->vno, $text);
            }
            else
            {
                // NOT pos
                $wdf = $field->isBoolIndex() ? 0 : ($field->weight | CMD_INDEX_FLAG_CHECKSTEM);
                $terms = $field->getCustomTokenizer()->getTokens($text, $doc);
                foreach ($terms as $term)
                {
                    $term = strtolower($term);
                    $cmds[] = new XSCommand(CMD_DOC_TERM, $wdf, $field->vno, $term);
                }
            }
        }
    }

    // submit cmd
    $cmds[] = new XSCommand(CMD_INDEX_SUBMIT);

    // execute cmd
    if ($this->_bufSize > 0)
        $this->appendBuffer(implode('', $cmds));
    else
    {
        for ($i = 0; $i < count($cmds) - 1; $i++)
            $this->execCommand($cmds[$i]);
        $this->execCommand($cmds[$i], CMD_OK_RQST_FINISHED);
    }

    // after submit
    $doc->afterSubmit($this);
}

更新索引文档 该方法相当于先根据主键删除已存在的旧文档, 然后添加该文档 如果你能明确认定是新文档, 则建议使用 add


XSSearch

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSSearch » XSServer » XSComponent
版本 | 1.0.0
源代码 | sdk/php/lib/XSSearch.class.php
XS 搜索类, 执行搜索功能 有部分方法支持串接操作
$xs->search->setQuery($str)->setLimit(10, 10)->search();
$xs->close();

Public 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
correctedQuery | array | 获取修正后的搜索词列表 | XSSearch
dbTotal | int | 获取搜索数据库内的数据总量 | XSSearch
facets | array | 读取最近一次分面搜索记数 | XSSearch
hotQuery | array | 获取热门搜索词列表 | XSSearch
lastCount | int | 获取最近那次搜索的匹配总数估值 | XSSearch
project | string | 获取当前项目名称 | XSServer
query | string | 获取解析后的搜索语句 | XSSearch
relatedQuery | array | 获取相关搜索词列表 | XSSearch
respond | XSCommand | 从服务器读取响应指令 | XSServer
socket | mixed | 获取连接资源描述符 | XSServer
xs | XS | 服务端关联的 XS 对象 | XSServer

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数, 打开连接 | XSServer
__destruct() | 析构函数, 关闭连接 | XSServer
__get() | 魔术方法 __get | XSComponent
__isset() | 魔术方法 __isset | XSComponent
__set() | 魔术方法 __set | XSComponent
__unset() | 魔术方法 __unset | XSComponent
addDb() | 添加搜索的数据库名, 支持多库同时搜索 | XSSearch
addRange() | 添加搜索过滤区间或范围 | XSSearch
addWeight() | 添加权重索引词 | XSSearch
close() | 关闭连接 | XSServer
count() | 估算搜索语句的匹配数据量 | XSSearch
execCommand() | 执行服务端指令并获取返回值 | XSServer
getCorrectedQuery() | 获取修正后的搜索词列表 | XSSearch
getDbTotal() | 获取搜索数据库内的数据总量 | XSSearch
getExpandedQuery() | 获取展开的搜索词列表 | XSSearch
getFacets() | 读取最近一次分面搜索记数 | XSSearch
getHotQuery() | 获取热门搜索词列表 | XSSearch
getLastCount() | 获取最近那次搜索的匹配总数估值 | XSSearch
getProject() | 获取当前项目名称 | XSServer
getQuery() | 获取解析后的搜索语句 | XSSearch
getRelatedQuery() | 获取相关搜索词列表 | XSSearch
getRespond() | 从服务器读取响应指令 | XSServer
getSocket() | 获取连接资源描述符 | XSServer
hasRespond() | 判断服务端是否有可读数据 | XSServer
highlight() | 搜索结果字符串高亮处理 | XSSearch
markResetScheme() | 标记字段方案重置 | XSSearch
open() | 连接搜索服务端并初始化 | XSSearch
reopen() | 重新打开连接 | XSServer
search() | 获取匹配的搜索结果文档 | XSSearch
sendCommand() | 往服务器直接发送指令 (无缓存) | XSServer
setCharset() | 设置字符集 | XSSearch
setCollapse() | 设置折叠搜索结果 | XSSearch
setDb() | 设置要搜索的数据库名 | XSSearch
setFacets() | 设置分面搜索记数 | XSSearch
setFuzzy() | 开启模糊搜索 | XSSearch
setLimit() | 设置搜索结果的数量和偏移 | XSSearch
setMultiSort() | 设置多字段组合排序方式 | XSSearch
setProject() | 设置当前项目 | XSServer
setQuery() | 设置默认搜索语句 | XSSearch
setSort() | 设置搜索结果的排序方式 | XSSearch
setTimeout() | 设置服务端超时秒数 | XSServer
terms() | 获取搜索语句中的高亮词条列表 | XSSearch

属性明细

correctedQuery 属性 只读
public array getCorrectedQuery(string $query=NULL)

获取修正后的搜索词列表 通常当某次检索结果数量偏少时, 可以用该函数设计 "你是不是要找: ..." 功能

dbTotal 属性 只读
public int getDbTotal()

获取搜索数据库内的数据总量

facets 属性 (自版本 v1.1.0 起可用)
public array getFacets(string $field=NULL)
public XSSearch setFacets(mixed $field, bool $exact=false)

读取最近一次分面搜索记数 必须在某一次 search 之后调用本函数才有意义

hotQuery 属性 只读
public array getHotQuery(int $limit=6, string $type='total')

获取热门搜索词列表

lastCount 属性 只读
public int getLastCount()

获取最近那次搜索的匹配总数估值

参见

  • search
query 属性
public string getQuery(string $query=NULL)
public XSSearch setQuery(string $query)

获取解析后的搜索语句

relatedQuery 属性 只读
public array getRelatedQuery(string $query=NULL, int $limit=6)

获取相关搜索词列表

方法明细

addDb() 方法
public XSSearch addDb(string $name)
$name | string |
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L321 (显示)
public function addDb($name)
{
    $name = strval($name);
    $this->execCommand(array('cmd' => CMD_SEARCH_ADD_DB, 'buf' => $name));
    $this->_curDbs[] = $name;
    return $this;
}

添加搜索的数据库名, 支持多库同时搜索

参见

  • setDb
addRange() 方法
public XSSearch addRange(string $field, mixed $from, mixed $to)
$field | string |
$from | mixed | 起始值(不包含), 若设为 null 则相当于匹配 <= to (字典顺序)
$to | mixed | 结束值(包含), 若设为 null 则相当于匹配 >= from (字典顺序)
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L206 (显示)
public function addRange($field, $from, $to)
{
    if ($from !== null || $to !== null)
    {
        if (strlen($from) > 255 || strlen($to) > 255)
            throw new XSException('Value of range is too long');

        $vno = $this->xs->getField($field)->vno;
        $from = XS::convert($from, 'UTF-8', $this->_charset);
        $to = XS::convert($to, 'UTF-8', $this->_charset);
        if ($from === null)
            $cmd = new XSCommand(CMD_QUERY_VALCMP, CMD_QUERY_OP_FILTER, $vno, $to, chr(CMD_VALCMP_LE));
        else if ($to === null)
            $cmd = new XSCommand(CMD_QUERY_VALCMP, CMD_QUERY_OP_FILTER, $vno, $from, chr(CMD_VALCMP_GE));
        else
            $cmd = new XSCommand(CMD_QUERY_RANGE, CMD_QUERY_OP_FILTER, $vno, $from, $to);
        $this->execCommand($cmd);
    }
    return $this;
}

添加搜索过滤区间或范围

addWeight() 方法
public XSSearch addWeight(string $field, string $term, float $weight=1)
$field | string | 索引词所属的字段
$term | string | 索引词
$weight | float | 权重计算缩放比例
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L236 (显示)
public function addWeight($field, $term, $weight = 1)
{
    $this->addQueryTerm($field, $term, CMD_QUERY_OP_AND_MAYBE, $weight);
    return $this;
}

添加权重索引词 无论是否包含这种词都不影响搜索匹配, 但会参与计算结果权重, 使结果的相关度更高

参见

  • addQueryTerm
count() 方法
public int count(string $query=NULL)
$query | string | 搜索语句, 若传入 null 使用默认语句, 调用后会还原默认排序方式 如果搜索语句和最近一次 search 的语句一样, 请改用 getLastCount 以提升效率
{return} | int | 匹配的搜索结果数量, 估算数值
源码: sdk/php/lib/XSSearch.class.php#L376 (显示)
public function count($query = null)
{
    $query = $query === null ? '' : $this->preQueryString($query);
    if ($query === '' && $this->_count !== null)
        return $this->_count;

    $cmd = new XSCommand(CMD_SEARCH_GET_TOTAL, 0, $this->_defaultOp, $query);
    $res = $this->execCommand($cmd, CMD_OK_SEARCH_TOTAL);
    $ret = unpack('Icount', $res->buf);

    if ($query === '')
        $this->_count = $ret['count'];
    return $ret['count'];
}

估算搜索语句的匹配数据量

getCorrectedQuery() 方法
public array getCorrectedQuery(string $query=NULL)
$query | string | 需要展开的前缀, 可为拼音、英文、中文
{return} | array | 返回搜索词组成的数组
源码: sdk/php/lib/XSSearch.class.php#L631 (显示)
public function getCorrectedQuery($query = null)
{
    $ret = array();

    try
    {
        if ($query === null)
        {
            if ($this->_count > 0 && $this->_count > ceil($this->getDbTotal() * 0.001))
                return $ret;
            $query = $this->cleanFieldQuery($this->_query);
        }
        if (empty($query) || strpos($query, ':') !== false)
            return $ret;
        $buf = XS::convert($query, 'UTF-8', $this->_charset);
        $cmd = array('cmd' => CMD_QUERY_GET_CORRECTED, 'buf' => $buf);
        $res = $this->execCommand($cmd, CMD_OK_QUERY_CORRECTED);
        if ($res->buf !== '')
            $ret = explode("\n", XS::convert($res->buf, $this->_charset, 'UTF-8'));
    }
    catch (XSException $e)
    {
        if ($e->getCode() != CMD_ERR_XAPIAN)
            throw $e;
    }

    return $ret;
}

获取修正后的搜索词列表 通常当某次检索结果数量偏少时, 可以用该函数设计 "你是不是要找: ..." 功能

getDbTotal() 方法
public int getDbTotal()
{return} int 数据总量
源码: sdk/php/lib/XSSearch.class.php#L486 (显示)
public function getDbTotal()
{
    $cmd = new XSCommand(CMD_SEARCH_DB_TOTAL);
    $res = $this->execCommand($cmd, CMD_OK_DB_TOTAL);
    $tmp = unpack('Itotal', $res->buf);
    return $tmp['total'];
}

获取搜索数据库内的数据总量

getExpandedQuery() 方法
public array getExpandedQuery(string $query, int $limit=10)
$query | string | 需要展开的前缀, 可为拼音、英文、中文
$limit | int | 需要返回的搜索词数量上限, 默认为 10, 最大值为 20
{return} | array | 返回搜索词组成的数组
源码: sdk/php/lib/XSSearch.class.php#L583 (显示)
public function getExpandedQuery($query, $limit = 10)
{
    $ret = array();
    $limit = max(1, min(20, intval($limit)));

    try
    {
        $buf = XS::convert($query, 'UTF-8', $this->_charset);
        $cmd = array('cmd' => CMD_QUERY_GET_EXPANDED, 'arg1' => $limit, 'buf' => $buf);
        $res = $this->execCommand($cmd, CMD_OK_RESULT_BEGIN);

        // echo "Raw Query: " . $res->buf . "\n";
        // get result documents
        while (true)
        {
            $res = $this->getRespond();
            if ($res->cmd == CMD_SEARCH_RESULT_FIELD)
            {
                $ret[] = XS::convert($res->buf, $this->_charset, 'UTF-8');
            }
            else if ($res->cmd == CMD_OK && $res->arg == CMD_OK_RESULT_END)
            {
                // got the end
                // echo "Parsed Query: " . $res->buf . "\n";
                break;
            }
            else
            {
                $msg = 'Unexpected respond in search {CMD:' . $res->cmd . ', ARG:' . $res->arg . '}';
                throw new XSException($msg);
            }
        }
    }
    catch (XSException $e)
    {
        if ($e->getCode() != CMD_ERR_XAPIAN)
            throw $e;
    }

    return $ret;
}

获取展开的搜索词列表

getFacets() 方法 (自版本 v1.1.0 起可用)
public array getFacets(string $field=NULL)
$field | string | 读取分面记数的字段, 若为 null 则返回全部分面搜索记录
{return} | array | 返回由值和计数组成的关联数组, 若不存在或未曾登记过则返回空数组
源码: sdk/php/lib/XSSearch.class.php#L276 (显示)
public function getFacets($field = null)
{
    if ($field === null)
        return $this->_facets;
    return isset($this->_facets[$field]) ? $this->_facets[$field] : array();
}

读取最近一次分面搜索记数 必须在某一次 search 之后调用本函数才有意义

getHotQuery() 方法
public array getHotQuery(int $limit=6, string $type='total')
$limit | int | 需要返回的热门搜索数量上限, 默认为 6, 最大值为 50
$type | string | 排序类型, 默认为 total(搜索总量), 可选值还有 lastnum(上周), currnum(本周)
{return} | array | 返回以搜索词为键, 搜索指数为值的关联数组
源码: sdk/php/lib/XSSearch.class.php#L500 (显示)
public function getHotQuery($limit = 6, $type = 'total')
{
    $ret = array();
    $limit = max(1, min(50, intval($limit)));

    // query from log_db
    $this->xs->setScheme(XSFieldScheme::logger());
    try
    {
        $this->setDb(self::LOB_DB)->setLimit($limit);
        if ($type !== 'lastnum' && $type !== 'currnum')
            $type = 'total';
        $result = $this->search($type . ':1');
        foreach ($result as $doc) /* @var $doc XSDocument */
        {
            $body = $doc->body;
            $ret[$body] = $doc->f($type);
        }
        $this->restoreDb();
    }
    catch (XSException $e)
    {
        if ($e->getCode() != CMD_ERR_XAPIAN)
            throw $e;
    }
    $this->xs->restoreScheme();

    return $ret;
}

获取热门搜索词列表

getLastCount() 方法
public int getLastCount()
{return} int 匹配数据量, 如从未搜索则返回 false
源码: sdk/php/lib/XSSearch.class.php#L477 (显示)
public function getLastCount()
{
    return $this->_lastCount;
}

获取最近那次搜索的匹配总数估值

参见

  • search
getQuery() 方法
public string getQuery(string $query=NULL)
$query | string | 搜索语句, 若传入 null 使用默认语句
{return} | string | 返回解析后的搜索语句
源码: sdk/php/lib/XSSearch.class.php#L94 (显示)
public function getQuery($query = null)
{
    $query = $query === null ? '' : $this->preQueryString($query);
    $cmd = new XSCommand(CMD_QUERY_GET_STRING, 0, $this->_defaultOp, $query);
    $res = $this->execCommand($cmd, CMD_OK_QUERY_STRING);
    return XS::convert($res->buf, $this->_charset, 'UTF-8');
}

获取解析后的搜索语句

getRelatedQuery() 方法
public array getRelatedQuery(string $query=NULL, int $limit=6)
$query | string | 搜索语句, 若传入 null 使用默认语句
$limit | int | 需要返回的相关搜索数量上限, 默认为 6, 最大值为 20
{return} | array | 返回搜索词组成的数组
源码: sdk/php/lib/XSSearch.class.php#L536 (显示)
public function getRelatedQuery($query = null, $limit = 6)
{
    $ret = array();
    $limit = max(1, min(20, intval($limit)));

    // Simple to disable query with field filter
    if ($query === null)
        $query = $this->cleanFieldQuery($this->_query);

    if (empty($query) || strpos($query, ':') !== false)
        return $ret;

    // Search the log database
    $op = $this->_defaultOp;
    $this->xs->setScheme(XSFieldScheme::logger());
    try
    {
        $result = $this->setDb(self::LOB_DB)->setFuzzy()->setLimit($limit + 1)->search($query);
        foreach ($result as $doc) /* @var $doc XSDocument */
        {
            $doc->setCharset($this->_charset);
            $body = $doc->body;
            if (!strcasecmp($body, $query))
                continue;
            $ret[] = $body;
            if (count($ret) == $limit)
                break;
        }
    }
    catch (XSException $e)
    {
        if ($e->getCode() != CMD_ERR_XAPIAN)
            throw $e;
    }
    $this->restoreDb();
    $this->xs->restoreScheme();
    $this->_defaultOp = $op;

    return $ret;
}

获取相关搜索词列表

highlight() 方法
public string highlight(string $value)
$value | string | 需要处理的数据
{return} | string | 高亮后的数据
源码: sdk/php/lib/XSSearch.class.php#L666 (显示)
public function highlight($value)
{
    // return empty value directly
    if (empty($value))
        return $value;

    // initlize the highlight replacements
    if (!is_array($this->_highlight))
        $this->initHighlight();

    // process replace
    if (isset($this->_highlight['pattern']))
        $value = preg_replace($this->_highlight['pattern'], $this->_highlight['replace'], $value);
    if (isset($this->_highlight['pairs']))
        $value = strtr($value, $this->_highlight['pairs']);
    return $value;
}

搜索结果字符串高亮处理 对搜索结果文档的字段进行高亮、飘红处理, 高亮部分加上 em 标记

markResetScheme() 方法
public void markResetScheme()
源码: sdk/php/lib/XSSearch.class.php#L333 (显示)
public function markResetScheme()
{
    $this->_resetScheme = true;
}

标记字段方案重置

参见

  • XS::setScheme
open() 方法
public void open(string $conn)
$conn string
源码: sdk/php/lib/XSSearch.class.php#L55 (显示)
public function open($conn)
{
    parent::open($conn);
    $this->_prefix = array();
    $this->_fieldSet = false;
    $this->_lastCount = false;
}

连接搜索服务端并初始化 每次重新连接后所有的搜索语句相关设置均被还原

参见

  • XSServer::open
search() 方法
public XSDocument[] search(string $query=NULL)
$query | string | 搜索语句, 若传入 null 使用默认语句
{return} | XSDocument[] | 匹配的搜索结果文档列表
源码: sdk/php/lib/XSSearch.class.php#L398 (显示)
public function search($query = null)
{
    $this->_highlight = $query;
    $query = $query === null ? '' : $this->preQueryString($query);
    $page = pack('II', $this->_offset, $this->_limit > 0 ? $this->_limit : self::PAGE_SIZE);

    // get result header
    $cmd = new XSCommand(CMD_SEARCH_GET_RESULT, 0, $this->_defaultOp, $query, $page);
    $res = $this->execCommand($cmd, CMD_OK_RESULT_BEGIN);
    $tmp = unpack('Icount', $res->buf);
    $this->_lastCount = $tmp['count'];

    // load vno map to name of fields
    $ret = $this->_facets = array();
    $vnoes = $this->xs->getScheme()->getVnoMap();

    // get result documents
    while (true)
    {
        $res = $this->getRespond();
        if ($res->cmd == CMD_SEARCH_RESULT_FACETS)
        {
            $off = 0;
            while (($off + 6) < strlen($res->buf))
            {
                $tmp = unpack('Cvno/Cvlen/Inum', substr($res->buf, $off, 6));
                if (isset($vnoes[$tmp['vno']]))
                {
                    $name = $vnoes[$tmp['vno']];
                    $value = substr($res->buf, $off + 6, $tmp['vlen']);
                    if (!isset($this->_facets[$name]))
                        $this->_facets[$name] = array();
                    $this->_facets[$name][$value] = $tmp['num'];
                }
                $off += $tmp['vlen'] + 6;
            }
        }
        else if ($res->cmd == CMD_SEARCH_RESULT_DOC)
        {
            // got new doc
            $doc = new XSDocument($res->buf, $this->_charset);
            $ret[] = $doc;
        }
        else if ($res->cmd == CMD_SEARCH_RESULT_FIELD)
        {
            // fields of doc
            if (isset($doc))
            {
                $name = isset($vnoes[$res->arg]) ? $vnoes[$res->arg] : $res->arg;
                $doc->setField($name, $res->buf);
            }
        }
        else if ($res->cmd == CMD_OK && $res->arg == CMD_OK_RESULT_END)
        {
            // got the end
            break;
        }
        else
        {
            $msg = 'Unexpected respond in search {CMD:' . $res->cmd . ', ARG:' . $res->arg . '}';
            throw new XSException($msg);
        }
    }

    if ($query === '')
    {
        $this->_count = $tmp['count'];
        // trigger to logQuery
        $this->logQuery();
    }
    $this->_limit = $this->_offset = 0;
    return $ret;
}

获取匹配的搜索结果文档 默认提取最匹配的前 self::PAGE_SIZE 个结果 如需分页请参见 setLimit 设置, 每次调用本函数后都会还原 setLimit 的设置

setCharset() 方法
public XSSearch setCharset(string $charset)
$charset | string |
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L69 (显示)
public function setCharset($charset)
{
    $this->_charset = strtoupper($charset);
    if ($this->_charset == 'UTF8')
        $this->_charset = 'UTF-8';
    return $this;
}

设置字符集 默认字符集是 UTF-8, 如果您提交的搜索语句和预期得到的搜索结果为其它字符集, 请先设置

setCollapse() 方法
public XSSearch setCollapse(string $field, int $num=1)
$field | string | 依据该字段的值折叠搜索结果, 设为 null 则取消折叠
$num | int | 折叠后只是返最匹配的数据数量, 默认为 1, 最大值 255
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L189 (显示)
public function setCollapse($field, $num = 1)
{
    $vno = $field === null ? XSFieldScheme::MIXED_VNO : $this->xs->getField($field, true)->vno;
    $max = min(255, intval($num));

    $cmd = new XSCommand(CMD_SEARCH_SET_COLLAPSE, $max, $vno);
    $this->execCommand($cmd);
    return $this;
}

设置折叠搜索结果 注意, 每当调用 setDb 或 addDb 修改当前数据库时会重置此项设置

setDb() 方法
public XSSearch setDb(string $name)
$name | string |
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L304 (显示)
public function setDb($name)
{
    $name = strval($name);
    $this->execCommand(array('cmd' => CMD_SEARCH_SET_DB, 'buf' => strval($name)));
    $this->_lastDb = $this->_curDb;
    $this->_lastDbs = $this->_curDbs;
    $this->_curDb = $name;
    $this->_curDbs = array();
    return $this;
}

设置要搜索的数据库名 若未设置, 使用默认数据库, 数据库必须位于服务端用户目录下 对于远程数据库, 请使用 stub 文件来支持

setFacets() 方法 (自版本 v1.1.0 起可用)
public XSSearch setFacets(mixed $field, bool $exact=false)
$field | mixed | 要进行分组统计的字段或字段组成的数组, 最多同时支持 8 个
$exact | bool | 是否要求绝对精确搜索, 这会造成较大的系统开销
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L251 (显示)
public function setFacets($field, $exact = false)
{
    $buf = '';
    if (!is_array($field))
        $buf = chr($this->xs->getField($field)->vno);
    else
    {
        foreach ($field as $ff)
        {
            $buf .= chr($this->xs->getField($ff)->vno);
        }
    }
    $cmd = array('cmd' => CMD_SEARCH_SET_FACETS, 'buf' => $buf);
    $cmd['arg1'] = $exact === true ? 1 : 0;
    $this->execCommand($cmd);
    return $this;
}

设置分面搜索记数 用于记录匹配搜索结果中按字段值分组的数量统计, 每次调用 search 后会还原设置 对于多次调用 $exact 参数以最后一次为准, 只支持字段值不超过 255 字节的情况

setFuzzy() 方法
public XSSearch setFuzzy(bool $value=true)
$value | bool | 设为 true 表示开启模糊搜索, 设为 false 关闭模糊搜索
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L83 (显示)
public function setFuzzy($value = true)
{
    $this->_defaultOp = $value === true ? CMD_QUERY_OP_OR : CMD_QUERY_OP_AND;
    return $this;
}

开启模糊搜索 默认情况只返回包含所有搜索词的记录, 通过本方法可以获得更多搜索结果

setLimit() 方法
public XSSearch setLimit(int $limit, int $offset=0)
$limit | int | 数量上限, 若设为 0 则启用默认值 self::PAGE_SIZE
$offset | int | 偏移量, 即跳过的结果数量, 默认为 0
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L290 (显示)
public function setLimit($limit, $offset = 0)
{
    $this->_limit = intval($limit);
    $this->_offset = intval($offset);
    return $this;
}

设置搜索结果的数量和偏移 用于搜索结果分页, 每次调用 search 后会还原这2个变量到初始值

setMultiSort() 方法 (自版本 v1.1.0 起可用)
public XSSearch setMultiSort(array $fields)
$fields | array | 排序依据的字段数组, 以字段名称为键, true/false 为值表示正序或逆序
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L127 (显示)
public function setMultiSort($fields)
{
    if (!is_array($fields))
        return $this->setSort($fields);

    // [vno][0/1] (0:reverse,1:asc)
    $buf = '';
    foreach ($fields as $key => $value)
    {
        if (is_bool($value))
        {
            $vno = $this->xs->getField($key, true)->vno;
            $asc = $value;
        }
        else
        {
            $vno = $this->xs->getField($value, true)->vno;
            $asc = false;
        }
        if ($vno != XSFieldScheme::MIXED_VNO)
            $buf .= chr($vno) . chr($asc ? 1 : 0);
    }
    if ($buf !== '')
    {
        $cmd = new XSCommand(CMD_SEARCH_SET_SORT, CMD_SORT_TYPE_MULTI, 0, $buf);
        $this->execCommand($cmd);
    }
    return $this;
}

设置多字段组合排序方式 当您需要根据多个字段的值按不同的方式综合排序时, 请使用这项

setQuery() 方法
public XSSearch setQuery(string $query)
$query | string | 搜索语句, 设为 null 则清空搜索语句
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L109 (显示)
public function setQuery($query)
{
    $this->clearQuery();
    if ($query !== null)
    {
        $this->_query = $query;
        $this->addQueryString($query);
    }
    return $this;
}

设置默认搜索语句 用于不带参数的 count 或 search 以及 terms 调用 可与 addWeight 组合运用

setSort() 方法
public XSSearch setSort(string $field, bool $asc=false)
$field | string | 依据指定字段的值排序, 设为 null 则用默认顺序
$asc | bool | 是否为正序排列, 即从小到大, 从少到多, 默认为反序
{return} | XSSearch | 返回对象本身以支持串接操作
源码: sdk/php/lib/XSSearch.class.php#L165 (显示)
public function setSort($field, $asc = false)
{
    if (is_array($field))
        return $this->setMultiSort($field);
    if ($field === null)
        $cmd = new XSCommand(CMD_SEARCH_SET_SORT, CMD_SORT_TYPE_RELEVANCE);
    else
    {
        $type = CMD_SORT_TYPE_VALUE | ($asc ? CMD_SORT_FLAG_ASCENDING : 0);
        $vno = $this->xs->getField($field, true)->vno;

        $cmd = new XSCommand(CMD_SEARCH_SET_SORT, $type, $vno);
    }
    $this->execCommand($cmd);
    return $this;
}

设置搜索结果的排序方式 注意, 每当调用 setDb 或 addDb 修改当前数据库时会重置排序设定 此函数第一参数的用法与 setMultiSort 兼容, 即也可以用该方法实现多字段排序

terms() 方法
public array terms(string $query=NULL, bool $convert=true)
$query | string | 搜索语句, 若传入 null 使用默认语句
$convert | bool | 是否进行编码转换, 默认为 true
{return} | array | 可用于高亮显示的词条列表
源码: sdk/php/lib/XSSearch.class.php#L344 (显示)
public function terms($query = null, $convert = true)
{
    $query = $query === null ? '' : $this->preQueryString($query);

    if ($query === '' && $this->_terms !== null)
        $ret = $this->_terms;
    else
    {
        $cmd = new XSCommand(CMD_QUERY_GET_TERMS, 0, $this->_defaultOp, $query);
        $res = $this->execCommand($cmd, CMD_OK_QUERY_TERMS);
        $ret = array();

        $tmps = explode(' ', $res->buf);
        for ($i = 0; $i < count($tmps); $i++)
        {
            $chr = ord(substr($tmps[$i], 0, 1));
            if ($tmps[$i] === '' || strpos($tmps[$i], ':') !== false)
                continue;
            $ret[] = $tmps[$i];
        }
        if ($query === '')
            $this->_terms = $ret;
    }
    return $convert ? XS::convert($ret, $this->_charset, 'UTF-8') : $ret;
}

获取搜索语句中的高亮词条列表


XSServer

All Packages | 属性 | 方法(函数)
包 | XS
继承关系 | class XSServer » XSComponent
子类 | XSIndex, XSSearch
版本 | 1.0.0
源代码 | sdk/php/lib/XSServer.class.php
XSServer 服务器操作对象 同时兼容于 indexd, searchd, 所有交互均采用 XSCommand 对象

Public 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
project | string | 获取当前项目名称 | XSServer
respond | XSCommand | 从服务器读取响应指令 | XSServer
socket | mixed | 获取连接资源描述符 | XSServer
xs | XS | 服务端关联的 XS 对象 | XSServer

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数, 打开连接 | XSServer
__destruct() | 析构函数, 关闭连接 | XSServer
__get() | 魔术方法 __get | XSComponent
__isset() | 魔术方法 __isset | XSComponent
__set() | 魔术方法 __set | XSComponent
__unset() | 魔术方法 __unset | XSComponent
close() | 关闭连接 | XSServer
execCommand() | 执行服务端指令并获取返回值 | XSServer
getProject() | 获取当前项目名称 | XSServer
getRespond() | 从服务器读取响应指令 | XSServer
getSocket() | 获取连接资源描述符 | XSServer
hasRespond() | 判断服务端是否有可读数据 | XSServer
open() | 打开服务端连接 | XSServer
reopen() | 重新打开连接 | XSServer
sendCommand() | 往服务器直接发送指令 (无缓存) | XSServer
setProject() | 设置当前项目 | XSServer
setTimeout() | 设置服务端超时秒数 | XSServer

属性明细

project 属性
public string getProject()
public void setProject(string $name, string $home='')

获取当前项目名称

respond 属性 只读
public XSCommand getRespond()

从服务器读取响应指令

socket 属性 只读
public mixed getSocket()

获取连接资源描述符

xs 属性
public XS $xs;

服务端关联的 XS 对象

方法明细

__construct() 方法
public void __construct(string $conn=NULL, XS $xs=NULL)
$conn | string | 服务端连接参数
$xs | XS | 需要捆绑的 xs 对象
源码: sdk/php/lib/XSServer.class.php#L140 (显示)
public function __construct($conn = null, $xs = null)
{
    $this->xs = $xs;
    if ($conn !== null)
        $this->open($conn);
}

构造函数, 打开连接

__destruct() 方法
public void __destruct()
源码: sdk/php/lib/XSServer.class.php#L150 (显示)
public function __destruct()
{
    $this->xs = null;
    $this->close();
}

析构函数, 关闭连接

close() 方法
public void close(bool $ioerr=false)
$ioerr bool 关闭调用是否由于 IO 错误引起的, 以免发送 quit 指令
源码: sdk/php/lib/XSServer.class.php#L192 (显示)
public function close($ioerr = false)
{
    if ($this->_sock && !($this->_flag & self::BROKEN))
    {
        if (!$ioerr && $this->_sendBuffer !== '')
        {
            $this->write($this->_sendBuffer);
            $this->_sendBuffer = '';
        }
        if (!$ioerr && !($this->_flag & self::FILE))
        {
            $cmd = new XSCommand(CMD_QUIT);
            fwrite($this->_sock, $cmd);
        }
        fclose($this->_sock);
        $this->_flag |= self::BROKEN;
    }
}

关闭连接 附带发送发送 quit 命令

execCommand() 方法
public mixed execCommand(mixed $cmd, int $res_arg=0, int $res_cmd=128)
$cmd | mixed | 要提交的指令, 若不是 XSCommand 实例则作为构造函数的第一参数创建对象
$res_arg | int | 要求的响应参数, 默认为 CMD_NONE 即不检测, 若检测结果不符 则认为命令调用失败, 会返回 false 并设置相应的出错信息
$res_cmd | int | 要求的响应指令, 默认为 CMD_OK 即要求结果必须正确.
{return} | mixed | 若无需要检测结果则返回 true, 其它返回响应的 XSCommand 对象
源码: sdk/php/lib/XSServer.class.php#L263 (显示)
public function execCommand($cmd, $res_arg = CMD_NONE, $res_cmd = CMD_OK)
{
    // create command object
    if (!$cmd instanceof XSCommand)
        $cmd = new XSCommand($cmd);

    // just cache the cmd for those need not answer
    if ($cmd->cmd & 0x80)
    {
        $this->_sendBuffer .= $cmd;
        return true;
    }

    // send cmd to server
    $buf = $this->_sendBuffer . $cmd;
    $this->_sendBuffer = '';
    $this->write($buf);

    // return true directly for local file
    if ($this->_flag & self::FILE)
        return true;

    // got the respond
    $res = $this->getRespond();

    // check respond
    if ($res->cmd === CMD_ERR && $res_cmd != CMD_ERR)
        throw new XSException($res->buf, $res->arg);
    // got unexpected respond command
    if ($res->cmd != $res_cmd || ($res_arg != CMD_NONE && $res->arg != $res_arg))
        throw new XSException('Unexpected respond {CMD:' . $res->cmd . ', ARG:' . $res->arg . '}');
    return $res;
}

执行服务端指令并获取返回值

getProject() 方法
public string getProject()
{return} string 项目名称
源码: sdk/php/lib/XSServer.class.php#L224 (显示)
public function getProject()
{
    return $this->_project;
}

获取当前项目名称

getRespond() 方法
public XSCommand getRespond()
{return} XSCommand 成功返回响应指令
源码: sdk/php/lib/XSServer.class.php#L314 (显示)
public function getRespond()
{
    // read data from server
    $buf = $this->read(8);
    $hdr = unpack('Ccmd/Carg1/Carg2/Cblen1/Iblen', $buf);
    $res = new XSCommand($hdr);
    $res->buf = $this->read($hdr['blen']);
    $res->buf1 = $this->read($hdr['blen1']);
    return $res;
}

从服务器读取响应指令

getSocket() 方法
public mixed getSocket()
{return} mixed 连接标识, 仅用于内部测试等目的
源码: sdk/php/lib/XSServer.class.php#L215 (显示)
public function getSocket()
{
    return $this->_sock;
}

获取连接资源描述符

hasRespond() 方法
public bool hasRespond()
{return} bool 如果有返回 true, 否则返回 false
源码: sdk/php/lib/XSServer.class.php#L330 (显示)
public function hasRespond()
{
    // check socket
    if ($this->_sock === null || $this->_flag & (self::BROKEN | self::FILE))
        return false;
    $wfds = $xfds = array();
    $rfds = array($this->_sock);
    $res = stream_select($rfds, $wfds, $xfds, 0, 0);
    return $res > 0;
}

判断服务端是否有可读数据 用于某些特别情况在 getRespond 前先调用和判断, 以免阻塞

open() 方法
public void open(mixed $conn)
$conn mixed 服务端连接参数, 支持: <端口号|host:port|本地套接字路径>
源码: sdk/php/lib/XSServer.class.php#L161 (显示)
public function open($conn)
{
    $this->close();
    $this->_conn = $conn;
    $this->_flag = self::BROKEN;
    $this->_sendBuffer = '';
    $this->_project = null;
    $this->connect();
    $this->_flag ^= self::BROKEN;
    if ($this->xs instanceof XS)
        $this->setProject($this->xs->getName());
}

打开服务端连接 如果已关联 XS 对象, 则会同时切换至相应的项目名称

reopen() 方法
public XSServer reopen(bool $force=false)
$force | bool | 是否强制重新连接, 默认为否
{return} | XSServer | 返回自己, 以便串接操作
源码: sdk/php/lib/XSServer.class.php#L180 (显示)
public function reopen($force = false)
{
    if ($this->_flag & self::BROKEN || $force === true)
        $this->open($this->_conn);
    return $this;
}

重新打开连接 仅应用于曾经成功打开的连并且异常关闭了

sendCommand() 方法
public void sendCommand(mixed $cmd)
$cmd mixed 要提交的指令, 支持 XSCommand 实例或 cmd 构造函数的第一参数
源码: sdk/php/lib/XSServer.class.php#L302 (显示)
public function sendCommand($cmd)
{
    if (!$cmd instanceof XSCommand)
        $cmd = new XSCommand($cmd);
    $this->write(strval($cmd));
}

往服务器直接发送指令 (无缓存)

setProject() 方法
public void setProject(string $name, string $home='')
$name | string | 项目名称
$home | string | 项目在服务器上的目录路径, 可选参数(不得超过255字节).
源码: sdk/php/lib/XSServer.class.php#L234 (显示)
public function setProject($name, $home = '')
{
    if ($name !== $this->_project)
    {
        $cmd = array('cmd' => CMD_USE, 'buf' => $name, 'buf1' => $home);
        $this->execCommand($cmd, CMD_OK_PROJECT);
        $this->_project = $name;
    }
}

设置当前项目

setTimeout() 方法
public void setTimeout(int $sec)
$sec int 秒数, 设为 0则永不超时直到客户端主动关闭
源码: sdk/php/lib/XSServer.class.php#L248 (显示)
public function setTimeout($sec)
{
    $cmd = array('cmd' => CMD_TIMEOUT, 'arg' => $sec);
    $this->execCommand($cmd, CMD_OK_TIMEOUT_SET);
}

设置服务端超时秒数


XSTokenizer

All Packages | 方法(函数)
包 | XS.tokenizer
继承关系 | interface XSTokenizer
子类 | XSTokenizerFull, XSTokenizerNone, XSTokenizerSplit, XSTokenizerXlen, XSTokenizerXstep
版本 | 1.0.0
源代码 | sdk/php/lib/XSTokenizer.class.php
自定义字段词法分析器接口 系统将按照 getTokens 返回的词汇列表对相应的字段建立索引

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
getTokens() | 执行分词并返回词列表 | XSTokenizer

方法明细

getTokens() 方法
abstract public array getTokens(string $value, XSDocument $doc=NULL)
$value | string | 待分词的字段值(UTF-8编码)
$doc | XSDocument | 当前相关的索引文档
{return} | array | 切好的词组成的数组
源码: sdk/php/lib/XSTokenizer.class.php#L33 (显示)
public function getTokens($value, XSDocument $doc = null);

执行分词并返回词列表


XSTokenizerFull

All Packages | 方法(函数)
包 | XS.tokenizer
继承关系 | class XSTokenizerFull
实现接口 | XSTokenizer
版本 | 1.0.0
源代码 | sdk/php/lib/XSTokenizer.class.php
内置整值分词器

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
getTokens() |  | XSTokenizerFull

方法明细

getTokens() 方法
public void getTokens($value, $doc=NULL)
$value
$doc
源码: sdk/php/lib/XSTokenizer.class.php#L62 (显示)
public function getTokens($value, XSDocument $doc = null)
{
    return array($value);
}


XSTokenizerNone

All Packages | 方法(函数)
包 | XS.tokenizer
继承关系 | class XSTokenizerNone
实现接口 | XSTokenizer
版本 | 1.0.0
源代码 | sdk/php/lib/XSTokenizer.class.php
内置空分词器

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
getTokens() |  | XSTokenizerNone

方法明细

getTokens() 方法
public void getTokens($value, $doc=NULL)
$value
$doc
源码: sdk/php/lib/XSTokenizer.class.php#L46 (显示)
public function getTokens($value, XSDocument $doc = null)
{
    return array();
}


XSTokenizerSplit

All Packages | 方法(函数)
包 | XS.tokenizer
继承关系 | class XSTokenizerSplit
实现接口 | XSTokenizer
版本 | 1.0.0
源代码 | sdk/php/lib/XSTokenizer.class.php
内置的分割分词器

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() |  | XSTokenizerSplit
getTokens() |  | XSTokenizerSplit

方法明细

__construct() 方法
public void __construct($arg=NULL)
$arg
源码: sdk/php/lib/XSTokenizer.class.php#L79 (显示)
public function __construct($arg = null)
{
    if ($arg !== null && $arg !== '')
        $this->arg = $arg;
}

getTokens() 方法
public void getTokens($value, $doc=NULL)
$value
$doc
源码: sdk/php/lib/XSTokenizer.class.php#L85 (显示)
public function getTokens($value, XSDocument $doc = null)
{
    if (strlen($this->arg) > 2 && substr($this->arg, 0, 1) == '/' && substr($this->arg, -1, 1) == '/')
        return preg_split($this->arg, $value);
    return explode($this->arg, $value);
}


XSTokenizerXlen

All Packages | 方法(函数)
包 | XS.tokenizer
继承关系 | class XSTokenizerXlen
实现接口 | XSTokenizer
版本 | 1.0.0
源代码 | sdk/php/lib/XSTokenizer.class.php
内置的定长分词器

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() |  | XSTokenizerXlen
getTokens() |  | XSTokenizerXlen

方法明细

__construct() 方法
public void __construct($arg=NULL)
$arg
源码: sdk/php/lib/XSTokenizer.class.php#L104 (显示)
public function __construct($arg = null)
{
    if ($arg !== null && $arg !== '')
    {
        $this->arg = intval($arg);
        if ($this->arg < 1 || $this->arg > 255)
            throw new XSException('Invalid argument for ' . __CLASS__ . ': ' . $arg);
    }
}

getTokens() 方法
public void getTokens($value, $doc=NULL)
$value
$doc
源码: sdk/php/lib/XSTokenizer.class.php#L114 (显示)
public function getTokens($value, XSDocument $doc = null)
{
    $terms = array();
    for ($i = 0; $i < strlen($value); $i += $this->arg)
    {
        $terms[] = substr($value, $i, $this->arg);
    }
    return $terms;
}


XSTokenizerXstep

All Packages | 方法(函数)
包 | XS.tokenizer
继承关系 | class XSTokenizerXstep
实现接口 | XSTokenizer
版本 | 1.0.0
源代码 | sdk/php/lib/XSTokenizer.class.php
内置的步长分词器

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() |  | XSTokenizerXstep
getTokens() |  | XSTokenizerXstep

方法明细

__construct() 方法
public void __construct($arg=NULL)
$arg
源码: sdk/php/lib/XSTokenizer.class.php#L136 (显示)
public function __construct($arg = null)
{
    if ($arg !== null && $arg !== '')
    {
        $this->arg = intval($arg);
        if ($this->arg < 1 || $this->arg > 255)
            throw new XSException('Invalid argument for ' . __CLASS__ . ': ' . $arg);
    }
}

getTokens() 方法
public void getTokens($value, $doc=NULL)
$value
$doc
源码: sdk/php/lib/XSTokenizer.class.php#L146 (显示)
public function getTokens($value, XSDocument $doc = null)
{
    $terms = array();
    $i = $this->arg;
    while (true)
    {
        $terms[] = substr($value, 0, $i);
        if ($i >= strlen($value))
            break;
        $i += $this->arg;
    }
    return $terms;
}


XSCsvDataSource

All Packages | 属性 | 方法(函数)
包 | XS.util
继承关系 | class XSCsvDataSource » XSDataSource
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
CSV 数据源 可在文件开头指定字段(必须是有效字段), 否则将默认按照 XS 项目字段顺序填充

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
arg |  |  | XSDataSource
type |  |  | XSDataSource

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSDataSource
getCharset() | 取得数据源的准确字符集 | XSDataSource
getData() | 从数据源中提取一条数据 | XSDataSource
instance() | 取得数据源对象实例 | XSDataSource

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
deinit() |  | XSCsvDataSource
getDataList() |  | XSCsvDataSource
init() |  | XSCsvDataSource

方法明细

deinit() 方法
protected void deinit()
源码: sdk/php/util/XSDataSource.class.php#L381 (显示)
protected function deinit()
{
    if ($this->fd)
    {
        fclose($this->fd);
        $this->fd = null;
    }
}

getDataList() 方法
protected void getDataList()
源码: sdk/php/util/XSDataSource.class.php#L390 (显示)
protected function getDataList()
{
    if (($item = fgetcsv($this->fd, 0, $this->delim)) === false)
    {
        echo "INFO: reach end of file or error occured, total lines: " . $this->line . "\n";
        return false;
    }

    $this->line++;
    if (count($item) === 1 && is_null($item[0]))
    {
        echo "WARNING: invalid csv line #" . $this->line . "\n";
        return $this->getDataList();
    }
    return array($item);
}

init() 方法
protected void init()
源码: sdk/php/util/XSDataSource.class.php#L366 (显示)
protected function init()
{
    $file = $this->arg;
    if (empty($file))
    {
        echo "WARNING: input file not specified, read data from <STDIN>\n";
        $file = 'php://stdin';
    }
    if (!($this->fd = fopen($file, 'r')))
        throw new XSException("Can not open input file: '$file'");
    $this->line = 0;
    if (isset($_SERVER['XS_CSV_DELIMITER']))
        $this->delim = $_SERVER['XS_CSV_DELIMITER'];
}


XSDataSource

All Packages | 属性 | 方法(函数)
包 | XS.util
继承关系 | abstract class XSDataSource
子类 | XSCsvDataSource, XSDatabaseDataSource, XSJsonDataSource
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
索引数据源抽象基类 此部分代码仅用于 indexer 工具程序

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
arg |  |  | XSDataSource
type |  |  | XSDataSource

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSDataSource
getCharset() | 取得数据源的准确字符集 | XSDataSource
getData() | 从数据源中提取一条数据 | XSDataSource
instance() | 取得数据源对象实例 | XSDataSource

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
deinit() | 执行数据提取完毕后的清理工作 | XSDataSource
getDataList() | 从数据源中提取若干条数据 | XSDataSource
init() | 执行数据提取的准备工作 | XSDataSource

属性明细

arg 属性
protected $arg;

type 属性
protected $type;

方法明细

__construct() 方法
public void __construct(mixed $type, $arg)
$type | mixed | 对象参数, 常为 SQL 语句或要导入的文件路径
$arg |  |
源码: sdk/php/util/XSDataSource.class.php#L29 (显示)
public function __construct($type, $arg)
{
    $this->type = $type;
    $this->arg = $arg;
    $this->init();
}

构造函数

deinit() 方法
protected void deinit()
源码: sdk/php/util/XSDataSource.class.php#L103 (显示)
protected function deinit()
{

}

执行数据提取完毕后的清理工作 将自动在没有更多数据供提取时调用此函数, 请在具体的数据源重载此函数

getCharset() 方法
public string getCharset()
{return} string 字符集名称
源码: sdk/php/util/XSDataSource.class.php#L85 (显示)
public function getCharset()
{
    return false;
}

取得数据源的准确字符集 如不能确定字符集, 请直接返回 false

getData() 方法
final public mixed getData()
{return} mixed 返回一条完整数据, 若无数据则返回 false
源码: sdk/php/util/XSDataSource.class.php#L62 (显示)
final public function getData()
{
    if ($this->dataPos === null || $this->dataPos === count($this->dataList))
    {
        $this->dataPos = 0;
        $this->dataList = $this->getDataList();
        if (!is_array($this->dataList) || count($this->dataList) === 0)
        {
            $this->deinit();
            $this->dataList = $this->dataPos = null;
            return false;
        }
    }
    $data = $this->dataList[$this->dataPos];
    $this->dataPos++;
    return $data;
}

从数据源中提取一条数据 实际使用时, 一般是循环调用此函数提取数据, 每条数据是由字段名为键的关联数组

while ($ds->getData() !== false)
{
  ...
}

getDataList() 方法
protected array getDataList()
{return} array
源码: sdk/php/util/XSDataSource.class.php#L113 (显示)
protected function getDataList()
{
    return false;
}

从数据源中提取若干条数据 必须在数据源中定义此函数, 返回值必须是各条数据的数组

init() 方法
protected void init()
源码: sdk/php/util/XSDataSource.class.php#L94 (显示)
protected function init()
{

}

执行数据提取的准备工作 将自动在第一次提取数据前调用, 请在具体的数据源重载此函数

instance() 方法
public static XSDataSource instance(string $type, mixed $arg=NULL)
$type | string | 数据源类型, 如: mysql://.., json, csv ...
$arg | mixed | 建立对象的参数, 如 SQL 语句, JSON/CSV 文件
{return} | XSDataSource | 初始化完毕的数据源对象
源码: sdk/php/util/XSDataSource.class.php#L42 (显示)
public static function instance($type, $arg = null)
{
    $type2 = ($pos = strpos($type, ':')) ? 'database' : $type;
    $class = 'XS' . ucfirst(strtolower($type2)) . 'DataSource';
    if (!class_exists($class))
        throw new XSException("Undefined data source type: `$type2'");
    return new $class($type, $arg);
}

取得数据源对象实例


XSDatabaseDataSource

All Packages | 属性 | 方法(函数)
包 | XS.util
继承关系 | class XSDatabaseDataSource » XSDataSource
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
SQL 数据库源

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
arg |  |  | XSDataSource
type |  |  | XSDataSource

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSDataSource
getCharset() | 返回数据库输出字符集 | XSDatabaseDataSource
getData() | 从数据源中提取一条数据 | XSDataSource
instance() | 取得数据源对象实例 | XSDataSource

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
deinit() |  | XSDatabaseDataSource
getDataList() | 返回一批数据 | XSDatabaseDataSource
init() |  | XSDatabaseDataSource

方法明细

deinit() 方法
protected void deinit()
源码: sdk/php/util/XSDataSource.class.php#L218 (显示)
protected function deinit()
{
    $this->db->close();
}

getCharset() 方法
public mixed getCharset()
{return} mixed 如果数据库不支持 UTF-8 转换则返回 false
源码: sdk/php/util/XSDataSource.class.php#L136 (显示)
public function getCharset()
{
    if ($this->db->setUtf8())
        return 'UTF-8';
    return parent::getCharset();
}

返回数据库输出字符集

getDataList() 方法
protected 结果数组, getDataList()
{return} 结果数组, 没有更多数据时返回 false
源码: sdk/php/util/XSDataSource.class.php#L227 (显示)
protected function getDataList()
{
    if ($this->limit <= 0)
        return false;
    $sql = $this->sql . ' LIMIT ' . min(self::PLIMIT, $this->limit) . ' OFFSET ' . $this->offset;
    $this->limit -= self::PLIMIT;
    $this->offset += self::PLIMIT;
    return $this->db->query($sql);
}

返回一批数据

init() 方法
protected void init()
源码: sdk/php/util/XSDataSource.class.php#L143 (显示)
protected function init()
{
    if (strstr($this->type, 'sqlite'))
    {
        $pos = strpos($this->type, ':');
        $param = array('scheme' => substr($this->type, 0, $pos));
        $param['path'] = substr($this->type, $pos + (substr($this->type, $pos + 1, 2) === '//' ? 3 : 1));
    }
    else if (!($param = parse_url($this->type)))
    {
        throw new XSException('Wrong format of DB connection parameter');
    }
    else
    {
        if (isset($param['user']))
            $param['user'] = urldecode($param['user']);
        if (isset($param['pass']))
            $param['pass'] = urldecode($param['pass']);
        $param['path'] = isset($param['path']) ? trim($param['path'], '/') : '';
        if (empty($param['path']))
            throw new XSException('Not contain dbname of DB connection parameter');
        if (($pos = strpos($param['path'], '/')) === false)
            $param['dbname'] = $param['path'];
        else
        {
            $param['dbname'] = substr($param['path'], 0, $pos);
            $param['table'] = substr($param['path'], $pos + 1);
        }
    }

    // get driver
    $driver = self::getDriverName($param['scheme']);
    $class = 'XSDatabase' . ucfirst($driver);
    if (!class_exists($class))
        throw new XSException("Undefined database driver: '$driver'");
    $this->db = new $class;
    $this->db->connect($param);

    // set SQL & parse limit/offset
    $this->limit = $this->offset = 0;
    $sql = $this->arg;
    if (empty($sql))
    {
        if (!isset($param['table']))
            throw new XSException('Not specified any query SQL or db table');
        $sql = 'SELECT * FROM ' . $param['table'];
    }
    else if (preg_match('/ limit\s+(\d+)(?:\s*,\s*(\d+)|\s+offset\s+(\d+))?\s*$/i', $sql, $match))
    {
        if (isset($match[3]))  // LIMIT xxx OFFSET yyy
        {
            $this->offset = intval($match[3]);
            $this->limit = intval($match[1]);
        }
        else if (isset($match[2])) // LIMIT yyy, xxx
        {
            $this->offset = intval($match[1]);
            $this->limit = intval($match[2]);
        }
        else // lIMIT xxx
        {
            $this->limit = intval($match[1]);
        }
        $sql = substr($sql, 0, strlen($sql) - strlen($match[0]));
    }

    $this->sql = $sql;
    if ($this->limit == 0)
    {
        $sql = preg_replace('/SELECT\s+.+?FROM/i', 'SELECT COUNT(*) AS count FROM', $sql);
        $res = $this->db->query1($sql);
        $this->limit = $res['count'] - $this->offset;
    }
}


XSJsonDataSource

All Packages | 属性 | 方法(函数)
包 | XS.util
继承关系 | class XSJsonDataSource » XSDataSource
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
JSON 数据源 要求以 \n (换行符) 分割, 每行为一条完整的 json 数据

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
arg |  |  | XSDataSource
type |  |  | XSDataSource

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
__construct() | 构造函数 | XSDataSource
getCharset() | 取得数据源的准确字符集 | XSDataSource
getData() | 从数据源中提取一条数据 | XSDataSource
instance() | 取得数据源对象实例 | XSDataSource

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
deinit() |  | XSJsonDataSource
getDataList() |  | XSJsonDataSource
init() |  | XSJsonDataSource

方法明细

deinit() 方法
protected void deinit()
源码: sdk/php/util/XSDataSource.class.php#L290 (显示)
protected function deinit()
{
    if ($this->fd)
    {
        fclose($this->fd);
        $this->fd = null;
    }
}

getDataList() 方法
protected void getDataList()
源码: sdk/php/util/XSDataSource.class.php#L299 (显示)
protected function getDataList()
{
    // read line (check to timeout?)
    $line = '';
    while (true)
    {
        $buf = fgets($this->fd, 8192);
        if ($buf === false || strlen($buf) === 0)
            break;
        $line .= $buf;
        if (strlen($buf) < 8191 || substr($buf, - 1, 1) === "\n")
            break;
    }

    // empty line (end of file)
    if (empty($line))
    {
        echo "INFO: reach end of the file, total lines: " . $this->line . "\n";
        return false;
    }

    // try to decode the line
    $this->line++;
    $line = rtrim($line, "\r\n");
    if (strlen($line) === 0)
    {
        echo "WARNING: empty line #" . $this->line . "\n";
        return $this->getDataList();
    }

    $item = json_decode($line, true);
    if (!is_array($item) || count($item) === 0)
    {
        switch (json_last_error())
        {
            case JSON_ERROR_DEPTH:
                $error = ' - Maximum stack depth exceeded';
                break;
            case JSON_ERROR_CTRL_CHAR:
                $error = ' - Unexpected control character found';
                break;
            case JSON_ERROR_SYNTAX:
                $error = ' - Syntax error, malformed JSON';
                break;
            default :
                $error = (count($item) === 0 ? ' - Empty array' : '');
                break;
        }
        echo "WARNING: invalid line #" . $this->line . $error . "\n";
        return $this->getDataList();
    }
    return array($item);
}

init() 方法
protected void init()
源码: sdk/php/util/XSDataSource.class.php#L277 (显示)
protected function init()
{
    $file = $this->arg;
    if (empty($file))
    {
        echo "WARNING: input file not specified, read data from <STDIN>\n";
        $file = 'php://stdin';
    }
    if (!($this->fd = fopen($file, 'r')))
        throw new XSException("Can not open input file: '$file'");
    $this->line = 0;
}


XSUtil

All Packages | 方法(函数)
包 | XS.util
继承关系 | class XSUtil
版本 | 1.0.0
源代码 | sdk/php/util/XSUtil.class.php
XSUtil 工具程序通用代码

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
convertIn() | 把用户输入的字符串转换为 UTF-8 编码 | XSUtil
convertOut() | 把 UTF-8 字符串转换为用户编码 | XSUtil
fixWidth() | 修正字符串至固定宽度 | XSUtil
getOpt() | 取得命令行参数 | XSUtil
parseOpt() | 解析命令行参数 | XSUtil
setCharset() | 设置输出、输入编码 | XSUtil

方法明细

convertIn() 方法
public static string convertIn(string $buf)
$buf | string | 要转换字符串
{return} | string | 转换后的字符串
源码: sdk/php/util/XSUtil.class.php#L78 (显示)
public static function convertIn($buf)
{
    if (self::$charset !== null)
        return XS::convert($buf, 'UTF-8', self::$charset);
    return $buf;
}

把用户输入的字符串转换为 UTF-8 编码

convertOut() 方法
public static string convertOut(string $buf)
$buf | string | 要转换字符串
{return} | string | 转换后的字符串
源码: sdk/php/util/XSUtil.class.php#L66 (显示)
public static function convertOut($buf)
{
    if (self::$charset !== null)
        return XS::convert($buf, self::$charset, 'UTF-8');
    return $buf;
}

把 UTF-8 字符串转换为用户编码

fixWidth() 方法
public static type fixWidth(string $text, int $size, string $pad=' ')
$text | string | 要修正的字符串
$size | int | 修正的目标宽度
$pad | string | 用于填充补足的字符
{return} | type |
源码: sdk/php/util/XSUtil.class.php#L32 (显示)
public static function fixWidth($text, $size, $pad = ' ')
{
    for ($i = $j = 0; $i < strlen($text) && $j < $size; $i++, $j++)
    {
        if ((ord($text[$i]) & 0xe0) == 0xe0)
        {
            if (($size - $j) == 1)
                break;
            $j++;
            $i += 2;
        }
    }
    return substr($text, 0, $i) . str_repeat($pad, $size - $j);
}

修正字符串至固定宽度 其中一个全角符号、汉字的宽度为半角字符的 2 倍。

getOpt() 方法
public static string getOpt(string $short, string $long=NULL, bool $extra=false)
$short | string | 短参数名
$long | string | 长参数名
$extra | bool | 是否补用默认顺序的参数
{return} | string | 返回可用的参数值，若不存在则返回 null
源码: sdk/php/util/XSUtil.class.php#L157 (显示)
public static function getOpt($short, $long = null, $extra = false)
{
    if (self::$options === null)
        self::parseOpt();

    $value = null;
    $options = self::$options;
    if ($long !== null && isset($options[$long]))
        $value = $options[$long];
    else if ($short !== null && isset($options[$short]))
        $value = $options[$short];
    else if ($extra === true && isset($options['-'][self::$optind]))
    {
        $value = $options['-'][self::$optind];
        self::$optind++;
    }
    return $value;
}

取得命令行参数 要求事先调用 parseOpt, 否则会自动以默认参数调用它。

参见

  • parseOpt
parseOpt() 方法
public static array parseOpt(array $valued=array ( ))
$valued | array | 需要附加值的参数列表
{return} | array | 解析完的参数数组，未指定 - 开头的选项统一放入 '-' 的子数组
源码: sdk/php/util/XSUtil.class.php#L90 (显示)
public static function parseOpt($valued = array())
{
    $result = array('-' => array());
    $params = isset($_SERVER['argv']) ? $_SERVER['argv'] : array();
    for ($i = 0; $i < count($params); $i++)
    {
        if ($params[$i] === '--')
        {
            for ($i = $i + 1; $i < count($params); $i++)
                $result['-'][] = $params[$i];
            break;
        }
        else if ($params[$i][0] === '-')
        {
            $value = true;
            $pname = substr($params[$i], 1);
            if ($pname[0] === '-')
            {
                $pname = substr($pname, 1);
                if (($pos = strpos($pname, '=')) !== false)
                {
                    $value = substr($pname, $pos + 1);
                    $pname = substr($pname, 0, $pos);
                }
            }
            else if (strlen($pname) > 1)
            {
                for ($j = 1; $j < strlen($params[$i]); $j++)
                {
                    $pname = substr($params[$i], $j, 1);
                    if (in_array($pname, $valued))
                    {
                        $value = substr($params[$i], $j + 1);
                        break;
                    }
                    else if (($j + 1) != strlen($params[$i]))
                    {
                        $result[$pname] = true;
                    }
                }
            }
            if ($value === true && in_array($pname, $valued) && isset($params[$i + 1]))
            {
                $value = $params[$i + 1];
                $i++;
            }
            $result[$pname] = $value;
        }
        else
        {
            $result['-'][] = $params[$i];
        }
    }
    self::$options = $result;
    self::$optind = 1;
    return $result;
}

解析命令行参数

setCharset() 方法
public static void setCharset(string $charset)
$charset string 期望得到的字符集
源码: sdk/php/util/XSUtil.class.php#L52 (显示)
public static function setCharset($charset)
{
    if ($charset !== null && strcasecmp($charset, 'utf8') && strcasecmp($charset, 'utf-8'))
    {
        self::$charset = $charset;
        ob_start(array(__CLASS__, 'convertOut'));
    }
}

设置输出、输入编码 默认输出的中文编码均为 UTF-8


XSDatabase

All Packages | 方法(函数)
包 | XS.util.db
继承关系 | abstract class XSDatabase
子类 | XSDatabaseMySQL, XSDatabaseMySQLI, XSDatabasePDO, XSDatabaseSQLite, XSDatabaseSQLite3
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
数据库操作基类 定义了 SQL 数据库源的四个基本操作: connect/query/close/setUtf8

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库连接 | XSDatabase
connect() | 连接数据库 | XSDatabase
query() | 查询 SQL 语句 | XSDatabase
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 设置数据库字符集为 UTF-8 | XSDatabase

方法明细

close() 方法
abstract public void close()
源码: sdk/php/util/XSDataSource.class.php#L428 (显示)
abstract public function close();

关闭数据库连接

connect() 方法
abstract public void connect(array $param)
$param array 连接参数, 采用 parse_url 解析, 可能包含: scheme,user,pass,host,path,table,dbname ...
源码: sdk/php/util/XSDataSource.class.php#L423 (显示)
abstract public function connect($param);

连接数据库

query() 方法
abstract public mixed query($sql)
$sql |  |
{return} | mixed | 非 SELECT 语句返回执行结果(true/false), SELECT 语句返回所有结果行的数组
源码: sdk/php/util/XSDataSource.class.php#L434 (显示)
abstract public function query($sql);

查询 SQL 语句

query1() 方法
public 查询结果首行, query1(string $sql)
$sql | string |
{return} | 查询结果首行, | 失败或无数据则返回 false
源码: sdk/php/util/XSDataSource.class.php#L450 (显示)
public function query1($sql)
{
    $sql = preg_replace('/ limit\s+(\d+)(?:\s*,\s*(\d+)|\s+offset\s+(\d+))?\s*$/i', '', $sql);
    $sql .= ' LIMIT 1';
    $res = $this->query($sql);
    return (is_array($res) && isset($res[0])) ? $res[0] : false;
}

查询数据库首行

setUtf8() 方法
public bool setUtf8()
{return} bool 如果数据库能直接输出 UTF-8 编码则返回 true 否则返回 false
源码: sdk/php/util/XSDataSource.class.php#L440 (显示)
public function setUtf8()
{
    return false;
}

设置数据库字符集为 UTF-8


XSDatabaseMySQL

All Packages | 方法(函数)
包 | XS.util.db
继承关系 | class XSDatabaseMySQL » XSDatabase
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
使用传统 MySQL 扩展

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库连接 | XSDatabaseMySQL
connect() | 连接数据库 | XSDatabaseMySQL
query() | 执行 SQL 语句查询 | XSDatabaseMySQL
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 将输出字符集设置为 UTF-8 | XSDatabaseMySQL

方法明细

close() 方法
public void close()
源码: sdk/php/util/XSDataSource.class.php#L493 (显示)
public function close()
{
    if ($this->link)
    {
        mysql_close($this->link);
        $this->link = null;
    }
}

关闭数据库连接

connect() 方法
public void connect(array $param)
$param array 连接参数, 包含: user,pass,host,table,dbname ...
源码: sdk/php/util/XSDataSource.class.php#L474 (显示)
public function connect($param)
{
    $host = isset($param['host']) ? $param['host'] : ini_get('mysql.default_host');
    $host .= ( (isset($param['port']) && $param['port'] != 3306) ? ':' . $param['port'] : '');
    $user = isset($param['user']) ? $param['user'] : ini_get('mysql.default_user');
    $pass = isset($param['pass']) ? $param['pass'] : ini_get('mysql.default_pw');
    if (($this->link = mysql_connect($host, $user, $pass)) === false)
        throw new XSException("Can not connect to mysql server: '$uesr@$host'");
    if (!mysql_select_db($param['dbname'], $this->link))
    {
        $this->close();
        throw new XSException("Can not switch to database name: '{$param['dbname']}'");
    }
    $this->setUtf8();
}

连接数据库

query() 方法
public mixed query(string $sql)
$sql | string | 要执行的 SQL 语句
{return} | mixed |
源码: sdk/php/util/XSDataSource.class.php#L507 (显示)
public function query($sql)
{
    //echo "[DEBUG] SQL: $sql\n";
    $res = mysql_query($sql, $this->link);
    if ($res === false)
        throw new XSException('MySQL ERROR(#' . mysql_errno($this->link) . '): ' . mysql_error($this->link));
    if (!is_resource($res))
        $ret = $res;
    else
    {
        $ret = array();
        while ($tmp = mysql_fetch_assoc($res))
        {
            $ret[] = $tmp;
        }
        mysql_free_result($res);
    }
    return $ret;
}

执行 SQL 语句查询

setUtf8() 方法
public bool setUtf8()
{return} bool MySQL 自 4.1.0 起支持字符集
源码: sdk/php/util/XSDataSource.class.php#L531 (显示)
public function setUtf8()
{
    if (version_compare(mysql_get_server_info($this->link), '4.1.0', '>='))
        return @mysql_query("SET NAMES 'utf8'", $this->link);
    return false;
}

将输出字符集设置为 UTF-8


XSDatabaseMySQLI

All Packages | 方法(函数)
包 | XS.util.db
继承关系 | class XSDatabaseMySQLI » XSDatabase
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
面向对象的 MySQLI 扩展

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库连接 | XSDatabaseMySQLI
connect() | 连接数据库 | XSDatabaseMySQLI
query() | 执行 SQL 语句查询 | XSDatabaseMySQLI
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 将输出字符集设置为 UTF-8 | XSDatabaseMySQLI

方法明细

close() 方法
public void close()
源码: sdk/php/util/XSDataSource.class.php#L574 (显示)
public function close()
{
    if ($this->obj)
    {
        $this->obj->close();
        $this->obj = null;
    }
}

关闭数据库连接

connect() 方法
public void connect(array $param)
$param array 连接参数, 包含: user,pass,host,table,dbname ...
源码: sdk/php/util/XSDataSource.class.php#L554 (显示)
public function connect($param)
{
    $host = isset($param['host']) ? $param['host'] : ini_get('mysqli.default_host');
    $user = isset($param['user']) ? $param['user'] : ini_get('mysqli.default_user');
    $pass = isset($param['pass']) ? $param['pass'] : ini_get('mysqli.default_pw');
    $port = isset($param['port']) ? $param['port'] : ini_get('mysqli.default_port');
    $this->obj = new mysqli($host, $user, $pass, '', $port);
    if ($this->obj->connect_error)
        throw new XSException("Can not connect to mysql server: '$uesr@$host'");
    if (!$this->obj->select_db($param['dbname']))
    {
        $this->close();
        throw new XSException("Can not switch to database name: '{$param['dbname']}'");
    }
    $this->setUtf8();
}

连接数据库

query() 方法
public mixed query(string $sql)
$sql | string | 要执行的 SQL 语句
{return} | mixed |
源码: sdk/php/util/XSDataSource.class.php#L588 (显示)
public function query($sql)
{
    //echo "[DEBUG] SQL: $sql\n";
    $res = $this->obj->query($sql);
    if ($res === false)
        throw new XSException('MySQL ERROR(#' . $this->obj->error . '): ' . $this->obj->errno);
    if (!is_object($res))
        $ret = $res;
    else
    {
        $ret = array();
        while ($tmp = $res->fetch_assoc())
        {
            $ret[] = $tmp;
        }
        $res->free();
    }
    return $ret;
}

执行 SQL 语句查询

setUtf8() 方法
public bool setUtf8()
{return} bool 始终返回 true
源码: sdk/php/util/XSDataSource.class.php#L612 (显示)
public function setUtf8()
{
    $this->obj->set_charset('utf8');
    return true;
}

将输出字符集设置为 UTF-8


XSDatabasePDO

All Packages | 属性 | 方法(函数)
包 | XS.util.db
继承关系 | abstract class XSDatabasePDO » XSDatabase
子类 | XSDatabasePDO_MySQL, XSDatabasePDO_SQLite
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
面向对象的 PDO 扩展基类

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
obj |  |  | XSDatabasePDO

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库 | XSDatabasePDO
connect() | 连接数据库 | XSDatabasePDO
query() | 执行 SQL 语句 | XSDatabasePDO
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 设置数据库字符集为 UTF-8 | XSDatabase

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
makeDsn() | 提取参数内容生成 PDO 连接专用的 DSN | XSDatabasePDO

属性明细

obj 属性
protected $obj;

方法明细

close() 方法
public void close()
源码: sdk/php/util/XSDataSource.class.php#L777 (显示)
public function close()
{
    $this->obj = null;
}

关闭数据库

connect() 方法
public void connect(array $param)
$param array 连接参数, 包含: user, pass ...
源码: sdk/php/util/XSDataSource.class.php#L759 (显示)
public function connect($param)
{
    $dsn = $this->makeDsn($param);
    $user = isset($param['user']) ? $param['user'] : 'root';
    $pass = isset($param['pass']) ? $param['pass'] : '';
    try
    {
        $this->obj = new PDO($dsn, $user, $pass);
    }
    catch (PDOException $e)
    {
        throw new XSException($e->getMessage());
    }
}

连接数据库 具体的每个类必须实现 makeDsn 来将参数转换为 dsn

参见

  • makeDsn
makeDsn() 方法
abstract protected void makeDsn(array $param)
$param array
源码: sdk/php/util/XSDataSource.class.php#L804 (显示)
abstract protected function makeDsn($param);

提取参数内容生成 PDO 连接专用的 DSN

query() 方法
public mixed query(string $sql)
$sql | string | 要执行的 SQL 语句
{return} | mixed |
源码: sdk/php/util/XSDataSource.class.php#L787 (显示)
public function query($sql)
{
    //echo "[DEBUG] SQL: $sql\n";
    $res = $this->obj->query($sql);
    if ($res === false)
    {
        $info = $this->obj->errorInfo();
        throw new XSException('SQLSTATE[' . $info[0] . '] [' . $info[1] . '] ' . $info[2]);
    }
    $ret = $res->fetchAll(PDO::FETCH_ASSOC);
    return $ret;
}

执行 SQL 语句


XSDatabasePDO_MySQL

All Packages | 属性 | 方法(函数)
包 | XS.util.db
继承关系 | class XSDatabasePDO_MySQL » XSDatabasePDO » XSDatabase
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
PDO.MySQL 实现

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
obj |  |  | XSDatabasePDO

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库 | XSDatabasePDO
connect() | 连接数据库 | XSDatabasePDO
query() | 执行 SQL 语句 | XSDatabasePDO
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 将输出字符集设置为 UTF-8 | XSDatabasePDO_MySQL

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
makeDsn() | 生成 MySQL DSN | XSDatabasePDO_MySQL

方法明细

makeDsn() 方法
protected string makeDsn(array $param)
$param | array | 包含 host, port, dbname
{return} | string |
源码: sdk/php/util/XSDataSource.class.php#L822 (显示)
protected function makeDsn($param)
{
    $dsn = 'mysql:host=' . (isset($param['host']) ? $param['host'] : 'localhost');
    if (isset($param['port']) && $param['port'] != 3306)
        $dsn .= ';port=' . $param['port'];
    $dsn .= ';dbname=' . $param['dbname'];
    return $dsn;
}

生成 MySQL DSN

setUtf8() 方法
public bool setUtf8()
{return} bool 始终返回 true
源码: sdk/php/util/XSDataSource.class.php#L835 (显示)
public function setUtf8()
{
    // BUGFIXED: 感谢 tina 网友协助测试, 此处将 utf8 改为 utf-8 提高兼容性
    return $this->obj->prepare("SET NAMES 'utf-8'")->execute();
}

将输出字符集设置为 UTF-8


XSDatabasePDO_SQLite

All Packages | 属性 | 方法(函数)
包 | XS.util.db
继承关系 | class XSDatabasePDO_SQLite » XSDatabasePDO » XSDatabase
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
PDO.SQLite 实现

Protected 属性

隐去继承来的属性

名称 | 类型 | 描述 | 定义于
obj |  |  | XSDatabasePDO

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库 | XSDatabasePDO
connect() | 连接数据库 | XSDatabasePDO
query() | 执行 SQL 语句 | XSDatabasePDO
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 设置数据库字符集为 UTF-8 | XSDatabase

Protected 方法

隐去继承来的方法

名称 | 描述 | 定义于
makeDsn() | 生成 SQLite DSN | XSDatabasePDO_SQLite

方法明细

makeDsn() 方法
protected string makeDsn(array $param)
$param | array | 包含 path 为数据库路径
{return} | string |
源码: sdk/php/util/XSDataSource.class.php#L857 (显示)
protected function makeDsn($param)
{
    $dsn = 'sqlite:' . $param['path'];
    return $dsn;
}

生成 SQLite DSN


XSDatabaseSQLite

All Packages | 方法(函数)
包 | XS.util.db
继承关系 | class XSDatabaseSQLite » XSDatabase
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
使用传统的 SQLite 扩展

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库 | XSDatabaseSQLite
connect() | 打开数据库 | XSDatabaseSQLite
query() | 执行 SQL 语句查询 | XSDatabaseSQLite
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 设置数据库字符集为 UTF-8 | XSDatabase

方法明细

close() 方法
public void close()
源码: sdk/php/util/XSDataSource.class.php#L643 (显示)
public function close()
{
    if ($this->link)
    {
        sqlite_close($this->link);
        $this->link = null;
    }
}

关闭数据库

connect() 方法
public void connect(array $param)
$param array 连接参数, 包含: path
源码: sdk/php/util/XSDataSource.class.php#L634 (显示)
public function connect($param)
{
    if (($this->link = sqlite_open($param['path'])) === false)
        throw new XSException("Can not open sqlite file: '{$param['path']}'");
}

打开数据库

query() 方法
public mixed query(string $sql)
$sql | string | 要执行的 SQL 语句
{return} | mixed |
源码: sdk/php/util/XSDataSource.class.php#L657 (显示)
public function query($sql)
{
    //echo "[DEBUG] SQL: $sql\n";
    $res = sqlite_query($this->link, $sql);
    if ($res === false)
        throw new XSException('SQLITE ERROR: ' . sqlite_error_string($this->link));
    if (!is_resource($res))
        $ret = $res;
    else
    {
        $ret = array();
        while ($tmp = sqlite_fetch_array($res, SQLITE_ASSOC))
        {
            $ret[] = $tmp;
        }
    }
    return $ret;
}

执行 SQL 语句查询


XSDatabaseSQLite3

All Packages | 方法(函数)
包 | XS.util.db
继承关系 | class XSDatabaseSQLite3 » XSDatabase
版本 | 1.0.0
源代码 | sdk/php/util/XSDataSource.class.php
面向对象的 SQLite3 扩展

Public 方法

隐去继承来的方法

名称 | 描述 | 定义于
close() | 关闭数据库 | XSDatabaseSQLite3
connect() | 打开数据库 | XSDatabaseSQLite3
query() | 执行 SQL 语句查询 | XSDatabaseSQLite3
query1() | 查询数据库首行 | XSDatabase
setUtf8() | 设置数据库字符集为 UTF-8 | XSDatabase

方法明细

close() 方法
public void close()
源码: sdk/php/util/XSDataSource.class.php#L707 (显示)
public function close()
{
    if ($this->obj)
    {
        $this->obj->close();
        $this->obj = null;
    }
}

关闭数据库

connect() 方法
public void connect(array $param)
$param array 连接参数, 包含: path
源码: sdk/php/util/XSDataSource.class.php#L692 (显示)
public function connect($param)
{
    try
    {
        $this->obj = new SQLite3($param['path'], SQLITE3_OPEN_READONLY);
    }
    catch (Exception $e)
    {
        throw new XSException($e->getMessage());
    }
}

打开数据库

query() 方法
public mixed query(string $sql)
$sql | string | 要执行的 SQL 语句
{return} | mixed |
源码: sdk/php/util/XSDataSource.class.php#L721 (显示)
public function query($sql)
{
    //echo "[DEBUG] SQL: $sql\n";
    $res = $this->obj->query($sql);
    if ($res === false)
        throw new XSException('SQLITE3 ERROR(#' . $this->obj->lastErrorCode() . '): ' . $this->obj->lastErrorMsg());
    if (!is_object($res))
        $ret = $res;
    else
    {
        $ret = array();
        while ($tmp = $res->fetchArray(SQLITE3_ASSOC))
        {
            $ret[] = $tmp;
        }
        $res->finalize();
    }
    return $ret;
}

执行 SQL 语句查询


关于 xunsearch

Xunsearch 是一个高性能、全功能的全文检索解决方案。

Xunsearch 旨在帮助一般开发者针对既有的海量数据，快速而方便地建立自己的全文搜索引擎。

Xunsearch 中文译名为“迅搜”，代码中的经常被缩写为 XS，既是英文名称的缩略也是中文声母缩写。 这儿的“迅”是快速的意思，至少包含了两层涵义：其一代表了搜索结果的响应能力，其二则为二次开发难度、速度。

Xunsearch 采用结构化分层设计，包含后端服务、前端开发包两大部分，层次清晰而不交叉。 其中后端是采用 C/C++ 编写的守护进程，而前端采用最为流行的脚本语言 PHP ，对于 web 搜索项目更为方便。 具体参见架构设计。

Xunsearch 极大程度降低的搜索开发的难度，除了常规的中文分词、字段检索、布尔语法等功能外， 还比其它免费的解决方案提供了用户急需的相关搜索、拼音搜索、结果高亮、搜索建议等等。 具体的清单请参见我们的功能列表。

Xunsearch 真正全面开源，并使用最流行的开源许可协议 GPL 发布。您可以免费获取本项目的全部源代码， 自由的使用它，并在许可条件下修改和再分发，具体参见授权声明文件。

1. 应用领域?

前提是要有一定的 PHP (或其它对应的 SDK 语言) 开发能力，并备有 Unix 类型操作系统的服务器至少一台。

Xunsearch 可以帮助您建立各种行业门户/垂直搜索、BBS 论坛搜索、CMS/Web站内搜索、文档/文献资料检索， 以及各种基于现有数据库系统的全文检索。

2. 历史渊源?

大约在 2006 年创始人 hightman 开始了全文检索的相关探索，先是发布了纯 PHP 实现的中文分词代码， 之后又推出了 MySQL/MyISAM 全文索引的中文分词补丁 MyFT。经过一系列不断完善和改进后，于 2007 年正式发布中文分词项目 scws 并一直维护至今。

起初开发分词的目的就是为了解决中文的全文检索，全文检索技术虽然已经趋于成熟，但多为商业产品。免费开源、 并且要好用的还真难找了，于是萌动了自己开发全文检索的念头。2008 年推出了比较粗糙的搜索解决方案 FTPHP， 之后于 2010 年又开启了 Xungle 项目，直接免去各种技术需求，为用户提供包含软硬环境在内的搜索服务。

在 Xungle 项目的运作过程中，我们发现还有大量的用户出于各种原因，不得不自行解决搜索技术。此外， 我们发现了国外一个优秀的开源搜索引擎基础库 Xapian，它采用 C/C++ 开发，并且性能卓越，久经考验。 但是并没有整合中文分词，并且开发难度、复杂度也较大。于是我们下决心整合 Xapian 和 Scws，并在此基础上 提供更多、更常用的搜索功能。这就是 Xunsearch ，目前版本发布正在准备中，代码也已托管到 github 中。

3. 开发团队?

本项目由杭州云圣网络科技公司创立并维护，同时也是唯一就 Xunsearch 提供相应的商业技术服务、支持的公司。

4. 致谢?

是大量 xungle 用户的提议促使我们开启 xunsearch 项目，Xunsearch 的往后发展将离不开广大用户的支持， 还要特别感谢在此其间义务参与 xunsearch 内测的一些朋友。

Xunsearch 底层采用 C/C++ 编写，其中用到了许多其它优秀项目，在线特别感谢它们的团队和作者。 主要有以下几个：

  • xapian-core 这是 Xunsearch 的底层索引设计方案
  • scws 默认内置的中文分词解决方案
  • libevent 后端服务器的事件处理模型
  • nginx 搜索服务器的设计借鉴了 nginx 的方式，采用多进程、多线程混合用于处理高并发请求
  • Yii 非常优秀的 PHP 开发框架，xunsearch 官网采用该框架，文档组织形式也参考自 Yii
$Id$


特色和优势

我们追求的最大特色是：快，搜索响应快，开发上手快。

  1. 支持海量数据，高速搜索响应，敬请参见 Xapian 里的 Scalability。 据描述单库最多支持 40 亿条数据，在 500 万网页大约 1.5TB 的数据中，非缓存情况下检索时间不超过 1 秒。

  2. 健壮的后端守护程序，内置缓存设计，事件模型基于 libevent。

  3. 内置专为搜索而自主开发的 scws 中文分词，搜索效果好，又能保障查全率。

  4. 后端采用稳定高效的 C/C++ 开发，前端采用流行的 PHP 脚本语言，堪称最佳组合。

  5. 极低的开发难度，接口简单易用，而且文档规范、全面。

  6. 与 Lucene, Sphinx 之类相比较，Xunsearch 提供了更丰富而必需的功能，开发周期更短。

  7. 功能强大，内置了大量只有商业、大型搜索引擎才提供的功能。支持包括字段检索、结果高亮、 字段排序、布尔语法、区间检索、聚合搜索、相关搜索、权重微调、拼音搜索、 搜索建议等等专业搜索引擎具备的功能。

$Id$


Xunsearch 架构简图

Xunsearch 分为后端服务和前端开发包两大部分，这两个部分允许部署在不同服务器中。

后端是采用 C/C++ 开发的守护进程，包括索引服务器(xs-indexd)、搜索服务器(xs-searchd)。 索引服务器用于集中处理索引变动，并自动调用工具程序优化和更新数据库；搜索服务器 借鉴了 nginx 的作法，使用进程、线程混合工作模式处理高并发的搜索请求。

通常在二次开发时并不需要去关心后端的实现，只要在我们提供的前端开发包基础上开发即可。

前端开发包通常使用脚本语言编写，负责协商前端调用与后端服务通讯，我们称之为 SDK 开发包。理论上支持各种包含 socket 通讯实现的脚本语言，但目前我们只提供了 PHP 语言，以后再陆续考虑和开发其它语言。非常欢迎有能力有意愿的朋友提供、贡献其它语种的代码。

下面是我们用字符直接制作的一张架构简图。

              ==============================
              |   Your Search Application  |
              ==============================
                  /\                /\
................ /||\ ............ /||\ ....................
:                 ||                ||                     :
:             +-----------------------------+              :
:             |  [PHP]  | other lang (TODO) |              :
:             |-----------------------------|              :
:             |   SDK: (xunsearch devkit)   |              :
:             +-----------------------------+              :
:                 /                   \                    :
:                /                     \                   :
:    +-----------------+          +-------------------+    :
:    |  Index-server   |          |   Search-server   |    :
:    |-----------------|          |-------------------|    :
:    |   xs-indexd     |          |    xs-searchd     |    :
:    |   xs-import     |          |     1*master      |    :
:    |   xs-logging    |          | N*worker(M*thread)|    :
:    +-----------------+          +-------------------+    :
:                |                       |                 :
:                |                       |                 :
:             +-----------------------------+              :
:             |     Xapian-core + scws      |              :
:             +-----------------------------+              :
:                |          |            |                 :
:      +------------+  +--------+  +---------------+       :
:      | Local Disk |  | Memory |  | Remote socket |       :
:      +------------+  +--------+  +---------------+       :
:                                                          :
...................... Xun Search ..........................
$Id$


下载 Xunsearch

Xunsearch 真正全面开源，并使用最流行的开源许可协议 GPL 发布。您可以免费获取本项目的全部源代码， 自由的使用它，并在许可条件下修改和再分发，具体参见授权声明。

  • 稳定版(stable) 意味着您可以安心的将它应用于您的生产环境中。
  • 测试版(beta) 表示这个版本用于测试收集问题进行改进，不推荐应用于生产环境。
  • 开发版(devel) 表示这个版本正处于不断开发修改中，不推荐应用于生产环境。
 | Xunsearch-Full (完整安装包)
最新稳定版 | xunsearch-full-latest.tar.bz2 ($RELEASE，修改日志)
最新测试版 | xunsearch-full-$BETA0.tar.bz2 (测试版：$BETA，CHM手册 修改日志)
文档 | CHM 格式 、在线阅读 、安装说明
终止维护 | 2013-12-31 (活跃开发中，视情况进行延长)
最新开发版 | xunsearch-full-dev.tar.bz2 (开发版：$VERSION，修改日志)
GIT 代码仓库 | http://github.com/hightman/xunsearch
历史版本 | 暂无 下载以往版本

从 Github 下载源码

我们的源代码托管在免费的 github 服务上，最方便的浏览源码方式是直接通过浏览器访问它所内置的代码浏览器。

如果您了解并熟悉 git 您可以用下面的任意一种方式导出 xunsearch 源代码：

git clone git://github.com/hightman/xunsearch.git
git clone https://github.com/hightman/xunsearch.git

Note: 特别提醒的是 git 代码仓库可能包含尚未完成的功能或问题，请勿将此代码用于生产环境。 如果您打算使用这些代码，请先参考论坛相关文章。

$Id$


商业服务与支持

Xunsearch 本身是一个通用的搜索引擎软件，并以免费开源的方式提供下载、使用， 但仍需备有相应的硬件环境、相关的技术人员进行二次开发。

我们提供了好几种商业服务与支持，帮助您完成个性需求定制开发、大规模搜索引擎架构等。

1. 定制开发服务?

  • 如果您没有足够的开发人员或专门的搜索团队，请找我们提供支持。

  • 如果您需要构建大规模的搜索引擎，那么需要进行相应的分布式设计、架构，请找我们提供支持。

  • 如果您有超出 xunsearch 现有功能的更高需求，请找我们提供支持。

  • 联系 Email 地址：support@xunsearch.com

2. 搜索托管服务?

假如您甚至没有自己的服务器，我们的兄弟项目 xungle 专业提供搜索服务。 您可以把整个搜索项目托管在我们的服务器上运行。

$Id$


XunSearch 授权许可证

$Id$

Xunsearch (包含 SDK 在内) 是一个免费开源的全文搜索软件，在以下描述的 GPL 通用许可证的条款下发布。

Copyright (C) 2011, 著作权由杭州云圣网络科技有限公司所有。 著作权人保留一切相关权利。

Copyright (C) 2010-2011 by Hangzhou YunSheng Network Technology Co., Ltd. All rights reserved.

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License long with this program. If not, see http://www.gnu.org/licenses/.

1. 官方提供、用户贡献的文档许可证?

官方提供以及用户贡献的文档内容以 GNU 自由文档许可证 (GFDL) 向公众授权。 在一般情况下，您可以自由的拷贝、修改、再发布　Xunsearch 文档内容，只要您赋予新版本同样的条款， 并保留文档作者署名。（在文章中添加链接指向原作者，或标明文档出处）。

您可以在 GNU 自由文档许可证 1.2 版或由自由软件基金会发布的更新版本条款下拷贝、分布或修改这些文档， 无固定段落，无封面文字，无封底文字。

2. 第三方组件的许可证?

软件包中包含的其它依赖或组件则保持其原有的方式授权，著作权也归原始作者所有。

  • libevent BSD 协议
  • xapian-core GPL 协议
  • scws-1.x.y 未明确，但与 BSD 协议较为相似


文档资料

Xunsearch 包含了后端服务、前端开发包（即 SDK）两大部分，在开发过程中几乎不需要关注后端， 所以我们提供的文档大部分都是 SDK 文档，用于二次开发。

1. 开始&安装?

如果您第一次接触 xunsearch，请先阅读安装说明，在您的主机上先安装一份完整的 xunsearch。

2. 权威指南?

这里是学习和入门的必经之路，它全面介绍相对应的 SDK　的用法，是学习和掌握 xunsearch 的权威资料。点此进入 PHP-SDK 权威指南。

3. API 文档?

这儿是由程序自动从代码中提取并生成的 API 文档，在您读完权威指南并着手开始自己的搜索引擎时， 您可以从这儿读取到详细的 API 用法及说明，包含了每个类对象的属性、方法介绍。PHP-SDK API 文档。

4. Wiki 技巧、经验?

这儿是由用户和我们共同编写的 xunsearch 使用技巧，常常会针对一些常见的需求进行讲解和分析。 如果您在为一项功能疑惑或不解，请尝试在这里找到您需要的答案。

5. 其他资源?

如果您还有任何其它问题或建议、需求，请到论坛发贴，我们或热心网友会作出解答。

$Id$