http://blog.csdn.net/twsxtd/article/details/8313843

How to Index documents

Creating an index obiect

可以使用index.create_in()函数创建index对象:

[python] view plaincopy
  1. import os, os.path  
  2. from whoosh import index  
  3.   
  4. if not os.path.exists("indexdir"):  
  5.     os.mkdir("indexdir")  
  6.   
  7. ix = index.create_in("indexdir", schema)  

带开一个已经存在某个目录的索引,使用index.open_dir()

[python] view plaincopy
  1. import whoosh.index as index  
  2.   
  3. ix = index.open_dir("indexdir")  

这些是便利方法:

[python] view plaincopy
  1. from whoosh.filedb.filestore import FileStorage  
  2. storage = FileStorage("indexdir")  
  3.   
  4. # Create an index  
  5. ix = storage.create_index(schema)  
  6.   
  7. # Open an existing index  
  8. storage.open_index()  

你和index对象一起创建的schema对象是可序列化的并且和index一起存储
你可以在同一个目录下面使用多个索引,用关键字参数分开

[python] view plaincopy
  1. # Using the convenience functions  
  2. ix = index.create_in("indexdir", schema=schema, indexname="usages")  
  3. ix = index.open_dir("indexdir", indexname="usages")  
  4.   
  5. # Using the Storage object  
  6. ix = storage.create_index(schema, indexname="usages")  
  7. ix = storage.open_index(indexname="usages")  


Clearing the index

在一个目录上调用index.craete_in()函数可以清除已经存在的索引的内容
可以用函数index.exist_in()来检测制定目录上面是否有一个有效的索引

  1. exists = index.exists_in("indexdir")  
  2. usages_exists = index.exists_in("indexdir", indexname="usages")  

