bhabegger commented on code in PR #2817: URL: https://github.com/apache/jackrabbit-oak/pull/2817#discussion_r4259230513
########## oak-search-lucene-ng/src/main/java/org/apache/jackrabbit/oak/plugins/index/luceneNg/internal/editor/LuceneNgDocumentMaker.java: ########## @@ -0,0 +1,576 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.plugins.index.luceneNg.internal.editor; + +import org.apache.jackrabbit.oak.api.Blob; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.plugins.index.luceneNg.LuceneNgIndexConstants; +import org.apache.jackrabbit.oak.plugins.index.search.Aggregate; +import org.apache.jackrabbit.oak.plugins.index.search.FieldNames; +import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition; +import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition.IndexingRule; +import org.apache.jackrabbit.oak.plugins.index.search.PropertyDefinition; +import org.apache.jackrabbit.oak.plugins.index.search.spi.binary.FulltextBinaryTextExtractor; +import org.apache.jackrabbit.oak.plugins.index.search.spi.editor.FulltextDocumentMaker; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.util.ISO8601; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.DoubleDocValuesField; +import org.apache.lucene.document.DoublePoint; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.LongPoint; +import org.apache.lucene.document.NumericDocValuesField; +import org.apache.lucene.document.SortedDocValuesField; +import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.document.StringField; +import org.apache.lucene.document.TextField; +import org.apache.lucene.facet.FacetsConfig; +import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetField; +import org.apache.lucene.util.BytesRef; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.PropertyType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Builds a Lucene 9 {@link Document} for a single node, implementing the abstract hooks + * required by the shared {@link FulltextDocumentMaker} framework (the same framework + * {@code oak-lucene} and {@code oak-search-elastic} use). + * + * <p>Field-<em>selection</em> gating (which hook fires for which {@link PropertyDefinition} + * flag) is handled entirely by the framework's {@code makeDocument} template method; these + * hooks only create the fields once invoked.</p> + * + * <p>{@link #indexAggregateValue} routes a matched child/relative node's text into the + * parent's {@code :fulltext} field, giving this module index-time <b>aggregation</b>.</p> + */ +public class LuceneNgDocumentMaker extends FulltextDocumentMaker<Document> { + + private static final Logger LOG = LoggerFactory.getLogger(LuceneNgDocumentMaker.class); + + private static final int MAX_FIELD_LENGTH = IndexDefinition.DEFAULT_MAX_FIELD_LENGTH; + + private final FacetsConfig facetsConfig; + + /** + * @param textExtractor optional binary text extractor; this module has no binary + * extraction support (see {@link #addBinary}) so this is normally + * {@code null}. Retained for parity with the framework contract. + * @param definition the (LuceneNg) index definition + * @param indexingRule the indexing rule matched for the node being indexed + * @param path the content path of the node being indexed + * @param facetsConfig the pre-built facets configuration (dimensions registered by the + * editor context); used by {@link #finalizeDoc} to build facet fields + */ + public LuceneNgDocumentMaker(@Nullable FulltextBinaryTextExtractor textExtractor, + IndexDefinition definition, + IndexingRule indexingRule, + String path, + FacetsConfig facetsConfig) { + super(textExtractor, definition, indexingRule, path); + this.facetsConfig = facetsConfig; + } + + @Override + protected Document initDoc() { + Document doc = new Document(); + // Path fields are always added — they use the ":path" / ":parent" prefixes which + // cannot collide with JCR property names. + doc.add(new StringField(FieldNames.PATH, path, Field.Store.YES)); + int lastSlash = path.lastIndexOf('/'); + String parentPath = lastSlash == 0 ? "/" : path.substring(0, lastSlash); + doc.add(new StringField(LuceneNgIndexConstants.FIELD_PARENT_PATH, parentPath, Field.Store.NO)); + return doc; + } + + @Override + protected Document finalizeDoc(Document doc, boolean dirty, boolean facet) throws IOException { + return (facet && facetsConfig != null) ? facetsConfig.build(doc) : doc; + } + + @Override + protected boolean isFacetingEnabled() { + return facetsConfig != null; + } + + // ------------------------------------------------------------------------- + // Typed / property-index fields + // ------------------------------------------------------------------------- + + /** + * Adds the property-index (exact-match / point) field for the value at position {@code i}. + * The framework's {@code addTypedFields} iterates array values and calls this once per value, + * so this method handles a single value only. + * + * <p>When the index definition declares Long/Double/Date, the value is converted and a + * numeric point field is written (guaranteeing a consistent Lucene field type across all + * documents); otherwise the field type is driven by the actual Oak value type (String + * exact-match).</p> + */ + @Override + protected void indexTypedProperty(Document doc, PropertyState property, String pname, + PropertyDefinition pd, int i) { + if (pd.isTypeDefined()) { + switch (pd.getType()) { + case PropertyType.LONG: { + Long lv = readAsLong(property, i); + if (lv != null) { + doc.add(new LongPoint(pname, lv)); + } else { + LOG.debug("Skipping property '{}': declared Long but value cannot be converted", pname); + } + return; + } + case PropertyType.DOUBLE: { + Double dv = readAsDouble(property, i); + if (dv != null) { + doc.add(new DoublePoint(pname, dv)); + } else { + LOG.debug("Skipping property '{}': declared Double but value cannot be converted", pname); + } + return; + } + case PropertyType.DATE: { + Long millis = readAsDateMillis(property, i); + if (millis != null) { + doc.add(new LongPoint(pname, millis)); + } else { + LOG.debug("Skipping property '{}': declared Date but value cannot be converted", pname); + } + return; + } + default: + // Declared as String (or another non-numeric type): fall through to + // actual-type dispatch so string/boolean handling is unchanged. + indexByActualType(doc, property, pname, pd, i); + return; + } + } + // No explicit type declaration: drive field type from the actual Oak value type. + indexByActualType(doc, property, pname, pd, i); + } + + /** + * Indexes the value at position {@code i} using the property's actual Oak value type. + * Numeric/boolean values are indexed as string exact-match fields, and only when the + * property is single-valued. Binary values are ignored here (never call + * {@code getValue(STRING)} on a binary). + */ + private void indexByActualType(Document doc, PropertyState property, String pname, + PropertyDefinition pd, int i) { + switch (property.getType().tag()) { + case PropertyType.NAME: + doc.add(new StringField(pname, property.getValue(Type.NAME, i), Field.Store.NO)); + break; + case PropertyType.LONG: Review Comment: FYI, Lucene 4.7 was type tolerant, so legacy could pick the field type from each value at index time. Lucene 9 isn't: a field has to keep a consistent shape across all documents. Hence the problem above. The single vs multiple issue is now fixed. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
