lucene3.0.3의 디지털 인덱스 및 디지털 범위 조회
12769 단어 luceneNumericField
나는 3일 오후를 보았고 오전 내내 루틴이 숫자에 대한 색인과 숫자 범위에 대한 검색을 이해했다. 주요한 시간은 모두 Numeric Range Query에 썼다. 비록 한 번의 실패에도 불구하고 나는 포기하지 않았다. 연구와 탐색은 본래 나의 큰 흥미였고 마지막 기쁨은 이전의 모든 고통보다 시원했다.메모 고마워요.비고: 루틴의 색인 형식에 익숙하지 않고 특히 루틴과 처음 접촉했다면 돌아가세요. 이 노트는 원본 코드를 깊이 연구하는 프로그래머에게만 적합합니다.
숫자의 색인에 대해 숫자를 문자열로 직접 바꾸는 것은 아니다. 왜냐하면 이렇게 하면 범위 검색을 할 수 없기 때문이다. 예를 들어 우리 색인 1, 5, 20, 56, 89, 200, 201, 202, 203 등이다.299, 500, 루틴의 문자에 따라 정렬하면 사전표에서 1,20,200,201,202,203...299, 5, 56, 500, 89, 분명히 그의 순서는 범위 검색에 사용되지 않았다. 만약에 우리가 범위 검색을 1-300을 하려면 우리는 이 구역 아래의term를 모두 없애야 한다. 9로 시작하는 term이 있을 수 있지만 그가 모든term의 맨 뒤에 나타나기 때문에 문자 순서에 따라 정렬하는 것이 가장 좋은 정렬 방식이 아니다.우리는 카드luene가 어떻게 저장되는지 본다. 숫자의 영역에 대해 lucene의NumeircField를 사용한다. 그 중에서 가장 중요한 속성은 Token Stream-Numeric Token Stream이다. 이 영역의 숫자를 단어(즉 트리 트리를 형성하는 것)로 나누는 데 사용된다. 그의incrementToken 방법을 보면 이 방법은 숫자를 단어로 나누는 데 사용된다.여기서 우리는 롱형의 숫자를 예로 삼아 단어를 나누는 방법은 호출된 NumericUtils이다.longToPrefixCoded 방법:
/** shift, precisionStep */
@Override
public boolean incrementToken() {
if (valSize == 0)
throw new IllegalStateException("call set???Value() before usage");
if (shift >= valSize)
return false;
clearAttributes();
final char[] buffer;
switch (valSize) {
case 64:
buffer = termAtt.resizeTermBuffer(NumericUtils.BUF_SIZE_LONG);
// , buffer
// , buffer , ,
termAtt.setTermLength(NumericUtils.longToPrefixCoded(value, shift, buffer));
break;
case 32:
buffer = termAtt.resizeTermBuffer(NumericUtils.BUF_SIZE_INT);
termAtt.setTermLength(NumericUtils.intToPrefixCoded((int) value, shift, buffer));
break;
default:
// should not happen
throw new IllegalArgumentException("valSize must be 32 or 64");
}
typeAtt.setType((shift == 0) ? TOKEN_TYPE_FULL_PREC : TOKEN_TYPE_LOWER_PREC);
posIncrAtt.setPositionIncrement((shift == 0) ? 1 : 0);// , 0 , 0
shift += precisionStep;//
return true;
}
위의 방법에서 두 가지 중요한 속성이 있는데 하나는 시프트이고 하나는precisionStep이다. 컴퓨터에서 숫자는 2진법으로 표시한다. 단어를 나눌 때 그의 사고방식은 매번 왼쪽으로precisionStep의 위치를 옮기는 것이다. 옮긴 숫자는 모두 무시되고 나머지 숫자는 하나의 문자열을 형성한다.precisionStep은 매번 편이되는 2진법의 위치입니다. 시프트는 현재 편이된 전체 위치를 표시합니다. NumericUtils를 보십시오.longToPrefixCoded 소스:
public static int longToPrefixCoded(final long val, final int shift, final char[] buffer) {
if (shift > 63 || shift < 0)
throw new IllegalArgumentException("Illegal shift value, must be 0..63");
// char , 64- ( shift) 7, 7 bit ,(63-shift)/7 + 1 。
int nChars = (63 - shift) / 7 + 1;
// char , , 32+shift , 。
int len = nChars + 1;
// —— , 32+shift , , ( , 0), , ( ) ( bx ax )
buffer[0] = (char) (SHIFT_START_LONG + shift);
//0x8000000000000000L 64 1, 0。 , 1, 0。 , , , 。( , )
long sortableBits = val ^ 0x8000000000000000L;
sortableBits >>>= shift; // shift , 。
while (nChars >= 1) { // , 7 , 7 , utf-8 ,7 127, 127 utf8 , 。
// Store 7 bits per character for good efficiency when UTF-8 encoding.
// The whole number is right-justified so that lucene can
// prefix-encode the terms more efficiently. 。
// 7 bit, char。 7 bit char, buffer
buffer[nChars--] = (char) (sortableBits & 0x7f);
sortableBits >>>= 7;// 7
}
return len;
}
위의 방법은 2진법으로 묘사하면 그다지 형상적이지 않다. 우리는 10진법으로 예를 들어 우리가 색인을 하려고 하는 것이 8153이라는 숫자라고 가정하면 우리의precisionStep은 1이다. 즉, 매번 10진법의 위치를 옮기고 첫 번째shift는 0이기 때문에 전체 8152는 하나의 문자열을 형성한다.게다가 편이량을 저장하는 데 쓰이는 비트(여기서 우리는 편이량을 나타내는 비트를 생략하고 숫자의 문자열 형식으로 최종적으로 형성된 문자열을 직접 사용한다). 그래서 첫 번째 형성된 문자열은 8153이고 두 번째 문자열은 815(하지만 편이량은 1), 세 번째 문자열은 81(편이량은 2), 네 번째 문자열은 8(편이량은 3)이다.그래서 그가 트리 구조라는 것을 쉽게 발견할 수 있다. 그가 형성한 이 네 개의term의 뜻도 이렇게 이해할 수 있다. 8xxx의 또는 81xx의 또는 815x의 것, 그리고 가장 정확한 8153은 모두 현재의 문서를 검색할 수 있다.현재 우리는precision을 좀 크게 해서 두 개의 십진법의 위치를 만들면 8153과 81이 형성된다. 그는 8153과 81xx 두 개의term에서 이document를 찾을 수 있다고 말했다.precision이 더 작을 때 더 많은term를 생성하면 색인도 반드시 더 커진다는 것을 알 수 있다(precision이 너무 크면 사실 범위 검색을 할 때 속도가 떨어진다. 이것은 검색할 때 다시 이야기하자).우리가 다른 숫자를 인덱스할 때 계속 다른term을 형성한다. 모든 이term는 최종적으로 트리 트리를 구성하고precisionStep이 작을수록 이 트리의 노드가 많아지며 최종 인덱스의 부피도 커진다.
검색:NumericRangeQuery 같은 종류는 트리 트리를 사용하여 범위를 검색하는 데 사용된다. 그의 관건은 검색할 범위를 구분하고 트리 트리에 있는 노드(즉 이전에 색인을 만들 때 주장했던term)를 찾은 다음에 다시 쓰기 규칙에 따라 모든 검색을 다시 쓰는 것이다. 예를 들어booleanQuery를 주장하는 것이다.Filter를 만드는 것은 이렇게 간단하지만, 그는 내가 알아보는 데 오랜 시간이 걸렸다.NumericRangeQuery의 가장 관건적인 부분은 이전의term을 찾는 것이다. GetEnum 방법을 호출하면 이 방법은 NumericRangeTermEnum으로 되돌아온다. 코드를 살펴보자.
//
NumericUtils.splitLongRange(new NumericUtils.LongRangeBuilder() {
@Override
public final void addRange(String minPrefixCoded, String maxPrefixCoded) {
rangeBounds.add(minPrefixCoded);
rangeBounds.add(maxPrefixCoded);
}
}, precisionStep, minBound, maxBound);
상술한 방법은 찾으려는 구간 범위(즉 최대치와 최소치)와prerecisionStep에 따라 형성된 트리 트리의 노드를 찾아 체인 테이블에 넣는다. 한마디로 NumericRangeTermEnum의 구조 방법에서 모든 대비를 >=또는 <=, NumericUtils로 전환시켰다.splitLongRange 메서드
/** */
private static void splitRange(final Object builder, final int valSize, final int precisionStep, long minBound,//
long maxBound/* */) {
if (precisionStep < 1)
throw new IllegalArgumentException("precisionStep must be >=1");
if (minBound > maxBound)
return;
for (int shift = 0;; shift += precisionStep) {
// calculate new bounds for inner precision
final long diff = 1L << (shift + precisionStep);
final long mask = ((1L << precisionStep) - 1L) << shift;//
// minBound , 0 , 。 。
// true, , addRange ( 1)。
final boolean hasLower = (minBound & mask) != 0L;
// mask , mask , , term 。
// true, , addRange ( 2)
final boolean hasUpper = (maxBound & mask) != mask;
// diff, ( ),
// 632,precisionStp 1, 632 2 , 63x, ,631 630 , 1, 64x , 63x。
final long nextMinBound = (hasLower ? (minBound + diff) : minBound) & ~mask;//&~mask shift shift+precisionStep 0.
// , , 9 , , maxBound 765, 5, 76x ,
// 769 768 。 , 75x 。
final long nextMaxBound = (hasUpper ? (maxBound - diff) : maxBound) & ~mask;
// ,
final boolean lowerWrapped = nextMinBound < minBound;
final boolean upperWrapped = nextMaxBound > maxBound;
// 1、
// 2、 , , , , 。
// 3、4 3、4
if (shift + precisionStep >= valSize || nextMinBound > nextMaxBound || lowerWrapped || upperWrapped) {
// We are in the lowest precision or the next precision is not available.
addRange(builder, valSize, minBound, maxBound, shift);
// exit the split recursion loop
break;
}
if (hasLower)
//
addRange(builder, valSize, minBound, minBound | mask, shift);//minBound ,minBound|mask ,
if (hasUpper)
//
addRange(builder, valSize, maxBound & ~mask, maxBound, shift);
minBound = nextMinBound;
maxBound = nextMaxBound;
}
}
설명1: final boolean has Lower = (minBound & mask)!=0L; 만약minBound와 현재 정밀도의 최대치를 0과 비교하면 minBound가 현재 정밀도 범위의 모든 위치가 0이라는 것을 의미한다. 그러면 차 정밀도 범위의 어떤 값도 >=0의 요구를 만족시키기 때문에 그는 하나의 노드를 추가하지 않아도 된다. 이 정밀도 범위에서 제한된 노드가 형성되지 않고 정밀도를 계속 줄이면 된다.반대로 현재 정밀도 범위 내에 제한이 있고 최소값이 아니라면 제한된 노드를 추가해서 검색 결과를 제한해야 하기 때문에 현재true일 때addRange 방법을 뒤에 추가합니다. (나중에 보겠습니다.)
설명2: final boolean has Upper = (max Bound & mask)!=maxk, 만약에 maxBound와 현재 정밀도의 최대 값이 같다면 현재 정밀도 범위 내에서 모든 값은 <=maxBound의 요구를 만족시킬 것이다. 이것은 현재 정밀도의 제한을 추가하지 않고 정밀도를 계속 줄이면 된다.
설명 3: nextMinBound
addRange 방법: 최종적으로 longToPrefix 방법을 호출하여 숫자를 문자열로 바꾸고 체인 테이블에 넣으면 매번 이동할 때마다 두 개의term을 추가합니다.
마지막으로 그가 생성한term를 어떻게 사용하는지 다시 한 번 봅시다. org에서.apache.lucene.search.NumericRangeQuery.NumericRangeTermEnum.next () 방법 중
/** Increments the enumeration to the next element. True if one exists. */
@Override
public boolean next() throws IOException {
// termEnum, termEnum ? ?
// term , , term , , term ,
// ( 3.0 tii ), while termEnum , term。
// if a current term exists, the actual enum is initialized:
// try change to next term, if no such term exists, fall-through
if (currentTerm != null) {
assert actualEnum != null;
if (actualEnum.next()) {
currentTerm = actualEnum.term();
if (termCompare(currentTerm))
return true;
}
}
// if all above fails, we go forward to the next enum, if one is available
currentTerm = null;
while (rangeBounds.size() >= 2) {// , addRange
assert rangeBounds.size() % 2 == 0;
// close the current enum and read next bounds
if (actualEnum != null) {
actualEnum.close();
actualEnum = null;
}
final String lowerBound = rangeBounds.removeFirst();//
this.currentUpperBound = rangeBounds.removeFirst();
// create a new enum
actualEnum = reader.terms(termTemplate.createTerm(lowerBound));
currentTerm = actualEnum.term();
if (currentTerm != null && termCompare(currentTerm))
return true;
// clear the current term for next iteration
currentTerm = null;
}
// no more sub-range enums available
assert rangeBounds.size() == 0 && currentTerm == null;
return false;
}
그의 사고방식은 제한조건으로 사용되는term에 따라 그들 구간에 있는 모든term을 찾는 것이다. 그래서 여기서 사고방식은 명랑해졌다. 요약하자면maxBoung과minBound,precisionStep에 따라 여러 개의 제한이 있는term을 만들고 이런 제한적인term에 따라 사전표에서 그들이 만나는 모든term을 찾는다.이 방법을 사용하는 장점은 잎사귀 노드에 대한 사용을 줄일 수 있다는 것이다. 모든 제한적인term을 생성할 때 트리트리트리의 아버지 노드와 아버지 노드의 아버지 노드가 사용되기 때문에 아버지 노드가 없거나 maxBound와minBound가 서로 교차하기 때문에 찾을 때 사전표가 대량의term를 돌아간다.제한된 노드 사이의term만 찾으면 검색된term의 수량이 크게 줄어들어 검색의 속도를 가속화시킨다.
검색할 때 사용하는precisionStep의 크기에 대한 영향: 이 값이 클수록 트리 트리의 깊이가 얕아지고 제한적인 노드의 범위가 넓을수록 찾은term의 수량이 많아지며 역배열표를 통합할 때 느려진다.이 값이 작을수록 생성된 트리 트리의 깊이가 커지고 제한 노드의 개수가 크며 검색된term의 수가 작을수록 합병 속도가 커진다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
자바 키워드 전체 텍스트 검색 인 스 턴 스package javaLucene; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.F...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.