(你也可以简单地删除目录上面的索引文件,例如如果索引目录只有一个,使用shutil.rmtree()来移除索引然后再重新创建

Indexing documents

一旦你创建了索引对象,你可以使用IndexWriter对象向其中添加document,可以使用Index.writer()来获取writer对象:

[python] view plaincopy
  1. ix = index.open_dir("index")  
  2. writer = ix.writer()  

创建writer的同时会对index加锁,因此同一时间只能有一个线程/进程进行写操作

Note:因为创建writer的时候会创建一个锁,因此在多进程/多线程的时候如果已经有一个writer对象打开那么再打开一个writer的时候可能会抛出一个异常(whoosh.store.LockError)Whoosh有一些例子实现writer的锁操作(whoosh.writing.AsncWriter和whoosh.writing.BufferedWriter)
Note:在writer打开并且提交的过程中,index是可读的,对已经存在reader无影响并且创建的新的reader对象也可以打开index文件,writer提交之后,已经存在的reader对象只能读到没有提交之前的内容,而新创建的reader可以读到最新提交的内容。

IndexWriter对象的add_document(**kwarg)方法接受关键字参数:

[python] view plaincopy
  1. writer = ix.writer()  
  2. writer.add_document(title=u"My document", content=u"This is my document!",  
  3.                     path=u"/a", tags=u"first short", icon=u"/icons/star.png")  
  4. writer.add_document(title=u"Second try", content=u"This is the second example.",  
  5.                     path=u"/b", tags=u"second short", icon=u"/icons/sheep.png")  
  6. writer.add_document(title=u"Third time's the charm", content=u"Examples are many.",  
  7.                     path=u"/c", tags=u"short", icon=u"/icons/book.png")  
  8. writer.commit()  

你不需要为每一个field提供一个值,Whoosh不关心你是否漏掉某个field,被索引的field必须是unicode串,而被存储而不被索引的field可以时任意的可序列化的对象

[python] view plaincopy
  1. writer.add_document(path=u"/a", title=u"A", content=u"Hello there")  
  2. writer.add_document(path=u"/a", title=u"A", content=u"Deja vu!")  

这将添加两个document到索引,见下文的update_document方法,它使用unique field来替换而不是追加内容

Indexing and storing different values for the same field

如果你有一个既需要索引又需要存储的field,你可以索引一个unicode值但是存储一个不同的对象(通常是这样,但是有时候非常有用)使用一个特别的关键字参数_stored_<fieldname>正常的值将被分析和索引,但是存储的这个值将会在结果里面出现:

[python] view plaincopy
  1. writer.add_document(title=u"Title to be indexed", _stored_title=u"Stored title")  


Finishing adding documents

一个IndexWriter对象就像一个数据库的事物对象。你可以给你的索引用一系列的改变然后一次性的提交。使用commit将IndexWriter提交的内容保存

[python] view plaincopy
  1. writer.commit()  

一旦你的document在index里面,你就可以搜索他们,如果你想关闭writer而不保存任何提交的东西,使用cancel()

  1. writer.cancel()  

记住只要你有一个writer是打开的,没有其它的线程能够另外得到一个writer然后修改index,并且一个writer通常打开着几个文件,因此你在完成工作前必须调用commit或者cancel方法

Merging segements

一个Whoosh filddb的index事实上是一个或者更多个被称为segement的“sub-index”子索引构成的。因此当你添加一个新的document到index里面的时候,Whoosh将创建一个新的segement到一个已经存在segement里面而不是把他放到documents里面(这样开销很大,因为涉及到磁盘文件上项的排序)因此当你搜索索引的时候,Whoosh会搜索不同的segement然后将结果整合到一起,看起来就像在同一个index一样(这种设计是模仿Lucene)
因此添加documents的时候有一个segement比不停地重新写入index更为高效。但是搜索多个segemet多少会有点拖慢速度,并且你的segement越多,速度越慢,因此当你commit的时候Whoosh有一个自己的算法来保证小的segement能够整合成更少的大一点的segement。
如果不想在commit的时候整合segement,可以将关键字参数merge设为False

[python] view plaincopy
  1. writer.commit(merge = False)  

若想整合所有的segement,优化索引可以使用optimize关键字参数

[python] view plaincopy
  1. writer.commit(optimize = True)  

因为优化的过程需要重写index上面的所有信息,因此在一个大的索引上面可能会非常慢,通常建议使用Whoosh自己的优化算法而不是一次性地优化

(Index对象也有optimize()方法来优化index它简单地创建一个writer然后调用commit(optimize = True))

如果你想对整合的过程有更多的控制,一可以重写整合策略的函数然后把他当做commit的参数,具体见NO_MERGE,MERGE_SMALL,OTIMIZE 函数在whoosh.fieldb.filewriting模块中的实现。

Delete Document

你可以用IndexWriter下列方法删除document然后使用commit方法在磁盘上删除
delete_document(document)
使用自己内部的document number删除一个document较低级的方法
is_deleted(docnum)
返回真如果这个给定数字的文档被删除了
delete_by_term(fieldname,termtext)
删除任何包含这个给定的termtext的field
delete_by_query(query)
删除任何匹配这个query的documents

[python] view plaincopy
  1. #Delete document by its path--this field must be indexed  
  2. ix.delete_by_term("path",u'/a/b/c')  
  3. #save the deletion to disk  
  4. ix.commit()  

在filedb的后端,删除一个document实际上他就是简单地把这个document添加到一个删除列表,当你搜索的时候他不会返回删除列表里面的内容,但是索引里面的内容仍然存在,他们的统计信息也不会被更新,直到你整合segement(这因为删除信息肯定会立即涉及到重写磁盘,这样效率会很低)

Updating document

如果你想替换掉一个已经存在的document,一可以先删除他然后再添加,当然你也可以使用IndexWriter对象的update_document方法一步完成
对于update_document这个方法,你必须确保至少有一个field的内容是唯一的,Whoosh会使用这个内容来搜索结果然后删除:

[python] view plaincopy
  1. from whoosh.fields import Schema,ID,TEXT  
  2.   
  3. schema = Schema (path=ID (unique=True),content=TEXT)  
  4.   
  5. ix = index.create_in("index")  
  6. writer = ix.writer()  
  7. writer.add_document(path=u"/a",content=u"the first document")  
  8. writer.add_cocument(path=u"/b",content=u"The second document")  
  9. writer.commit()  
  10.   
  11. writer = ix.writer()  
  12. #Because "path" is marked as unique,calling update_document with path = u"/a"  
  13. #will delete any existing document where the path field contains /a  
  14. writer.update_document(path=u"/a",content="Replacement for the first document")  
  15. writer.commit()  

这个“unique”field必须是被索引的
如果没有document能够搭配这个unique的field,那么这个方法就跟add_document一样

“unique”的field和update_document方法仅仅是为了方便删除和添加使用,Whoosh没有固有的唯一性概念,你也没法在使用add_document的时候强制指定唯一性

Incremental Indexing(增量索引)
当你索引一个documents的时候,可能有两种方式:一个是给所有的内容全部索引,一个时只更新发生改变的内容
给所有内容全部索引是很简单的:

[python] view plaincopy
  1. import os.path  
  2. from whoosh import index  
  3. from whoosh.fields import Schema, ID, TEXT  
  4.   
  5. def clean_index(dirname):  
  6.   # Always create the index from scratch  
  7.   ix = index.create_in(dirname, schema=get_schema())  
  8.   writer = ix.writer()  
  9.   
  10.   # Assume we have a function that gathers the filenames of the  
  11.   # documents to be indexed  
  12.   for path in my_docs():  
  13.   add_doc())writer, path)  
  14.   
  15.   writer.commit()  
  16.   
  17.   
  18. def get_schem()  
  19.     return Schema(path=ID(unique=True, stored=True), content=TEXT)  
  20.   
  21.   
  22. def add_doc(writer, path):  
  23.     fileobj=open(path, "rb")  
  24.     content=fileobj.read()  
  25.     fileobj.close()  
  26.     writer.add_document(path=path, content=content)  

