diff --git a/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordGraph.java b/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordGraph.java index 20e4320d..272b7b35 100644 --- a/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordGraph.java +++ b/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordGraph.java @@ -225,7 +225,8 @@ public class QWordGraph implements Iterable { } public Comparator topologicalComparator() { - return Comparator.comparing(sortOrder::get); + Comparator comp = Comparator.comparing(sortOrder::get); + return comp.thenComparing(QWord::ord); } } diff --git a/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordPathsRenderer.java b/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordPathsRenderer.java index a8e96837..762a7d1b 100644 --- a/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordPathsRenderer.java +++ b/code/functions/search-query/java/nu/marginalia/functions/searchquery/query_parser/model/QWordPathsRenderer.java @@ -21,6 +21,13 @@ class QWordPathsRenderer { return new QWordPathsRenderer(graph).render(graph.reachability()); } + + private static String render(Collection paths, + QWordGraph.ReachabilityData reachability) + { + return new QWordPathsRenderer(paths).render(reachability); + } + /** Render the paths into a human-readable infix-style expression. *

* This method is recursive, but the recursion depth is limited by the @@ -34,8 +41,7 @@ class QWordPathsRenderer { // Find the commonality of words in the paths - Map commonality = paths.stream().flatMap(QWordPath::stream) - .collect(Collectors.groupingBy(w -> w, Collectors.summingInt(w -> 1))); + Map commonality = nodeCommonality(); // Break the words into two categories: those that are common to all paths, and those that are not @@ -72,10 +78,7 @@ class QWordPathsRenderer { } // Recurse into the non-overlapping portions - if (!nonOverlappingPortions.isEmpty()) { - var wp = new QWordPathsRenderer(nonOverlappingPortions); - resultJoiner.add(wp.render(reachability)); - } + resultJoiner.add(render(nonOverlappingPortions, reachability)); } } else if (commonality.size() > 1) { // The case where no words are common to all paths @@ -117,8 +120,10 @@ class QWordPathsRenderer { .sorted(Map.Entry.comparingByKey(reachability.topologicalComparator())) // Sort by topological order to ensure consistent output .map(e -> { String commonWord = e.getKey().word(); + // Recurse into the branches: - String branchPart = new QWordPathsRenderer(e.getValue()).render(reachability); + String branchPart = render(e.getValue(), reachability); + return STR."\{commonWord} \{branchPart}"; }) .collect(Collectors.joining(" | ", " ( ", " ) ")); @@ -130,4 +135,10 @@ class QWordPathsRenderer { return resultJoiner.toString().replaceAll("\\s+", " ").trim(); } + /** Compute how many paths each word is part of */ + private Map nodeCommonality() { + return paths.stream().flatMap(QWordPath::stream) + .collect(Collectors.groupingBy(w -> w, Collectors.summingInt(w -> 1))); + } + }