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]

Reply via email to