从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中恢复原始数据的。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值