lucene之document
来源:互联网 发布:c语言总结 编辑:程序博客网 时间:2024/05/06 03:54
private float boost = 1.0f;这 里的fields是一个Vector,应该是为了同步的需要。Vector的代码里充斥了synchronized。。。其实要同步的话还有一个办法,就 是Collections.sychronizedList方法。不过本质是一样的,还是synchronized。这个synchronized一来, 不论读写统统给我锁住,实在是太霸道。现在还不清楚这个fields的读写操作比例如何,如果读多并且多线程的话,搞个读写锁应该好点吧?不知道为啥在 java的concurrent包里面没有一个这样的list实现,只找到一个CopyOnWriteList。这个可以研究一下,如果没问题Doug Lea大牛应该不会漏了这个东东吧。。。 我刚才之所以说“大部分方法”就是因为还有 boost 的set/get方法。boost在搜索中最后反映出来的应该是结果的优先级,它在其它的地方有更多的设置。在Document类里的这个boost其实是对这个Document里面所有的Field的一个缺省设置。 要注意的一点是:这个值不是直接存储在索引的Document里面的,而是以乘积的形式对这个Document里面的每个Field作出贡献。所以这个值只在把Document写入索引以前有效。假如你建好索引以后,通过搜索搜出一个Document,然后getBoost()得到它的boost值,这个值已经不是前面你写入索引时候的那个值了。 Field:Field由三个类主打:Fieldable,AbstractField,Field当然了,有AbstractField当然还会有别的Field实现,不过不在这个包里面,先不管。Fieldable定义了Field的所有行为。有一大陀的属性,大多挺直观的。isLazy()值得说一下。Lazy的Field意味着在搜索结果里这个Field的值缺省是不读取的,只有当你真正对这个Field取值的时候才会去取。所以如果你要对它取值,你得保证IndexReader还没有close。get/set Boost和Document里的意思差不多,无非是for field的,它也是不在索引里直接存储的。不太确定的是get/set OmitNorms。注释上面这么写的:/** Expert:
*
* If set, omit normalization factors associated with this indexed field.
* This effectively disables indexing boosts and length normalization for this field.
*/在Code 里一顿乱搜以后,指向了org.apache.lucene.search.Similarity#lengthNorm(String,int)这个方 法。Similarity类ms代表搜索结果相关性。lengthNorm的作用是根据一个Field里面term的总数来计算这个Field的 normalization value。这个值用来对Field的boost进行修正:DocumentWriter#writeNorms(String segment){...if(fi.isIndexed &&!fi.omitNorms){
float norm = fieldBoosts[n] * similarity.lengthNorm(fi.name, fieldLengths[n]);...}...lucene认为较长的Field里面的匹配相对精度较低。相反假如说短短几个词里面就有匹配,那说明相关度较高。所以缺省的DefaultSimilarity是这样实现的://numTerms代表这个Field里面的term总数public float lengthNorm(String fieldName, int numTerms) {
return (float)(1.0 / Math.sqrt(numTerms));
}Similarity里面还有几个xxxNorm的方法,应该是类似作用。不过这个扯远了,扯到后面的东东了。回到get/set OmitNorms,可以看出这个参数可以控制这个参数修正的停用或启用。后面应该还会读到关于Norm的内容,到时再仔细分析。 Fieldable定义了四种可能的取值方法:string,binary,reader,tokenStream。通过它们你可以得到这个Field所代表的真正数据的值。这四个取值方法里面有且只有一个的返回值不是null。 AbstractField 实现了Fieldable接口,提供了一些缺省的实现代码。而Field类又继承了AbstractField。在Field类里,主要的代码是构造函数 和三个public static final class:Store,Index,TermVector。这三个类都是继承自org.apache.lucene.util.Parameter。 Parameter类其实是一个枚举(Enum),不过它实现了Serializable这个接口,因此是可以序列化的。所以继承了它的这三个静态类其实 代表了Field的三个参数。它们可能的值分别是:Store(Compress/Yes/No)——表示怎样存储这个Field,Index (No/Tokenized/UN_Tokenized/No_Norms)——表示怎样建索引,TermVector (No/Yes/With_Positions/With_Offsets/With_Positions_Offsets)——表示怎样存储Term。 在Field的构造函数里会用到这几个参数来进行初始化,不同的组合会给Field里面的属性设置不同的值。由于索引主要是处理字符串用的,lucene还为Field提供了两个工具类,DateTools和NumberTools,用来进行date和long型数据与string之间的互转。(互转的时候还有一些讲究,比如转换后要保持排序不变,具体可以看看code)。 FieldSelector:这是这个包里面最后的一组类的。简明的接口定义告诉我们了一切:public interface FieldSelector extends Serializable {FieldSelectorResult accept(String fieldName);
}
FieldSelectorResult又是一个可序列化的枚举类。我注意到这里transient关键字的用法。
public final class FieldSelectorResult implements Serializable {
public transient static final FieldSelectorResult LOAD = new FieldSelectorResult(0);
public transient static final FieldSelectorResult LAZY_LOAD = new FieldSelectorResult(1);
...
当 我们在序列化的时候,可能有某些成员我们不希望自动被序列化,transient可以把它们排除在序列化之外。奇怪的是static成员应当本身就不在自 动序列化之列,这里的transient好像有点多余了,难道是个双保险?前面同是可序列化枚举的Parameter类的子类们就没有使用 transient关键字。
FieldSelector的实现比较简单,就不说了。
Lucene add、updateDocument添加、更新与search查询
package
com.lucene;
002
003
import
java.io.IOException;
004
005
import
org.apache.lucene.analysis.standard.StandardAnalyzer;
006
import
org.apache.lucene.document.Document;
007
import
org.apache.lucene.document.Field;
008
import
org.apache.lucene.index.IndexWriter;
009
import
org.apache.lucene.index.Term;
010
import
org.apache.lucene.queryParser.QueryParser;
011
import
org.apache.lucene.search.Hits;
012
import
org.apache.lucene.search.IndexSearcher;
013
import
org.apache.lucene.search.Query;
014
015
public
class
UpdateDocument {
016
017
private
static
String path =
"d:/index"
;
018
019
020
public
static
void
main(String[] args){
021
// addIndex();
022
updateIndex();
023
search(
"李四"
);
024
search(
"王五"
);
025
}
026
027
public
static
void
addIndex(){
028
try
{
029
IndexWriter write =
new
IndexWriter(path,
new
StandardAnalyzer(),
true
);
030
031
Document doc =
new
Document();
032
doc.add(
new
Field(
"id"
,
"123456"
,Field.Store.YES,Field.Index.UN_TOKENIZED));
033
doc.add(
new
Field(
"userName"
,
"张三"
,Field.Store.YES,Field.Index.TOKENIZED));
034
doc.add(
new
Field(
"comefrom"
,
"北京"
,Field.Store.YES,Field.Index.TOKENIZED));
035
036
write.addDocument(doc);
037
038
write.close();
039
040
}
catch
(IOException e) {
041
e.printStackTrace();
042
}
043
}
044
045
046
public
static
void
updateIndex(){
047
try
{
048
049
IndexWriter write =
new
IndexWriter(path,
new
StandardAnalyzer(),
false
);
050
Document docNew =
new
Document();
051
docNew.add(
new
Field(
"id"
,
"123456"
,Field.Store.YES,Field.Index.UN_TOKENIZED));
052
docNew.add(
new
Field(
"userName"
,
"王五"
,Field.Store.YES,Field.Index.TOKENIZED));
053
Term term =
new
Term(
"id"
,
"123456"
);
054
/**
055
调用updateDocument的方法,传给它一个新的doc来更新数据,
056
Term term = new Term("id","1234567");
057
先去索引文件里查找id为1234567的Doc,如果有就更新它(如果有多条,最后更新后只有一条)。如果没有就新增.
058
059
数据库更新的时候,我们可以只针对某个列来更新,而lucene只能针对一行数据更新。
060
*/
061
write.updateDocument(term, docNew);
062
063
write.close();
064
065
}
catch
(IOException e) {
066
e.printStackTrace();
067
}
068
}
069
070
public
static
Query queryParser(String str){
071
QueryParser queryParser =
new
QueryParser(
"userName"
,
new
StandardAnalyzer());
072
try
{
073
Query query = queryParser.parse(str);
074
return
query;
075
}
catch
(Exception e) {
076
e.printStackTrace();
077
}
078
return
null
;
079
}
080
081
public
static
void
search(String str){
082
try
{
083
IndexSearcher search =
new
IndexSearcher(path);
084
085
Query query = queryParser(str);
086
087
Hits hits = search.search(query);
088
if
(hits==
null
){
089
return
;
090
}
091
if
(hits.length() ==
0
){
092
System.out.println(
" 没有搜索到'"
+ str+
"'"
);
093
return
;
094
}
095
for
(
int
i =
0
; i < hits.length(); i++) {
096
Document doc = hits.doc(i);
097
System.out.println(
"id = "
+hits.id(i));
098
System.out.println(
"own id = "
+ doc.get(
"id"
));
099
System.out.println(
"userName = "
+doc.get(
"userName"
));
100
System.out.println(
"come from = "
+doc.get(
"comefrom"
));
101
System.out.println(
""
);
102
}
103
104
}
catch
(Exception e) {
105
e.printStackTrace();
106
}
107
}
108
109
}
------华------丽------的------分------隔------线------start------
关于java中的Serializable,其实还有一些讲究的,算作今天的课外知识,贴在这里(找不到出处了,作者包涵)。
1、实现Serializable回导致发布的API难以更改,并且使得package-private和private这两个本来封装的较好的咚咚也不能得到保障了
2、Serializable 会为每个类生成一个序列号,生成依据是类名、类实现的接口名、public和protected方法,所以只要你一不小心改了一个已经publish的 API,并且没有自己定义一个long类型的叫做serialVersionUID的field,哪怕只是添加一个getXX,就会让你读原来的序列化到 文件中的东西读不出来(不知道为什么要把方法名算进去?)
3、不用构造函数用Serializable就可以构造对象,看起来不大合理,这被称为extralinguistic mechanism,所以当实现Serializable时应该注意维持构造函数中所维持的那些不变状态
4、增加了发布新版本的类时的测试负担
5、1.4版本后,JavaBeans的持久化采用基于XML的机制,不再需要Serializable
6、 设计用来被继承的类时,尽量不实现Serializable,用来被继承的interface也不要继承Serializable。但是如果父类不实现 Serializable接口,子类很难实现它,特别是对于父类没有可以访问的不含参数的构造函数的时候。所以,一旦你决定不实现 Serializable接口并且类被用来继承的时候记得提供一个无参数的构造函数
7、内部类还是不要实现Serializable好了,除非是static的,(偶也觉得内部类不适合用来干这类活的)
8、使用一个自定义的序列化方法
看看下面这个保存一个双向链表的例子:
publicclass StringList implements Serializable
{
privateint size = 0;
private Entry head = null;
privatestaticclass Entry implements Serializable{
String data;
Entry next;
Entry previous;
}
//Remainder ommitted
}
这样会导致链表的每个元素以及元素之间的关系(双向链表之间的连接)都保存下来,更好的方法是提供一个自定义的序列化如下:
//String List with a resonable custom serialized form
class StringList implements Serializable
{
privatetransientint size = 0;//!transient
privatetransient Entry head = null;//!transient
//no longer serializable!
privatestaticclass Entry{
String data;
Entry next;
Entry previous;
}
//Appends the specified string to the list
publicvoid add(String s) {/*...*/};
/**
* Serialize this StringList
instance
* @author yuchifang
* @serialData The size of the list (the number of strings
* it contains) is emitted(int), in the proper sequence
*/
privatevoid writeObject(ObjectOutputStream s) throws IOException
{
s.defaultWriteObject();
s.writeInt(size);
//Write out all elements in the proper order
for (Entry e = head; e != null; e = e.next)
s.writeObject(e.data);
}
privatevoid readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
int numElements = s.readInt();
//Read in all elements andd insert them in list
for (int i = 0; i <numElements; i++)
add((String)s.readObject());
}
//...remainder omitted
}
9、不管你选择什么序列化形式,声明一个显式的UID:
private static final long serialVersionUID = randomLongValue;
10、不需要序列化的东西使用transient注掉它吧,别什么都留着
11、writeObject/readObject重载以完成更好的序列化
readResolve 与 writeReplace重载以完成更好的维护invariant controllers
------华------丽------的------分------隔------线------End------
今天的所得:
概念:Document,Field,FieldSelector
初步认识:用normalize value修正boost
温故知新:Serializable transient
遗留问题:读写锁的concurrent list
- lucene之document
- lucene之Document之人为设置相关度得分
- Lucene 4.4 删除document
- Lucene的Document
- lucene中Document的实质
- Lucene深入学习(5)Lucene的Document与Field
- lucene删除索引中的Document示例
- org.apache.lucene.document.Field类
- lucene series 1 document 文档 索引创建
- Lucene中Document的内部实现
- Lucene为图书馆构建Document 和 Field
- Lucene从索引中搜索出Document
- Lucene实战之初识Lucene
- document.write 之重写
- Shell之Here Document
- JavaScript之document对象
- Shell之Here Document
- Unity3D之IOS Document
- DTC配置
- 熟悉Qt开发环境--Hello Qt
- java编程之I/O输入输出
- ADO-dataReader
- MFC中ListControl控件的使用
- lucene之document
- iptables
- 使用Redhat系统在线安装(执行yum)时报错: This system is not registered with RHN.
- java Map集合类简介
- 启用Ubuntu FrameBuffer系统编辑文件
- Ubuntu 常用软件安装
- hibernate联合主键的三种注解做法
- 管人管事<松与紧>
- JSON