搜尋引擎中一個非常重要的資料結構就是倒排索引,類似下表:
倒排索引可以分成兩部分,關鍵詞和和文件編號。文件編號資料我們可以使用一個大檔案來儲存。類似下面:
記錄好偏移量。而關鍵詞我們使用另外一種資料結構儲存,並記錄文件編號的檔案偏移量和編號數量,只要查詢到關鍵詞,就能通過偏移量獲取對應的文件編號。
文件編號使用int64儲存,但最終寫入到檔案是用二進位制來儲存,Go語言的 encoding/binary 包中的 binary.Write() 函式使得以二進位制格式寫資料非常簡單。封裝了一些介面方便我們讀寫int64
func main() {
h := util.NewFileHandler("test")//會建立test.bin檔案
index := h.WriteInt64(998,0) //寫入998,返回偏移量
num := h.ReadInt64(index) //傳入偏移量,讀取一個int64
s := h.ReadDocIdsArry(index,100) //傳入偏移量,讀取100個int64
fmt.Println(index,num,s)
}
package util
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
)
type FileHandler struct{
filePath string
file *os.File
}
const FILEDIR = "/data/index/"
//一個欄位一個檔案
func NewFileHandler(field string) *FileHandler{
root := GetPath()
filePath := root+FILEDIR+field+".bin"
var fp *os.File
var err error
if FileExist(filePath) {
fp,err = os.OpenFile(filePath, os.O_RDWR, 0666)
if err != nil {
fmt.Println("open file:",err)
}
}else{
fp,err = os.Create(filePath)
if err != nil {
fmt.Println("create file:",err)
}
}
fileHandler := new(FileHandler)
fileHandler.filePath = filePath
fileHandler.file = fp
return fileHandler
}
//從指定的位置讀取一個int64
func (fh *FileHandler)ReadInt64(start int64) int64{
buf := make([]byte,8)
_,err := fh.file.ReadAt(buf, start)
if err!=nil{
if err==io.EOF {
return -1
}
}
return bytetoint(buf) //把讀取的位元組轉為int64
}
//指定的地方寫入int64,不傳就獲取檔案最後的下標
func (fh *FileHandler)WriteInt64(value,start int64) int64{
if start < 1 {
start,_ = fh.file.Seek(0, io.SeekEnd) //表示0到檔案end的偏移量
}
b := inttobyte(value)
_,err := fh.file.WriteAt(b,start) //n表示寫入的位元組數,data是int64,所以n=8, 使用writeAt不能使用追加模式
if err!= nil{
fmt.Println(err)
}
return start
}
//從start下標讀取len個int64
func (fh *FileHandler)ReadDocIdsArry(start, len int64) []int64{
var i int64 = 0
res := make([]int64,0,len)
for ; i < len; i++{
start = start + i*8
num := fh.ReadInt64(start)
if num <= 0 { //越界了就直接返回
break
}
res = append(res,num)
}
return res
}
func FileExist(filePath string) bool{
_, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// []byte 轉化 int64
func bytetoint(by []byte) int64{
var num int64
b_buf := bytes.NewBuffer(by)
binary.Read(b_buf, binary.BigEndian, &num)
return num
}
// int64 轉 []byte
func inttobyte(num int64) []byte {
b_buf := new(bytes.Buffer)
binary.Write(b_buf, binary.BigEndian,&num) //num型別不能是int
return b_buf.Bytes()
}
//獲取當前程式目錄
func GetPath() string{
path,_ := os.Getwd()
return path
}
注意:
使用binary包時,必須使用明確的長度確定的型別,可以用int32,int64 但別用int。
原因:www.jianshu.com/p/219c24a4347c
func Write(w io.Writer, order ByteOrder, data interface{})
本作品採用《CC 協議》,轉載必須註明作者和本文連結