Lucene.Net无障碍学习和使用:搜索篇
一、初步认识搜索
先从上一篇示例代码中我们摘录一段代码看看搜索的简单实现:
private TopDocs Search(string keyword,string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
QueryParser parser = new QueryParser(field, new StandardAnalyzer());
Query query = parser.Parse(keyword);//搜索内容 contents (用QueryParser.Parse方法实例化一个查询)
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n); //获取搜索结果
watch.Stop();
StringBuffer sb = "索引完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
从上面代码,我们不难看出,搜索需要用到IndexSearcher,Query,QueryParser和TopDocs(或者Hits)四个核心类:
1、 IndexSearcher
IndexSearcher会打开索引文件,它不使用Lucene.Net的锁,可以理解为只读操作。它的Search方法是我们最常用的,该方法返回我们需要的结果。
2、QueryParser
QueryParser是Query的构造器,它的Parse方法会根据Analyzer构造一个合理的Query对象来应对搜索。
3、Query
Query类作为查询表达式的载体同样至关重要,它有丰富的子类,让我们可以应对多种变化的搜索需求,简单来说,我们想到的常用搜索Lucene.Net几乎已经都给我们实现了,你只要分辨应该使用那个类来搜索比较合理。
4、TopDocs(或者Hits)
这个类我们可以简单把它理解成它就是我们要的搜索结果集,通过它我们可以知道记录集合中的各个Document的详细信息:
/// <summary>
/// 显示搜索结果
/// </summary>
/// <param name="queryResult"></param>
private void ShowFileSearchResult(TopDocs queryResult)
{
if (queryResult == null || queryResult.totalHits == )
{
SetOutput("Sorry,没有搜索到你要的结果。");
return;
} int counter = ;
foreach (ScoreDoc sd in queryResult.scoreDocs)
{
try
{
Document doc = searcher.Doc(sd.doc);
string id = doc.Get("id");//获取id
string fileName = doc.Get("filename");//获取文件名
string contents = doc.Get("contents");//获取文件内容
string result = string.Format("这是第{0}个搜索结果,Id为{1},文件名为:{2},文件内容为:{3}{4}", counter, id, fileName, Environment.NewLine, contents);
SetOutput(result);
}
catch (Exception ex)
{
SetOutput(ex.Message);
}
counter++;
}
}
毫无疑问,搜索结果的准确性和Query以及QueryParser密切相关,其实还和一个东西有莫大的关系,下面我会再次提到。搜索的过程就是对 索引文件进行查找的过程。我们可以直白地这样理解:索引文件好比是数据库,查询表达式就像是T-SQL语句,最后通过Lucene.Net搜索引擎找到结 果集。
二、几种常用的Query介绍
A、单个索引文件进行搜索举例
1、TermQuery
private TopDocs TermQuery(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
Term t = new Term(field, keyword);
Query query = new TermQuery(t);
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n);
watch.Stop();
StringBuffer sb = "TermQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
这个Query主要用来查询关键字。我在测试TermQuery的时候,输入”jeff“是可以搜索到一条记录的,然后我输入找到的这条记录内容中的两个汉字“喜欢”,这一次却没有返回结果(当然,搜索不到也不能表示Jeff Wong还没有喜欢的人。 ^_^):
为什么呢?上面我们说搜索的准确程度和Query、QueryParser相关,根据我参考的几个文档,我怀疑第三个因素就是Analyzer(期 待高人指点,下面的BooleanQuery等几个搜索,用英文“think”可以搜出结果,但用中文也搜索不到结果),这里搜索不到的原因可能就是没有 指定Analyzer为StandardAnalyzer(存疑,TO DO)。
注:只要在QueryParse的Parse方法中只有一个keyword,就会自动转换成TermQuery。
2、RangeQuery
用于查询范围,看下面的示例:
private TopDocs RangeQuery( string field,string start,string end,bool isInclude)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索,id范围为{0}~{1}", start,end));
try
{
Term beginT = new Term(field, start);
Term endT = new Term(field, end);
Query query = new RangeQuery(beginT, endT, isInclude);
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n);
watch.Stop();
StringBuffer sb = "RangeQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
取id在3和5之间的满足搜索条件的结果集。
3、BooleanQuery
这个Query也经常使用,用于搜索满足多个条件的查询:
private TopDocs BooleanQuery(string keyword, string field)
{
string[] words = keyword.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
BooleanQuery boolQuery = new BooleanQuery();
Term beginT = new Term("id", "");
Term endT = new Term("id", "");
RangeQuery rq = new RangeQuery(beginT, endT, true); //rangequery id从3到5
for (int i = ; i < words.Length; i++)
{
TermQuery tq = new TermQuery(new Term(field, words[i]));//termquery
boolQuery.Add(tq, BooleanClause.Occur.MUST);
}
boolQuery.Add(rq, BooleanClause.Occur.MUST); Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(boolQuery, (Filter)null, n);
watch.Stop();
StringBuffer sb = "BooleanQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
希望大家注意到输入中文搜索不到结果的问题。我测试可能的几种情况都没有搜出结果。
4、PrefixQuery
PrefixQuery用于搜索是否包含某个特定前缀,常用于分类(Catalog)的检索:
private TopDocs PrefixQuery(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
Term t = new Term(field, keyword);
PrefixQuery query = new PrefixQuery(t);
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n);
watch.Stop();
StringBuffer sb = "PrefixQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
本文测试的时候,输入“ja”就可以搜到包含java和javascript两项结果了。
5、FuzzyQuery
模糊查询:
private TopDocs FuzzyQuery(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
Term t = new Term(field, keyword);
FuzzyQuery query = new FuzzyQuery(t);
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n);
watch.Stop();
StringBuffer sb = "FuzzyQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
我还从没有在项目中用过这个Query。
6、WildcardQuery
通配符搜索:
private TopDocs WildcardQuery(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
Term t = new Term(field, keyword);
WildcardQuery query = new WildcardQuery(t);
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n);
watch.Stop();
StringBuffer sb = "WildcardQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
本文示例您可以试着输入“java*”,它可以把包含java和javascript两项结果取出来。感觉这个比较好使,但是必须熟悉通配符的使用语法(看上去和正则类似)。
7、PhraseQuery
查询短语,这里面主要有一个slop的概念, 也就是各个词之间的位移偏差, 这个值会影响到结果的评分,可以通过SetSlop方法进行设定:
private TopDocs PhraseQuery(string keyword, string field,int slop)
{
string[] words = keyword.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
PhraseQuery query = new PhraseQuery();
query.SetSlop(slop);
foreach (string word in words)
{
Term t = new Term(field, word);
query.Add(t);
}
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n);
watch.Stop();
StringBuffer sb = "PhraseQuery搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
本文示例中,输入“think javascript”,并且设置slop为1,即可命中“think in javascript”那一项。
注意,旧的参考资料上说PhraseQuery对于短语的顺序是不管的(存疑,我测试的时候输入“javascript think”,就没有匹配任何结果),这点在查询时虽然提高了命中率,却会对性能产生很大的影响。
到这里,您可能会说,哇,介绍了这么多,应该已经涵盖了大部分可能的搜索情况了吧?嘿嘿,好问题,实际上万里长征我们才走到第一步。上面的代码示例基本上都是对内容(contents)或者id进行匹配搜索,这里我还要补充一种情况,就是同一索引文件下,对多个字段进行搜索,比如下面的代码就是通过MultiFieldQueryParser实现的对id和contents同时进行搜索:
/// <summary>
/// 多字段搜索(以空格,逗号等分隔符隔开)
/// </summary>
/// <param name="keyword"></param>
/// <returns></returns>
private TopDocs MulFieldsSearch(string keyword)
{
TopDocs docs = null;
int n = ;
SetOutput("正在检索关键字:" + keyword);
try
{
BooleanClause.Occur[] flags=new BooleanClause.Occur[]{BooleanClause.Occur.MUST,BooleanClause.Occur.MUST};
string[] fields = new string[] { "id", "contents" };
string[] values = keyword.Trim().Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
if (fields.Length != values.Length)
{
throw new Exception("字段和对应值不一致");
}
//MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, new StandardAnalyzer());
//parser.SetDefaultOperator(QueryParser.Operator.OR);//或者的关系
//Query query = parser.Parse(keyword);
Query query = MultiFieldQueryParser.Parse(values, fields, flags, new StandardAnalyzer());
Stopwatch watch = new Stopwatch();
watch.Start(); docs = searcher.Search(query, (Filter)null, n); //排序获取搜索结果
watch.Stop();
StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
}
return docs;
}
示例中,我输入“1 喜欢”,就可以把id为1,且内容保护“喜欢”的那条记录显示出来了。搜索结果如图:
注:上面介绍的这几种Query也适用于下面B中要介绍的多个索引文件搜索。
B、多个索引文件进行搜索举例
说一下我在本地的测试:索引文件index建好后,其他多余的索引文件我们就不费事了,直接把建好的索引index文件夹复制一份,命名为index1,然后就是通过MultiSearcher类进行多索引文件操作:
/// <summary>
/// 根据多个索引文件夹搜索
/// </summary>
/// <param name="keyword"></param>
/// <returns></returns>
private TopDocs MultiSearch(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
Searchable[] abs = new Searchable[];
abs[] = new IndexSearcher(INDEX_STORE_PATH);
abs[] = new IndexSearcher(INDEX_STORE_PATH1);
MultiSearcher searcher = new MultiSearcher(abs);//构造MultiSearcher
try
{
QueryParser parser = new QueryParser(field, new StandardAnalyzer());
Query query = parser.Parse(keyword);
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n); //排序获取搜索结果
watch.Stop();
StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
} /// <summary>
/// 显示搜索结果
/// </summary>
/// <param name="queryResult"></param>
private void ShowMultFileSearchResult(TopDocs queryResult)
{
if (queryResult == null || queryResult.totalHits == )
{
SetOutput("Sorry,没有搜索到你要的结果。");
return;
}
Searchable[] abs = new Searchable[];
abs[] = new IndexSearcher(INDEX_STORE_PATH);
abs[] = new IndexSearcher(INDEX_STORE_PATH1);
MultiSearcher searcher = new MultiSearcher(abs);//构造MultiSearcher
int counter = ;
foreach (ScoreDoc sd in queryResult.scoreDocs)
{
try
{
Document doc = searcher.Doc(sd.doc);
string id = doc.Get("id");//获取id
string fileName = doc.Get("filename");//获取文件名
string contents = doc.Get("contents");//获取文件内容
string result = string.Format("这是第{0}个搜索结果,Id为{1},文件名为:{2},文件内容为:{3}{4}", counter, id, fileName, Environment.NewLine, contents);
SetOutput(result);
}
catch (Exception ex)
{
SetOutput(ex.Message);
}
counter++;
}
}
搜索结果不出意外,通常都是匹配两份:
三、排序
排序我们通常都需要用到Sort和SortField类,顾名思义,这两个类专门是用来排序的:
/// <summary>
/// 根据索引排序搜索
/// </summary>
/// <param name="keyword"></param>
/// <returns></returns>
private TopDocs SortSearch(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
QueryParser parser = new QueryParser(field, new StandardAnalyzer());//针对内容查询
Query query = parser.Parse(keyword);//搜索内容 contents (用QueryParser.Parse方法实例化一个查询)
Stopwatch watch = new Stopwatch();
bool sortDirection = true; if (chkIsSortById.Checked == true)//按照id升序
{
sortDirection = false;
}
watch.Start();
Sort sort = new Sort();
SortField sf = new SortField("id", SortField.INT, sortDirection);//按照id字段排序,false表示升序,ture表示逆序
//SortField sf = new SortField("filename", SortField.DOC, false);//按照filename字段排序,false表示升序 sort.SetSort(sf); //多个条件排序
//Sort sort = new Sort();
//SortField f1 = new SortField("id", SortField.INT, false);
//SortField f2 = new SortField("filename", SortField.DOC, false);
//sort.SetSort(new SortField[] { f1, f2 });
docs = searcher.Search(query, (Filter)null, n, sort); //排序获取搜索结果
watch.Stop();
StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒";
SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
}
可以按照多个字段进行排序,据可靠消息,排序会降低搜索效率,所以通常对于数据量较频繁或者较大的搜索,通常都会采取某一些策略,先取出来再在程序中进行排序。
下图是简单的按照id升序的排序结果:
四、分页
本文的分页搜索主要就是利用旧文介绍的一个分页控件,根据返回的结果总数(TopDocs的totalHits),利用当前页和每页记录数进行分 页,而不是像数据库分页一样,一次就取出某一页的每页记录数,严格来讲,不算Lucene.Net自身的正确的分页(Lucene.Net的另一种分页需 要用到缓存),待我再看看源码研究研究,过段时间再总结一下。
/// <summary>
/// 根据索引分页搜索
/// </summary>
/// <param name="keyword"></param>
/// <returns></returns>
private TopDocs PagerSearch(string keyword, string field)
{
TopDocs docs = null;
int n = ;//最多返回多少个结果
SetOutput(string.Format("正在检索关键字:{0}", keyword));
try
{
QueryParser parser = new QueryParser(field, new StandardAnalyzer());//针对内容查询
Query query = parser.Parse(keyword);//搜索内容 contents (用QueryParser.Parse方法实例化一个查询)
Stopwatch watch = new Stopwatch();
watch.Start();
docs = searcher.Search(query, (Filter)null, n); //排序获取搜索结果
watch.Stop();
StringBuffer sb = "搜索完成,共用时:" + watch.Elapsed.Hours + "时 " + watch.Elapsed.Minutes + "分 " + watch.Elapsed.Seconds + "秒 " + watch.Elapsed.Milliseconds + "毫秒"; SetOutput(sb);
}
catch (Exception ex)
{
SetOutput(ex.Message);
docs = null;
}
return docs;
} /// <summary>
/// 显示分页搜索结果
/// </summary>
/// <param name="queryResult"></param>
private void ShowPagerSearchResult(TopDocs queryResult,int currentPage)
{
if (queryResult == null || queryResult.totalHits == )
{
SetOutput("Sorry,没有搜索到你要的结果。");
return;
} int counter = ;
int start = (currentPage - ) * recordsPerPage;//开始记录
int end = currentPage * recordsPerPage;//结束记录
if (end > queryResult.totalHits)
{
end = queryResult.totalHits;
}
for (int i = start; i < end; i++)
{
ScoreDoc sd = queryResult.scoreDocs[i];
try
{
Document doc = searcher.Doc(sd.doc);
string id = doc.Get("id");//获取id
string fileName = doc.Get("filename");//获取文件名
string contents = doc.Get("contents");//获取文件内容
string result = string.Format("这是第{0}页第{1}个搜索结果,Id为{2},文件名为:{3},文件内容为:{4}{5}",currentPage, counter, id, fileName, Environment.NewLine, contents);
SetOutput(result);
}
catch (Exception ex)
{
SetOutput(ex.Message);
}
counter++;
}
} /// <summary>
/// 将搜索结果分页显示
/// </summary>
/// <param name="currentPage"></param>
private void BindPagerResults(int currentPage)
{
searcher = new IndexSearcher(INDEX_STORE_PATH); //构建一个索引搜索器
TopDocs queryResult = PagerSearch(this.txtPagerKeyword.Text.Trim(), "contents");//按照内容搜索
int totalCount = queryResult.totalHits;//总记录数
ShowPagerSearchResult(queryResult,currentPage);
if (totalCount > )
{
this.panelPager.Visible = true;
//绑定页码相关信息
PagerControl pager = new PagerControl(currentPage, recordsPerPage, totalCount, "跳转");
pager.currentPageChanged += new EventHandler(pager_currentPageChanged);//页码变化 触发的事件
this.panelPager.Controls.Add(pager);//在Panel容器中加入分页控件
}
} private void pager_currentPageChanged(object sender, EventArgs e)
{
PagerControl pager = sender as PagerControl;
if (pager == null || string.IsNullOrEmpty(this.txtPagerKeyword.Text.Trim()))
{
return;
}
SetOutput(string.Format("==========================分页搜索开始时间:{0}===========================", DateTime.Now.ToString()));
currentPage = pager.CurrentPage;
BindPagerResults(currentPage);//查询数据并分页绑定
}
有图有真相:
关于分页控件,请参考拙文winform下的一个分页控件。
好了,简单常见的搜索就介绍到这里,有时间我会继续总结QueryParser的常用搜索语法和技巧以及一些复杂搜索(包括令人头疼的分组问题等等)。
最后,我想说的是,没有合理的索引数据作为搜索的前期准备,好比想要在程序员中找到漂亮体贴亭亭玉立温柔贤惠善解人意的女朋友一样,大部分工作和精力可能都是徒劳的,所以,合理创建索引的工作至关重要,在实际的开发中,按照项目需要,最大程度构造和优化您的索引吧。
改进后的demo下载:LuceneNetApp
Lucene.Net无障碍学习和使用:搜索篇的更多相关文章
- 【转载】Lucene.Net无障碍学习和使用:搜索篇
在上一篇中,我们初步理解了索引的增删改查基本操作.本文着重介绍一下常用的搜索,以及搜索结果的排序和分页.本文的搜索主要是基于前一篇介绍的文本文件的索引,建议下载最后改进的demo对照着看阅读本文,同时 ...
- Lucene.Net无障碍学习和使用:索引篇
一.简单认识索引 Lucene.Net的应用相对比较简单.一段时间以来,我最多只是在项目中写点代码,利用一下它的类库而已,对很多名词术语不是很清晰,甚至理解 可能还有偏差.从我过去的博客你也可以看出, ...
- 使用 Apache Lucene 和 Solr 4 实现下一代搜索和分析
使用 Apache Lucene 和 Solr 4 实现下一代搜索和分析 使用搜索引擎计数构建快速.高效和可扩展的数据驱动应用程序 Apache Lucene™ 和 Solr™ 是强大的开源搜索技术, ...
- Lucene.net入门学习
Lucene.net入门学习(结合盘古分词) Lucene简介 Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不是一个完整的全 ...
- mysql学习【第6篇】:权限和数据库设计
狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! mysql学习[第6篇]:权限和数据库设计 用户和权限管理 /* 用户和权限管理 */ ---- ...
- mysql学习【第5篇】:事务索引备份视图
狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! mysql学习[第5篇]:事务索引备份视图 MySQL事务 事务就是将一组SQL语句放在同一批次 ...
- mysql学习【第3篇】:使用DQL查询数据
狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! mysql学习[第3篇]:使用DQL查询数据 DQL语言 DQL( Data Query Lan ...
- jackson学习之十(终篇):springboot整合(配置类)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 从零开始学习jQuery (一) 入门篇
本系列文章导航 从零开始学习jQuery (一) 入门篇 一.摘要 本系列文章将带您进入jQuery的精彩世界, 其中有很多作者具体的使用经验和解决方案, 即使你会使用jQuery也能在阅读中发现些 ...
随机推荐
- POJ 2226 Muddy Fields(二分匹配 巧妙的建图)
Description Rain has pummeled the cows' field, a rectangular grid of R rows and C columns (1 <= R ...
- Sightseeing(dijlstar) 计算最短路和次短路的条数
Sightseeing Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 10004 Accepted: 3523 Desc ...
- Qt ------ 内存回收机制、new对象的回收
写在前面的总结: 建议:对于不能指定父对象的对象(对象通过moveToThread()移入其他线程.没有继承QObject的类产生的对象),在其他线程通过deleteLater()内存回收,其他通过指 ...
- eclipse好玩的插件集(一) CKEditor插件
啥也不说,先上效果图: 当你输入完图片的url时,你可以得到预览的图像,从而进行宽高调整! 使用方法: 在eclipse市场中搜索ckeditor 配置操作如下: 进行文件关联,这样就可以直接用c ...
- struts2之OGNL用法
浅析OGNL OGNL是Object-GraphNavigation Language的缩写,是一种功能强大的表达式语言 通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对 ...
- [cerc2012][Gym100624D]20181013
题意:一个序列,如果存在一个连续子序列,满足该子序列中没有只存在一次的序列,则原序列为boring,否则non-boring 题解: 分治递归 对一个序列,如果找到了一个只出现一次的数位于a[x],则 ...
- NGINX: 反向代理 Nexus
Nginx 反向代理 nexus 的服务, 一直卡在 Initialize... 解决方式是添加一个 header X-Forwarded-Proto: proxy_set_header X-Forw ...
- A题 hdu 1235 统计同成绩学生人数
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1235 统计同成绩学生人数 Time Limit: 2000/1000 MS (Java/Others) ...
- 2017-2018-1 20179205《Linux内核原理与设计》第五周作业
<Linux内核原理与设计>第五周作业 视频学习及操作分析 一.用户态.内核态和中断 内核态在CPU执行中对应高执行级别,执行级别为0级,具有特权指令,可以访问任意物理地址:用户态执行级别 ...
- 一个基于时间注入的perl小脚本
use strict; use warnings; use LWP::Simple; my %table_and_leng; ;;$count++){ #print "Test Table: ...