-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
SimpleIndexReader.java
232 lines (213 loc) · 9.14 KB
/
SimpleIndexReader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
* Copyright (c) 2002-2018 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.api.impl.schema.reader;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TotalHitCountCollector;
import java.io.IOException;
import java.util.Arrays;
import org.neo4j.collection.PrimitiveLongResourceIterator;
import org.neo4j.helpers.TaskControl;
import org.neo4j.helpers.TaskCoordinator;
import org.neo4j.internal.kernel.api.IndexOrder;
import org.neo4j.internal.kernel.api.IndexQuery;
import org.neo4j.internal.kernel.api.IndexQuery.IndexQueryType;
import org.neo4j.internal.kernel.api.exceptions.schema.IndexNotApplicableKernelException;
import org.neo4j.kernel.api.impl.index.collector.DocValuesCollector;
import org.neo4j.kernel.api.impl.index.partition.PartitionSearcher;
import org.neo4j.kernel.api.impl.schema.LuceneDocumentStructure;
import org.neo4j.kernel.api.impl.schema.sampler.NonUniqueLuceneIndexSampler;
import org.neo4j.kernel.api.impl.schema.sampler.UniqueLuceneIndexSampler;
import org.neo4j.kernel.api.schema.index.IndexDescriptor;
import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig;
import org.neo4j.storageengine.api.schema.AbstractIndexReader;
import org.neo4j.storageengine.api.schema.IndexProgressor;
import org.neo4j.storageengine.api.schema.IndexSampler;
import org.neo4j.values.storable.Value;
import static java.lang.String.format;
import static org.neo4j.internal.kernel.api.IndexQuery.IndexQueryType.exact;
import static org.neo4j.kernel.api.impl.schema.LuceneDocumentStructure.NODE_ID_KEY;
import static org.neo4j.kernel.api.schema.index.IndexDescriptor.Type.UNIQUE;
/**
* Schema index reader that is able to read/sample a single partition of a partitioned Lucene index.
*
* @see PartitionedIndexReader
*/
public class SimpleIndexReader extends AbstractIndexReader
{
private final PartitionSearcher partitionSearcher;
private final IndexDescriptor descriptor;
private final IndexSamplingConfig samplingConfig;
private final TaskCoordinator taskCoordinator;
public SimpleIndexReader( PartitionSearcher partitionSearcher,
IndexDescriptor descriptor,
IndexSamplingConfig samplingConfig,
TaskCoordinator taskCoordinator )
{
super( descriptor );
this.partitionSearcher = partitionSearcher;
this.descriptor = descriptor;
this.samplingConfig = samplingConfig;
this.taskCoordinator = taskCoordinator;
}
@Override
public IndexSampler createSampler()
{
TaskControl taskControl = taskCoordinator.newInstance();
if ( descriptor.type() == UNIQUE )
{
return new UniqueLuceneIndexSampler( getIndexSearcher(), taskControl );
}
else
{
return new NonUniqueLuceneIndexSampler( getIndexSearcher(), taskControl, samplingConfig );
}
}
@Override
public void query( IndexProgressor.NodeValueClient client, IndexOrder indexOrder, IndexQuery... predicates ) throws IndexNotApplicableKernelException
{
Query query = toLuceneQuery( predicates );
client.initialize( descriptor, search( query ).getIndexProgressor( NODE_ID_KEY, client ), predicates );
}
@Override
public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws IndexNotApplicableKernelException
{
Query query = toLuceneQuery( predicates );
return search( query ).getValuesIterator( NODE_ID_KEY );
}
private DocValuesCollector search( Query query )
{
try
{
DocValuesCollector docValuesCollector = new DocValuesCollector();
getIndexSearcher().search( query, docValuesCollector );
return docValuesCollector;
}
catch ( IOException e )
{
throw new RuntimeException( e );
}
}
private Query toLuceneQuery( IndexQuery... predicates ) throws IndexNotApplicableKernelException
{
IndexQuery predicate = predicates[0];
switch ( predicate.type() )
{
case exact:
Value[] values = new Value[predicates.length];
for ( int i = 0; i < predicates.length; i++ )
{
assert predicates[i].type() == exact :
"Exact followed by another query predicate type is not supported at this moment.";
values[i] = ((IndexQuery.ExactPredicate) predicates[i]).value();
}
return LuceneDocumentStructure.newSeekQuery( values );
case exists:
for ( IndexQuery p : predicates )
{
if ( p.type() != IndexQueryType.exists )
{
throw new IndexNotApplicableKernelException(
"Exists followed by another query predicate type is not supported." );
}
}
return LuceneDocumentStructure.newScanQuery();
case range:
assertNotComposite( predicates );
switch ( predicate.valueGroup() )
{
case NUMBER:
IndexQuery.NumberRangePredicate np = (IndexQuery.NumberRangePredicate) predicate;
return LuceneDocumentStructure.newInclusiveNumericRangeSeekQuery( np.from(),
np.to() );
case TEXT:
IndexQuery.TextRangePredicate sp = (IndexQuery.TextRangePredicate) predicate;
return LuceneDocumentStructure.newRangeSeekByStringQuery( sp.from(), sp.fromInclusive(),
sp.to(), sp.toInclusive() );
default:
throw new UnsupportedOperationException(
format( "Range scans of value group %s are not supported", predicate.valueGroup() ) );
}
case stringPrefix:
assertNotComposite( predicates );
IndexQuery.StringPrefixPredicate spp = (IndexQuery.StringPrefixPredicate) predicate;
return LuceneDocumentStructure.newRangeSeekByPrefixQuery( spp.prefix() );
case stringContains:
assertNotComposite( predicates );
IndexQuery.StringContainsPredicate scp = (IndexQuery.StringContainsPredicate) predicate;
return LuceneDocumentStructure.newWildCardStringQuery( scp.contains() );
case stringSuffix:
assertNotComposite( predicates );
IndexQuery.StringSuffixPredicate ssp = (IndexQuery.StringSuffixPredicate) predicate;
return LuceneDocumentStructure.newSuffixStringQuery( ssp.suffix() );
default:
// todo figure out a more specific exception
throw new RuntimeException( "Index query not supported: " + Arrays.toString( predicates ) );
}
}
@Override
public boolean hasFullValuePrecision( IndexQuery... predicates )
{
return false;
}
private void assertNotComposite( IndexQuery[] predicates )
{
assert predicates.length == 1 : "composite indexes not yet supported for this operation";
}
@Override
public long countIndexedNodes( long nodeId, Value... propertyValues )
{
Query nodeIdQuery = new TermQuery( LuceneDocumentStructure.newTermForChangeOrRemove( nodeId ) );
Query valueQuery = LuceneDocumentStructure.newSeekQuery( propertyValues );
BooleanQuery.Builder nodeIdAndValueQuery = new BooleanQuery.Builder().setDisableCoord( true );
nodeIdAndValueQuery.add( nodeIdQuery, BooleanClause.Occur.MUST );
nodeIdAndValueQuery.add( valueQuery, BooleanClause.Occur.MUST );
try
{
TotalHitCountCollector collector = new TotalHitCountCollector();
getIndexSearcher().search( nodeIdAndValueQuery.build(), collector );
// A <label,propertyKeyId,nodeId> tuple should only match at most a single propertyValue
return collector.getTotalHits();
}
catch ( IOException e )
{
throw new RuntimeException( e );
}
}
@Override
public void close()
{
try
{
partitionSearcher.close();
}
catch ( IOException e )
{
throw new IndexReaderCloseException( e );
}
}
private IndexSearcher getIndexSearcher()
{
return partitionSearcher.getIndexSearcher();
}
}