对于一个很小的document,每次都全部索引可能会非常快,但是对于大的documents,你可能需要仅仅更新改变的内容
为了这么做我们需要存储每一个document的最后修改时间,在这个例子里我们使用mtime来简化:

[python] view plaincopy
  1. def get_schema()  
  2. return Schema())path=ID(unique=True, stored=True), time=STORED, content=TEXT)  
  3.   
  4. def add_doc(writer, path):  
  5. fileobj=open())path, "rb")  
  6. content=fileobj.read()")  
  7. fileobj.close()")  
  8. modtime = os.path.getmtime())path)  
  9. writer.add_document()  
  10. path=path, content=content, time=modtime)  

现在我们能够判断是清除还是增量索引:

[python] view plaincopy
  1. def index_my_docs(dirname, clean=False):  
  2.   if clean:  
  3.     clean_index(dirname)  
  4.   else:  
  5.     incremental_index(dirname)  
  6.   
  7.   
  8. def incremental_index(dirname)  
  9.     ix = index.open_dir(dirname)  
  10.   
  11.     # The set of all paths in the index  
  12.     indexed_paths = set()  
  13.     # The set of all paths we need to re-index  
  14.     to_index = set()  
  15.   
  16.     with ix.searcher() as searcher:  
  17.       writer = ix.writer()  
  18.   
  19.       # Loop over the stored fields in the index  
  20.       for fields in searcher.all_stored_fields():  
  21.         indexed_path = fields['path']  
  22.         indexed_paths.add(indexed_path)  
  23.   
  24.         if not os.path.exists(indexed_path):  
  25.           # This file was deleted since it was indexed  
  26.           writer.delete_by_term('path', indexed_path)  
  27.   
  28.         else:  
  29.           # Check if this file was changed since it  
  30.           # was indexed  
  31.           indexed_time = fields['time']  
  32.           mtime = os.path.getmtime(indexed_path)  
  33.           if mtime > indexed_time:  
  34.             # The file has changed, delete it and add it to the list of  
  35.             # files to reindex  
  36.             writer.delete_by_term('path', indexed_path)  
  37.             to_index.add(indexed_path)  
  38.   
  39.       # Loop over the files in the filesystem  
  40.       # Assume we have a function that gathers the filenames of the  
  41.       # documents to be indexed  
  42.       for path in my_docs():  
  43.         if path in to_index or path not in indexed_paths:  
  44.           # This is either a file that's changed, or a new file  
  45.           # that wasn't indexed before. So index it!  
  46.           add_doc(writer, path)  
  47.   
  48.       writer.commit()  

增量索引功能:

 

1.在所有已经索引的路径里面循环
a.如果文件不存在了,在现有的document里面删除
b.如果文件仍然存在但是被修改过,添加到需要修改的列表
c.如果文件存在不管是否修改过,添加到一索引的路径里面

2.在磁盘上的所有文件遍历
a.如果文件不是以索引路径集合的一部分,那么这个文件是新的,需要索引他
b.如果路径是修改列表的一部分,也需要更新他
c.否则,跳过

Posted on 2014-12-01 16:13  旅途  阅读(2065)  评论(0)    收藏  举报