全文检索技术---Lucene
1 Lucene介绍
1.1 什么是Lucene
Lucene是apache下的一个开源的全文检索引擎工具包。它为软件开发人员提供一个简单易用的工具包(类库),以方便的在目标系统中实现全文检索的功能。
1.2 全文检索的应用场景
1.2.1 搜索引擎

©注意:
Lucene和搜索引擎是不同的,Lucene是一套用java或其它语言写的全文检索的工具包。它为应用程序提供了很多个api接口去调用,可以简单理解为是一套实现全文检索的类库。搜索引擎是一个全文检索系统,它是一个单独运行的软件系统。
1.2.2 站内搜索(关注)

1.3 全文检索定义
全文检索首先将要查询的目标文档中的词提取出来,组成索引,通过查询索引达到搜索目标文档的目的。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
2 Lucene实现全文检索的流程

全文检索的流程分为两大部分:索引流程、搜索流程。
- 索引流程:即采集数据à构建文档对象à分析文档(分词)à创建索引。
- 搜索流程:即用户通过搜索界面à创建查询à执行搜索,搜索器从索引库搜索à渲染搜索结果。
3 入门程序
3.1.1 第一步:添加jar包
入门程序只需要添加以下jar包:
- mysql5.1驱动包:mysql-connector-java-5.1.7-bin.jar
- 核心包:lucene-core-4.10.3.jar
- 分析器通用包:lucene-analyzers-common-4.10.3.jar
- 查询解析器包:lucene-queryparser-4.10.3.jar
3.1.2 PO,DAO以及测试代码

