從es中恢復不存儲的字段內容的方法

原理分析

es中有個隱藏的字段_source,這個字段中存了其他字段的內容,我們直接查詢es返回的結果中展示的各個字段的值其實就是從_source字段中讀取的。如果想要對一個字段只建索引,不做存儲。就是不把這個字段的值存在_source字段中,這樣查詢結果中就不會顯示該字段的內容。如下圖所示:
在這裏插入圖片描述
從test3的mapping信息中可以看出 name,count字段是不存儲字段內容的。

在這裏插入圖片描述

因此,我們查詢test3中的返回結果中只有age字段的值,沒有name,count字段的值。

那麼如何恢復索引中不存儲字段name,count的值呢?

其實es未每個字段都默認開啓了docValues。docValue中存儲了字段的正排索引,即DocId -> 字段內容的映射。在每條數據插入索引的同時會創建docValues,docValues的內容存在es的數據目錄中的dvd和dvm文件中。由於docValues中存儲着正排索引,因此理論上我們也可以從中獲取字段的內容,包括不存儲字段的內容。

es是基於Lucene開發的,es的數據文件就是lucene文件,es一個分片就是lucene的一個索引。因此我們可以利用lucene提供的API去解析es的分片數據,從中DocValues中恢復不存儲字段的內容。

實現

本文是基於es2.3.5版本的,它對應的lucene版本是5.5.0。因此實現代碼中使用的lucene版本是5.5.0。

1、創建索引

首先我們創建一個索引test3
在這裏插入圖片描述
mapping信息已經在上面貼出來了,爲了方便分析這個索引只有1個分片,0個副本。有3個字段:name(string not_analyzed),age(integer),count(integer)。其中name,count是不存儲的。我們的目標是恢復name,count的數據。

2、將分片文件夾拷貝出來

分片文件夾的位置:/mnt/disk1/data/es1/escluster/nodes/0/indices/test3/0
j將這個目錄拷貝到本機中:F:\workspace\HKBIgData\tmp\0

3、pom文件的依賴

<dependencies>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>5.5.0</version>
        </dependency>
        <!--一般分詞器,適用於英文分詞-->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>5.5.0</version>
        </dependency>
        <!--中文分詞器-->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-smartcn</artifactId>
            <version>5.5.0</version>
        </dependency>
        <!--對分詞索引查詢解析-->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>5.5.0</version>
        </dependency>
        <!--檢索關鍵字高亮顯示-->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-highlighter</artifactId>
            <version>5.5.0</version>
        </dependency>
    </dependencies>

4、實現代碼

import org.apache.lucene.index.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.nio.file.Paths;
public class Test {
    public static void main(String[] args) throws Exception {
        String toWrite;
        // 創建輸出文件
        File file = new File("F:\\workspace\\HKBIgData\\tmp\\result.txt");
        if (!file.exists()) {
            file.createNewFile();
        }
        FileWriter fileWriter = new FileWriter(file, true);
        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
        Directory directory = FSDirectory.open(Paths.get("F:\\workspace\\HKBIgData\\tmp\\0\\index"));
        //讀取索引文件
        DirectoryReader reader= DirectoryReader.open(directory);
        // 獲取segment數量
        int size = reader.leaves().size();
        // 遍歷每個segment,讀取docValue中的數據
        for (int i = 0; i < size; i++) {
            // 獲取字段的DocValue
            SortedNumericDocValues db = DocValues.getSortedNumeric(reader.leaves().get(i).reader(), "count");
            SortedNumericDocValues db2 = DocValues.getSortedNumeric(reader.leaves().get(i).reader(), "age");
            RandomAccessOrds str = (RandomAccessOrds)DocValues.getSortedSet(reader.leaves().get(i).reader(), "name");
            System.out.println("segment-------------" + i);
            // 逐條從docValue中解析出數據並打印出來,string 類型和數字類型的處理不一樣
            for (int j=0; j< str.getValueCount();j++) {
                db.setDocument(j);
                db2.setDocument(j);
                str.setDocument(j);
                toWrite = "age: "+db2.valueAt(j) + "|" + "count: "+db.valueAt(j) + "|" + "name: "+str.lookupOrd(str.ordAt(j)).utf8ToString();
                // 打印結果
                System.out.println(toWrite);
                // 將結果寫入輸出文件中
                bufferedWriter.write(toWrite+"\n");
                bufferedWriter.flush();
            }
        }
        bufferedWriter.close();
        reader.close();
        System.out.println("ending!!!");
    }
}

5、校驗結果

執行上面的程序後,生成的result.txt文件就是恢復後的數據了,打開文件:
在這裏插入圖片描述

在這裏插入圖片描述

可以看到生成數據的總數量爲860003,這和索引中的數據量是一致的。我們在看看每行數據的各個字段是否對應正確。
從結果中取出最後一條數據 name: Tn5fuNK1ICgNiX1ga15lL0Q2lN1gD4 去es中查詢
在這裏插入圖片描述

對比後可以看到,各個字段的數據是對應的。

總結

從docValues中恢復數據的實現並不難,寥寥幾行代碼就能完成。難點主要在於不知道如何正確使用lucene API去文件中恢復數據。由於之前分析過es聚合過程的源碼,裏面就有用lucene API讀取docvalues的代碼。因此才從中知道如何正確使用lucene API的。 另外有幾點注意事項:
1、本文是基於es2.3.5版本的,不同的es版本對於的lucene API版本也不同,API的使用方法或許會有所變化。
2、對於string analyzed 類型的字段,由於入索引的時候經過分詞,因此是不能從docValue中恢復原始數據的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章