Skip to content

0.61.0

Choose a tag to compare

@lmeyerov lmeyerov released this 18 Oct 21:02
· 200 commits to master since this release

Release PR: #2191

Added

  • GFQL relationship-type adjacency sidecars (#2184): gfql_index_edges(..., type_column=..., type_values=...) explicitly builds forward and reverse adjacency indexes for selected string or integer relationship values. Matching queries can narrow candidate edge rows while retaining the full edge predicate and canonical results; queries without a live sidecar use ordinary adjacency. Sidecars are built at graph setup, share neighbor arrays with ordinary indexes, and appear as typed: rows with incremental CSR bytes in show_indexes(). Typed options are keyword-only. cuDF builds may add overhead on dense-community shapes, and sidecars must be rebuilt after rebinding or changing edges.

  • GFQL Cypher backtick identifiers: property names, aliases, labels, relationship types, and map keys can preserve spaces, dots, hyphens, escaped backticks, and reserved words through parsing, binding, lowering, and rendering. Unterminated backticks remain syntax errors, and unquoted dotted-property semantics are unchanged (#2198).

  • GFQL node-type segments and indexed destination filtering (#2192, #2193): gfql_index_node_types(..., type_column=..., type_values=...) explicitly builds node row-position segments for selected scalar types or Boolean labels. Matching seeded wavefront hops can narrow source and destination candidates while retaining canonical node filters and results; queries without a live segment use the ordinary paths. A simple Polars plain-chain destination node predicate may additionally lower into an indexed direct hop; this narrow lowering is separate from general node-segment support. Segments are built at graph setup, appear as typed_node: node_type_segment rows in show_indexes(), and selective drop supports all segments, one column, or one segment.

  • GFQL Cypher aggregates over relationship rows: in a single required MATCH, aggregates such as MATCH (a)-[r]->(b) RETURN a, count(*), sum(r.w), collect(b.id) run on per-path binding rows, so group keys and arguments can reference any node or relationship alias, including DISTINCT, post-aggregate arithmetic (count(b) + 1) and multi-alias keys. These shapes previously raised unsupported-query errors.

  • GFQL DateTimeValue.from_epoch (#2197): construct an explicit UTC instant from epoch seconds or milliseconds with unit="s"|"ms"; typed wire datetime values accept the equivalent {"type": "datetime", "value": ..., "unit": "s"|"ms"} form.

Fixed

  • GFQL comparison predicates and typed wire inputs reject numeric-versus-datetime comparisons: a bare integer no longer silently compares as an opaque unitless value against a datetime column. Use a typed ISO datetime value or explicit DateTimeValue.from_epoch UTC epoch units. There is no default epoch unit, and epoch_ms predicate flags are rejected (#2197). This boundary does not change all mixed-type Cypher EQ/NE semantics.

  • GFQL Call runtime failures use structured E306: exceptions from safelisted handlers are wrapped as GFQLCallExecutionError with the handler name, requested engine, original exception type, and original exception as __cause__; input/reference validation retains its existing structured codes (#2199).

  • GFQL index byte reporting is engine-aware (#2201): show_indexes() reports Polars and cuDF sidecar/segment bytes without assuming a pandas-only Series interface.

  • GFQL Polars indexed connected bindings: the default planner gate now admits moderately sized key frontiers and edge gathers. Measured on LDBC SF1 IC4; other families showed no regression, and the relaxation is not claimed to generalize. Explicit cost-gate overrides still take precedence; other index paths and engines retain their defaults.

  • GFQL indexed traversal uses faster sorted uniqueness: integer index arrays with at least 128 entries and Boolean arrays with more than one entry use sorted deduplication, avoiding the slower numpy.unique path. Measured on NumPy 2.4; applies on all versions. Tiny integer arrays retain native unique because the extra sort/mask setup can cost more there; tiny Boolean arrays favor sorting. Other dtypes and cuPy retain their existing uniqueness semantics. IC4 benchmark evidence and follow-up are tracked in graphistry/pyg-bench#303.

  • Graph algorithms on Polars-bound graphs (#2025): compute_igraph, layout_igraph, compute_cugraph and layout_cugraph accept graphs bound to Polars DataFrame or LazyFrame frames. igraph runs on pandas and cuGraph on cuDF (via Arrow); results return as eager Polars frames with node id, source, destination and edge id dtypes preserved. The frame conversion helper is shared with GFQL call().

  • Layouts on Polars-bound graphs (#1966): circle_layout, tree_layout, ring_categorical_layout, ring_continuous_layout, time_ring_layout, group_in_a_box_layout, modularity_weighted_layout, mercator_layout and fa2_layout accept Polars DataFrame or LazyFrame frames. They run on pandas and return eager Polars frames, instead of crashing or silently returning pandas. Layouts with backend support honor an explicit engine; Mercator has no engine parameter, and tree's legacy engine='cudf' keyword still runs on pandas.

  • GFQL indexed bindings relationship uniqueness (#2177): the indexed connected-bindings and array path-bag routes could bind one relationship to two hops of a Cypher MATCH pattern (for example (a)-[:K]->(b)<-[:K]-(c) returned c = a through a single edge) under the default use/auto index policies. Both routes now drop paths that reuse an edge whenever two hops could match the same relationship; hops with pairwise-distinct edge types skip the check. With uniqueness enforced, undirected hops in multi-hop patterns are admitted to the indexed path.

  • GFQL Cypher count(DISTINCT <alias>) on binding rows: counted the active alias instead of the named one, so MATCH (a)-[r]->(b) WITH a, count(DISTINCT b) AS c returned 1 per group. It now counts distinct b; on Polars the shape runs natively instead of declining.