package cn.xjy.po ;
public class Book {
// 图书ID
private Integer id ;
// 图书名称
private String name ;
// 图书价格
private Float price ;
// 图书图片
private String pic ;
// 图书描述
private String description ;
public Book() {}
public Book(Integer id, String name, Float price, String pic, String description) {
super() ;
this.id = id ;
this.name = name ;
this.price = price ;
this.pic = pic ;
this.description = description ;
}
public Integer getId() {
return id ;
}
public void setId(Integer id) {
this.id = id ;
}
public String getName() {
return name ;
}
public void setName(String name) {
this.name = name ;
}
public Float getPrice() {
return price ;
}
public void setPrice(Float price) {
this.price = price ;
}
public String getPic() {
return pic ;
}
public void setPic(String pic) {
this.pic = pic ;
}
public String getDescription() {
return description ;
}
public void setDescription(String description) {
this.description = description ;
}
@Override
public String toString() {
return "Book [id=" + id + ", name=" + name + ", price=" + price + ", pic=" + pic
+ ", description=" + description + "]" ;
}
}
package cn.xjy.dao ; import java.sql.Connection ;
import java.sql.DriverManager ;
import java.sql.PreparedStatement ;
import java.sql.ResultSet ;
import java.util.ArrayList ;
import java.util.List ;
import cn.xjy.po.Book ; public class BookDaoImpl implements BookDao { @Override
public List<Book> getBooks() {
List<Book> books = new ArrayList<Book>() ; try {
Class.forName("com.mysql.jdbc.Driver") ;
Connection con = DriverManager.getConnection("jdbc:mysql:///luncene", "root", "root") ;
PreparedStatement statement = con.prepareStatement("select * from book") ;
ResultSet resultSet = statement.executeQuery() ;
while (resultSet.next()) {
Book book = new Book(resultSet.getInt("id"), resultSet.getString("name"),
resultSet.getFloat("price"), resultSet.getString("pic"),
resultSet.getString("description")) ;
books.add(book) ;
}
} catch (Exception e) {
e.printStackTrace() ;
} return books ;
} }
package cn.xjy.lucene ; import java.io.File ;
import java.util.ArrayList ;
import java.util.List ;
import org.apache.lucene.analysis.Analyzer ;
import org.apache.lucene.analysis.standard.StandardAnalyzer ;
import org.apache.lucene.document.Document ;
import org.apache.lucene.document.Field ;
import org.apache.lucene.document.Field.Store ;
import org.apache.lucene.document.FloatField ;
import org.apache.lucene.document.IntField ;
import org.apache.lucene.document.TextField ;
import org.apache.lucene.index.DirectoryReader ;
import org.apache.lucene.index.IndexReader ;
import org.apache.lucene.index.IndexWriter ;
import org.apache.lucene.index.IndexWriterConfig ;
import org.apache.lucene.index.Term ;
import org.apache.lucene.queryparser.classic.QueryParser ;
import org.apache.lucene.search.BooleanClause.Occur ;
import org.apache.lucene.search.BooleanQuery ;
import org.apache.lucene.search.IndexSearcher ;
import org.apache.lucene.search.NumericRangeQuery ;
import org.apache.lucene.search.Query ;
import org.apache.lucene.search.ScoreDoc ;
import org.apache.lucene.search.TermQuery ;
import org.apache.lucene.search.TopDocs ;
import org.apache.lucene.store.Directory ;
import org.apache.lucene.store.FSDirectory ;
import org.apache.lucene.util.Version ;
import org.wltea.analyzer.lucene.IKAnalyzer ;
import cn.xjy.dao.BookDao ;
import cn.xjy.dao.BookDaoImpl ;
import cn.xjy.po.Book ; public class TestLucene { /**
* 创建索引库
* @throws Exception
*/
public void lucene() throws Exception {
BookDao bookDao = new BookDaoImpl() ;
List<Book> books = bookDao.getBooks() ;
// 采集数据的目的是为了索引,在索引前需要将原始内容创建成文档(Document),
// 文档(Document)中包括一个一个的域(Field)。 // 1.创建document集合对象
List<Document> documents = new ArrayList<Document>() ; // 2.循环遍历数据集,根据需求创建不同的filed,添加到对应的document对象中
Document document = null ;
for (Book book : books) {
document = new Document() ;
Field id = new IntField("id", book.getId(), Store.YES) ;
Field name = new TextField("name", book.getName(), Store.YES) ; if (book.getId()==4)
name.setBoost(100f) ;// 设置权重.值越大搜索越靠前
Field price = new FloatField("price", book.getPrice(), Store.YES) ;
Field pic = new TextField("pic", book.getPic(), Store.YES) ;
Field description = new TextField("description", book.getDescription(), Store.NO) ; document.add(id) ;
document.add(name) ;
document.add(price) ;
document.add(pic) ;
document.add(description) ;
documents.add(document) ;
} // 3.把每个document对象添加到document集合中 // 4.分析文档,对文档中的内容记性分词,实例化分析器对象,首先创建索引目录
Analyzer analyzer = new StandardAnalyzer() ;
Directory directory = FSDirectory.open(new File("src/index")) ; // 5.创建indexWriterConfig对象
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer) ; // 6.创建indexWriter对象
IndexWriter writer = new IndexWriter(directory, config) ; // 7.通过indexWriter对象,添加文档对象,写入索引库的过程
for (Document doc : documents) {
writer.addDocument(doc) ;
} // 8.关闭indexWriter流
writer.close() ; } /**
* 创建索引库,可解析中文
* @throws Exception
*/
public void luceneCN() throws Exception {
BookDao bookDao = new BookDaoImpl() ;
List<Book> books = bookDao.getBooks() ;
// 采集数据的目的是为了索引,在索引前需要将原始内容创建成文档(Document),
// 文档(Document)中包括一个一个的域(Field)。 // 1.创建document集合对象
List<Document> documents = new ArrayList<Document>() ; // 2.循环遍历数据集,根据需求创建不同的filed,添加到对应的document对象中
Document document = null ;
for (Book book : books) {
document = new Document() ;
Field id = new IntField("id", book.getId(), Store.YES) ;
Field name = new TextField("name", book.getName(), Store.YES) ; if (book.getId()==4)
name.setBoost(100f) ;// 设置权重.值越大搜索越靠前
Field price = new FloatField("price", book.getPrice(), Store.YES) ;
Field pic = new TextField("pic", book.getPic(), Store.YES) ;
Field description = new TextField("description", book.getDescription(), Store.YES) ; document.add(id) ;
document.add(name) ;
document.add(price) ;
document.add(pic) ;
document.add(description) ;
documents.add(document) ;
} // 3.把每个document对象添加到document集合中 // 4.分析文档,对文档中的内容记性分词,实例化分析器对象,首先创建索引目录
Analyzer analyzer = new IKAnalyzer();
Directory directory = FSDirectory.open(new File("src/index")) ; // 5.创建indexWriterConfig对象
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer) ; // 6.创建indexWriter对象
IndexWriter writer = new IndexWriter(directory, config) ; // 7.通过indexWriter对象,添加文档对象,写入索引库的过程
for (Document doc : documents) {
writer.addDocument(doc) ;
} // 8.关闭indexWriter流
writer.close() ; } /**
* 删除指定的索引
* @throws Exception
*/
public void deleteIndex() throws Exception {
// 1.指定索引库的位置
Directory directory = FSDirectory.open(new File("src/index")) ; // 2.创建indexWriterConfig
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, null) ; // 3.创建indexWriter
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig) ; // 4.删除指定的索引(new Term())
// indexWriter.deleteDocuments(new Term("id", "1"));//参数是Term()
// indexWriter.deleteDocuments(new QueryParser("id", new
// StandardAnalyzer()).parse("id:1"));//参数为query
indexWriter.deleteAll() ;// 删除所有
// 5.关闭流
indexWriter.close() ; System.out.println("删除成功") ; // 在查询一遍验证是否删除
searchIndex() ;
} /**
* 更新索引,
* 最好的做法是先查出要修改的索引进行更新
* @throws Exception
*/
public void updateIndex() throws Exception {
// 1.指定索引库
Directory directory = FSDirectory.open(new File("src/index")) ; // 2.定义indexReader
IndexReader indexReader = DirectoryReader.open(directory) ; // 3.定义indexSearcher
IndexSearcher indexSearcher = new IndexSearcher(indexReader) ; Query query = new QueryParser("id", new StandardAnalyzer()).parse("id:1") ;
// 查询索引库
TopDocs topDocs = indexSearcher.search(query, 1) ; // 获取查询到的对象
ScoreDoc scoreDoc = topDocs.scoreDocs[0] ; // 获取document对象
Document document = indexSearcher.doc(scoreDoc.doc) ; // 更新内容
document.removeField("name") ;
document.add(new TextField("name", "这是测试更新的内容", Store.YES)) ; // 初始化indexWriterConfig和indexWriter对象
IndexWriterConfig IndexWriterConfig = new IndexWriterConfig(Version.LATEST,
new StandardAnalyzer()) ;
IndexWriter indexWriter = new IndexWriter(directory, IndexWriterConfig) ; // 开始更新,这个方法第一个参数如果设置为null,则不会删除原来的数据,而且添加了一条更新后的新数据
// 为了保证数据的严谨性,必须删除为更新之前的数据,添加上更新后的数据就哦了
indexWriter.updateDocument(new Term("id", "1"), document) ;
indexWriter.close() ;
indexReader.close() ; System.out.println("更新成功") ;
} /**
* 可多条件连接QueryParser会将用户输入的查询表达式解析成Query对象实例。
* 搜索 Query query = queryParser.parse("*:*") ;
* @throws Exception
*/
public void searchIndex() throws Exception {
// 创建分析器
Analyzer analyzer = new StandardAnalyzer() ; // 查询条件
QueryParser queryParser = new QueryParser("description", analyzer) ;
Query query = queryParser.parse("description:个") ; // 指定搜索目录
Directory directory = FSDirectory.open(new File("src/index")) ; // 创建indexReader
IndexReader indexReader = DirectoryReader.open(directory) ; // 创建indexSearch对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader) ; // 查询索引库
TopDocs topDocs = indexSearcher.search(query, 10) ; // 获取前十条记录
ScoreDoc [] scoreDocs = topDocs.scoreDocs ; System.out.println("文档个数:" + topDocs.totalHits) ; for (ScoreDoc scoreDoc : scoreDocs) {
Document doc = indexSearcher.doc(scoreDoc.doc) ;
System.out.println(doc) ;
}
} /**
* 这种不可多条件查询
* 搜索 Query query = new TermQuery(new Term("id", "1"));
* @throws Exception
*/
public void searchIndex2() throws Exception {
// 创建分析器
Analyzer analyzer = new StandardAnalyzer() ; // 查询条件
Query query = new TermQuery(new Term("description", "徐景洋驻马店")) ; // 指定搜索目录
Directory directory = FSDirectory.open(new File("src/index")) ; // 创建indexReader
IndexReader indexReader = DirectoryReader.open(directory) ; // 创建indexSearch对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader) ; // 查询索引库
TopDocs topDocs = indexSearcher.search(query, 10) ; // 获取前十条记录
ScoreDoc [] scoreDocs = topDocs.scoreDocs ; System.out.println("文档个数:" + topDocs.totalHits) ; for (ScoreDoc scoreDoc : scoreDocs) {
Document doc = indexSearcher.doc(scoreDoc.doc) ;
System.out.println(doc) ;
}
} /**
* NumericRangeQuery,指定数字范围查询.(创建field类型时,注意与之对应)
* 搜索 Query query = NumericRangeQuery.newIntRange("id", 1, 9, true, true);
* @throws Exception
*/
public void searchIndex3() throws Exception {
// 创建分析器
Analyzer analyzer = new StandardAnalyzer() ; // 查询条件
// 创建查询
// 第一个参数:域名
// 第二个参数:最小值
// 第三个参数:最大值
// 第四个参数:是否包含最小值
// 第五个参数:是否包含最大值 Query query = NumericRangeQuery.newIntRange("id", 1, 9, true, true) ; // 指定搜索目录
Directory directory = FSDirectory.open(new File("src/index")) ; // 创建indexReader
IndexReader indexReader = DirectoryReader.open(directory) ; // 创建indexSearch对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader) ; // 查询索引库
TopDocs topDocs = indexSearcher.search(query, 10) ; // 获取前十条记录
ScoreDoc [] scoreDocs = topDocs.scoreDocs ; System.out.println("文档个数:" + topDocs.totalHits) ; for (ScoreDoc scoreDoc : scoreDocs) {
Document doc = indexSearcher.doc(scoreDoc.doc) ;
System.out.println(doc) ;
}
} /**
* 1、MUST和MUST表示“与”的关系,即“并集”。
2、MUST和MUST_NOT前者包含后者不包含。
3、MUST_NOT和MUST_NOT没意义
4、SHOULD与MUST表示MUST,SHOULD失去意义;
5、SHOUlD与MUST_NOT相当于MUST与MUST_NOT。
6、SHOULD与SHOULD表示“或”的概念。 * BooleanQuery,布尔查询,实现组合条件查询。
* 搜索 BooleanQuery query = new BooleanQuery() ;
* @throws Exception
*/
public void searchIndex4() throws Exception {
// 创建分析器
Analyzer analyzer = new StandardAnalyzer() ; // 查询条件
BooleanQuery query = new BooleanQuery() ; Query query1 = new TermQuery(new Term("name", "spring")) ;
Query query2 = NumericRangeQuery.newFloatRange("price", 60f, 80f, true, true) ; // MUST:查询条件必须满足,相当于AND
// SHOULD:查询条件可选,相当于OR
// MUST_NOT:查询条件不能满足,相当于NOT非
query.add(query2, Occur.SHOULD) ;
query.add(query1, Occur.MUST) ; // 指定搜索目录
Directory directory = FSDirectory.open(new File("src/index")) ; // 创建indexReader
IndexReader indexReader = DirectoryReader.open(directory) ; // 创建indexSearch对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader) ; // 查询索引库
TopDocs topDocs = indexSearcher.search(query, 10) ; // 获取前十条记录
ScoreDoc [] scoreDocs = topDocs.scoreDocs ; System.out.println("文档个数:" + topDocs.totalHits) ; for (ScoreDoc scoreDoc : scoreDocs) {
Document doc = indexSearcher.doc(scoreDoc.doc) ;
System.out.println(doc) ;
}
} }
package cn.xjy.test ; import org.junit.Test ;
import cn.xjy.lucene.TestLucene ; public class MyTest { @Test
public void testIndex() throws Exception {
TestLucene lucene = new TestLucene() ;
// lucene.lucene();
lucene.luceneCN() ;
System.out.println("创建成功") ;
} @Test
public void testSearch() throws Exception {
TestLucene lucene = new TestLucene() ;
// lucene.searchIndex();
lucene.searchIndex2() ;
// lucene.searchIndex3();
// lucene.searchIndex4();
} @Test
public void testDelete() throws Exception {
TestLucene lucene = new TestLucene() ;
lucene.deleteIndex() ;
} @Test
public void testUpdate() throws Exception {
TestLucene lucene = new TestLucene() ;
lucene.updateIndex() ;
}
}
配置文件:IKAnalyzer.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties> <comment>IK Analyzer 扩展配置</comment>
<!-- 用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">mydict.dic</entry>
<!-- 用户可以在这里配置自己的扩展停用词字典 -->
<entry key="ext_stopwords">ext_stopword.dic</entry> </properties>
4 Field域
4.1 Field属性
Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个Field,Document只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。
l 是否分词(tokenized)
是:作分词处理,即将Field值进行分词,分词的目的是为了索引。
比如:商品名称、商品简介等,这些内容用户要输入关键字搜索,由于搜索的内容格式大、内容多需要分词后将语汇单元索引。
否:不作分词处理
比如:商品id、订单号、身份证号等
l 是否索引(indexed)
是:进行索引。将Field分词后的词或整个Field值进行索引,索引的目的是为了搜索。
比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
否:不索引。该域的内容无法搜索到
比如:商品id、文件路径、图片路径等,不用作为查询条件的不用索引。
l 是否存储(stored)
是:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取。
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
否:不存储Field值,不存储的Field无法通过Document获取
比如:商品简介,内容较大不用存储。如果要向用户展示商品简介可以从系统的关系数据库中获取商品简介。
4.2 Field常用类型
下边列出了开发中常用 的Filed类型,注意Field的属性,根据需求选择:
|
Field类 |
数据类型 |
Analyzed 是否分词 |
Indexed 是否索引 |
Stored 是否存储 |
说明 |
|
StringField(FieldName, FieldValue,Store.YES)) |
字符串 |
N |
Y |
Y或N |
这个Field用来构建一个字符串Field,但是不会进行分词,会将整个串存储在索引中,比如(订单号,身份证号等) 是否存储在文档中用Store.YES或Store.NO决定 |
|
LongField(FieldName, FieldValue,Store.YES) |
Long型 |
Y |
Y |
Y或N |
这个Field用来构建一个Long数字型Field,进行分词和索引,比如(价格) 是否存储在文档中用Store.YES或Store.NO决定 |
|
StoredField(FieldName, FieldValue) |
重载方法,支持多种类型 |
N |
N |
Y |
这个Field用来构建不同类型Field 不分析,不索引,但要Field存储在文档中 |
|
TextField(FieldName, FieldValue, Store.NO) 或 TextField(FieldName, reader) |
字符串 或 流 |
Y |
Y |
Y或N |
如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略. |
5 使用中文分词器IKAnalyzer
IKAnalyzer继承Lucene的Analyzer抽象类,使用IKAnalyzer和Lucene自带的分析器方法一样,将Analyzer测试代码改为IKAnalyzer测试中文分词效果。
如果使用中文分词器ik-analyzer,就在索引和搜索程序中使用一致的分词器ik-analyzer。
5.1 添加jar包

