bitflicker64 commented on code in PR #2994:
URL: https://github.com/apache/hugegraph/pull/2994#discussion_r3947216107


##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/traversal/optimize/TraversalUtil.java:
##########
@@ -645,14 +667,327 @@ private static boolean 
extractHasContainers(HugeVertexStep<?> newStep,
 
     private static boolean canExtractHasContainers(HugeGraph graph,
                                                    HasContainerHolder holder) {
-        for (HasContainer has : holder.getHasContainers()) {
+        // Keep unsafe labels and their sibling properties for local filtering.
+        if (hasUnsafeLabelPredicate(holder)) {
+            return false;
+        }
+        List<HasContainer> hasContainers = holder.getHasContainers();
+        for (HasContainer has : hasContainers) {
             if (!canExtractHasContainer(graph, has)) {
                 return false;
             }
         }
         return true;
     }
 
+    private static void prepareLocalHasContainers(
+            Step<?, ?> source, Traversal.Admin<?, ?> traversal) {
+        QueryHolder query = (QueryHolder) source;
+        Step<?, ?> step = source.getNextStep();
+        while (step instanceof HasStep || step instanceof NoOpBarrierStep) {
+            Step<?, ?> next = step.getNextStep();
+            if (step instanceof HasStep) {
+                HasContainerHolder holder = (HasContainerHolder) step;
+                for (HasContainer has : new 
ArrayList<>(holder.getHasContainers())) {
+                    // Paging is query metadata, never an element property 
filter.
+                    if (QueryHolder.SYSPROP_PAGE.equals(has.getKey())) {
+                        query.addHasContainer(has);
+                        holder.removeHasContainer(has);
+                        continue;
+                    }
+                    if (T.id.getAccessor().equals(has.getKey())) {
+                        // ID lookup is complete across labels. Existing source
+                        // IDs and unsupported predicates still filter locally.
+                        if (source instanceof HugeGraphStep &&
+                            GraphStep.processHasContainerIds((HugeGraphStep<?, 
?>) source, has)) {
+                            holder.removeHasContainer(has);
+                            continue;
+                        }
+                        holder.removeHasContainer(has);
+                        holder.addHasContainer(new HasContainer(has.getKey(),
+                                localIdPredicate(has.getPredicate())));
+                        continue;
+                    }
+                    if (T.label.getAccessor().equals(has.getKey())) {
+                        holder.removeHasContainer(has);
+                        holder.addHasContainer(new 
LocalLabelHasContainer(has.getPredicate()));
+                        continue;
+                    }
+                    if (isSysProp(has.getKey())) {

Review Comment:
   ‼️ `hasKey()` and `hasValue()` silently return nothing on this path.
   
   `~page`, `T.id` and `T.label` have all been handled and `continue`d above, 
and `isSysProp` (line 1545) resolves through `token2HugeKey`, so the only keys 
that reach this branch are `T.key` (`"key"`) and `T.value` (`"value"`). They 
stay in the local `HasStep` untouched.
   
   On the extraction path they are pushed down: `canExtractHasContainer` 
returns `true` for every sysprop key (line 993) and `convContains2Relation` 
(line 1403) turns them into `Condition.containsKey(PROPERTIES, pkId)` / 
`containsValue(...)`. That is what makes `g.V().hasKey("age")` work today 
(`VertexCoreTest:3419`, `EdgeCoreTest:2884`).
   
   Left local, TinkerPop's `HasContainer.test(Element)` (gremlin-core 3.5.1) 
special-cases only `T.id` and `T.label` and otherwise falls through to 
`element.properties(this.key)`, so the container is evaluated as a property 
literally named `"key"`. `HugeVertex.properties(String...)` (line 573) catches 
the `IllegalArgumentException` from `graph().propertyKey("key")` and continues, 
so the iterator is empty and the predicate is false for every element.
   
   Result: `g.V().hasKey("age").hasLabel(P.neq("person"))` returns nothing 
instead of the vertices that have an `age` property, with no error. Same for 
`hasValue(...)` and for the `HugeVertexStep` variant. I confirmed the plan 
difference: `__.V().hasKey("age")` moves the `T.key` container into 
`HugeGraphStep` and drops the local `HasStep`, while 
`__.V().hasKey("age").hasLabel(P.neq("other"))` leaves it in the `HasStep` with 
its original `eq(age)` predicate.
   
   Please handle `T.key` and `T.value` explicitly here, the way `T.id` and 
`T.label` are handled just above. They are label-independent, so pushing them 
into the source query stays complete. A regression test for `hasKey(...)` next 
to a negative label would pin this.



##########
hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/HugeGraph.java:
##########
@@ -289,6 +290,9 @@ public interface HugeGraph extends Graph {
 
     <K, V> V option(TypedOption<K, V> option);
 
+    // Build the same term matcher used by SEARCH indexes, without reading 
data.
+    Predicate<Object> searchPredicate(String text);

Review Comment:
   🧹 Adding an abstract method to the published `HugeGraph` interface forces 
every out-of-tree implementor to recompile, and an implementation that is not 
recompiled fails with `AbstractMethodError` once this method is called. In-tree 
only `StandardHugeGraph` and `HugeGraphAuthProxy` implement the interface, so 
the build surfaces nothing.
   
   The interface already carries nine `default` methods, so a `default` 
throwing `UnsupportedOperationException` would keep third-party implementations 
compiling. Note that a `default` cannot delegate to 
`GraphIndexTransaction.searchPredicate(this.analyzer(), text)`, because 
`analyzer()` is private to `StandardHugeGraph` and is not on the interface. 
Please either make it `default`, or confirm the break is intended for this 
release.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to