feat(graph_db): implement Neo4j fulltext search for TreeTextMemory compatibility - #2168
feat(graph_db): implement Neo4j fulltext search for TreeTextMemory compatibility#2168Timelovers wants to merge 3 commits into
Conversation
…mpatibility Add Apache Lucene-backed fulltext search via db.index.fulltext.queryNodes() for both Neo4jGraphDB (Enterprise/AuraDB) and Neo4jCommunityGraphDB. ## What This Does - Implements search_by_fulltext() with comprehensive filter support: scope, status, user_name, search_filter, threshold, and advanced filters - Adds lazy fulltext index creation (_ensure_fulltext_index) that automatically creates the index on first search invocation - Adds Lucene special character escaping (_escape_lucene_query) to safely handle user-provided query terms with special chars - Removes empty stub from Neo4jCommunityGraphDB — inherits the parent class implementation since Community Edition supports FULLTEXT INDEX ## Why This resolves two explicit TODO markers left by maintainers: - neo4j.py:1014 - neo4j_community.py:483 TreeTextMemory's keyword/fulltext recall path previously returned empty results when using Neo4j as the graph backend. This implementation makes the fulltext recall path functional for all Neo4j deployments. ## Tests - Added 20 unit tests in tests/graph_dbs/test_fulltext_search.py - Coverage: basic search, multi-word OR queries, scope/status/user_name filtering, search_filter equality, threshold post-filtering, Lucene special character escaping, and lazy index creation - All tests use mocked Neo4j driver (no external dependencies) Co-authored-by: Timelovers <46080686+Timelovers@users.noreply.github.com>
🤖 Open Code ReviewTarget: PR #2168 🔍 OpenCodeReview found 8 issue(s) in this PR. 1.
|
|
Hi maintainers 👋 Quick note on this PR — it resolves the two Added 20 unit tests with mocked driver. Let me know if anything needs adjusting. Thanks! |
❌ Automated Test Results: FAILED
Failed tests:
Error detailsBranch: |
…ling, performance, threshold - Validate search_filter keys against _VALID_PROPERTY_NAME_RE to prevent Cypher injection through crafted property names - Narrow bare except in _fulltext_index_exists to Neo4j ClientError - Move _LUCENE_SPECIAL_CHARS to module-level frozenset (avoid per-call alloc) - Push threshold filter into Cypher WHERE clause instead of Python post-filter - Fix test mock to use side_effect dispatch (avoid shared return_value) - Add injection-rejection test and wildcard-mixed-term test Co-authored-by: Timelovers <46080686+Timelovers@users.noreply.github.com>
|
Thanks for the review @Memtensor-AI. Pushed a fix for all 6 issues:
|
|
|
Hi @bittergreen — friendly ping on this one. The bot review issues have been addressed and CI checks passed. Let me know if anything else is needed. Thanks! |
1 similar comment
|
Hi @bittergreen — friendly ping on this one. The bot review issues have been addressed and CI checks passed. Let me know if anything else is needed. Thanks! |
|
friendly bump — let me know if this needs any changes. Thanks! |
- Remove dead mock side_effect assignment in test - Remove unused uuid import in test - Simplify _fulltext_index_exists: narrow except, remove double fallback - Validate index_name with _VALID_PROPERTY_NAME_RE before interpolation - Narrow wildcard guard to only * and ? - Log only param keys not values (PII protection) - Use lazy format for threshold log statement - Move ClientError import to module level Co-authored-by: Timelovers <46080686+Timelovers@users.noreply.github.com>
|
Hi @syzsunshine219 — you kindly confirmed our other PR on memmy-agent the other day. This one on MemOS has been waiting for review since July 25. Both bot review rounds passed and CI is green. Would you be able to take a look or suggest someone who can? Thanks! |
Description
Resolves two TODO markers at neo4j.py:1014 and neo4j_community.py:483 — both said "TODO: Implement fulltext search for Neo4j to be compatible with TreeTextMemory's keyword/fulltext recall path."
Uses Neo4j's built-in `db.index.fulltext.queryNodes` (Enterprise & Community) with a lazy-created Lucene fulltext index on `Memory.memory`. Follows the same filter pattern as `search_by_embedding` — scope, status, user_name, knowledgebase_ids, search_filter, threshold all work.
Removed the empty stub in Neo4jCommunityGraphDB — Community Edition supports FULLTEXT INDEX, so it inherits from the parent.
Type of change
How Has This Been Tested?
Checklist