- 代码部分在上面的部分包含有中文解析.
全文检索技术---Lucene的更多相关文章
- (转)全文检索技术学习(一)——Lucene的介绍
http://blog.csdn.net/yerenyuan_pku/article/details/72582979 本文我将为大家讲解全文检索技术——Lucene,现在这个技术用到的比较多,我觉得 ...
- Lucene全文检索技术
Lucene全文检索技术 今日大纲 ● 搜索的概念.搜索引擎原理.倒排索引 ● 全文索引的概念 ● 使用Lucene对索引进行CRUD操作 ● Lucene常用API详解 ● ...
- Lucene全文检索技术学习
---------------------------------------------------------------------------------------------------- ...
- ELK-全文检索技术-lucene
ELK : ELK是ElasticSearch,LogStash以及Kibana三个产品的首字母缩写 一.倒排索引 学习elk,必须先掌握倒排索引思想, 参考文档: https://www.cn ...
- JAVAEE——Lucene基础:什么是全文检索、Lucene实现全文检索的流程、配置开发环境、索引库创建与管理
1. 学习计划 第一天:Lucene的基础知识 1.案例分析:什么是全文检索,如何实现全文检索 2.Lucene实现全文检索的流程 a) 创建索引 b) 查询索引 3.配置开发环境 4.创建索引库 5 ...
- 全文搜索技术—Lucene
1. 内容安排 实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来.还可以根据中文词语进程查询,并且支持多种条件查询. 本案例中的原始内容就是磁盘上的文件 ...
- 全文检索(Lucene&Solr)
全文检索(Lucene&Solr) 1)什么是全文检索?为什么需要全文检索? 结构化数据(mysql等)方便查询,而非结构化数据(如多篇文章)是难以查询到自己需要的,所以要使用全文检索. 全文 ...
- 全文检索框架---Lucene
一.什么是全文检索 1.数据分类 我们生活中的数据总体分为两种:结构化数据和非结构化数据. 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等. 非结构化数据:指不定长或无固定格式 ...
- (转)全文检索技术学习(二)——配置Lucene的开发环境
http://blog.csdn.net/yerenyuan_pku/article/details/72589380 Lucene下载 Lucene是开发全文检索功能的工具包,可从官方网站http: ...
随机推荐
- 大数据分析处理框架——离线分析(hive,pig,spark)、近似实时分析(Impala)和实时分析(storm、spark streaming)
大数据分析处理架构图 数据源: 除该种方法之外,还可以分为离线数据.近似实时数据和实时数据.按照图中的分类其实就是说明了数据存储的结构,而特别要说的是流数据,它的核心就是数据的连续性和快速分析性: 计 ...
- Memorial for Nanjing victims today 南京大屠杀死难者公祭仪式今于南京举行 勿忘国耻,活捉小*日*本。
China will hold an annual memorial for the victims of the Nanjing Massacre today in the eastern city ...
- Codeforces Round #276 (Div. 2)C. Bits(构造法)
这道题直接去构造答案即可. 对于l的二进制表示,从右到左一位一位的使其变为1,当不能再变了(再变l就大于r了)时,答案就是l. 这种方法既可以保证答案大于等于l且小于等于r,也可以保证二进制表示时的1 ...
- Too Rich(贪心加搜索)
个人心得:10月份月赛题目,很low,就过了一道水题而且是把所有猜测都提交才过的.这段时间不知道忙什么去了, 也没怎么刷题感觉自己越来越差,还不如新来的大一学弟呢,别人起码天天刷代码到半夜,比起刚在区 ...
- [Luogu4177][CEOI2008]order
luogu sol 这题有点像网络流24题里面的太空飞行计划啊. 最大收益=总收益-最小损失. 先令\(ans=\sum\)任务收益. 源点向每个任务连容量为收益的边. 每个机器向汇点连容量为购买费用 ...
- loj 6083.「美团 CodeM 资格赛」数码
题目: 给定两个整数\(l\)和\(r\),对于任意\(x\),满足\(l\leq x\leq r\),把\(x\)所有约数写下来. 对于每个写下来的数,只保留最高位的那个数码.求\([1,9]\)中 ...
- Linux下全局安装composer方法
1.下载composer curl -sS https://getcomposer.org/installer | php 2.将composer.phar文件移动到bin目录以便全局使用compos ...
- MMS(mongodb监控工具)
今天好几个人问我如何查看mongodb的连接数,在mongo shell中执行: shard1:PRIMARY> db.serverStatus().connections { "cu ...
- mendeley 参考文献管理工具
本文由Suzzz原创,发布于http://www.cnblogs.com/Suzzz/p/4044144.html,转载请保留此声明 目录 介绍 功能 运行截图 安装方法 创建 Desktop Ent ...
- 7.Selenium+Python实现搜索百度的测试用例
1.导入测试用例需要的模块,unittest是python的内置模块,它提供了组织测试用例的框架 import unittest # 导入测试用例的模块 2.测试用例继承于unittest class ...