nidx-binding 6.9.2.post622__tar.gz → 6.9.3.post624__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nidx-binding might be problematic. Click here for more details.
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/PKG-INFO +1 -1
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/pyproject.toml +1 -1
- nidx_binding-6.9.3.post624/nidx_vector/src/hnsw/build.rs +172 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/hnsw/disk_hnsw.rs +62 -52
- nidx_binding-6.9.3.post624/nidx_vector/src/hnsw/ram_hnsw.rs +178 -0
- nidx_binding-6.9.2.post622/nidx_vector/src/hnsw/ops_hnsw.rs → nidx_binding-6.9.3.post624/nidx_vector/src/hnsw/search.rs +18 -142
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/hnsw.rs +4 -2
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/segment/tests.rs +1 -1
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/segment.rs +14 -9
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/pyproject.toml +1 -1
- nidx_binding-6.9.2.post622/nidx_vector/src/hnsw/ram_hnsw.rs +0 -128
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.config/nextest.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-0cfce9b29547f8f5bafa6e440f86103be7b8c4ad2fd92db9ac223f4efbe23d10.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-1a561eed00f3dbe868bf5030059793300209179dc8fb73e4b57a54b5e81262fe.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-1d3fca2682e25a01143da92285297f134a6a105a96f64d87e0db3abb219855e4.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-249b3b57c27a71baa823f1fe0f0bba9c9af36f61c28f731e58beea60ec48e687.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-24cb6b683daa42d7125f862e25943ab4be7bf275cd8739f8da4859d701795e1a.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-263c8fce6db5b03bbd012fafdba6943cbee6ed7eb8976cdef4f5b01dde7ca6fd.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-2a5d92fb1638df830a4477a7cdf24e6db6b43034b7bbe74fdfb63e8afe2c4071.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-2b065a363f58caed60e3706603c1260dbf5a4c795604a5b68edda22eb07fec1b.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-3fc3cb39934683de8cd475ce1368c8373453eb1e01f81587d66b9d14b109ce6e.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-48f33b77b7c1633467b0b2efcaa1d3c207e7757e4f1d83b40d15e6ca365f7771.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-4ae09f2c08e2f324bee01bb8487a8f37678a1c5e9d327339235c50d4921a8949.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-4d7a76fa413c9ef0ce2a47ac7bb7e01d3e6a2aabded9487d21010a53efee8852.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-4fcbdd6657c7dc9b60b3a563dd41711b3dbcf72ce063427b7a01f8cddf34c244.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-577109ac00ccfbd38ecaccab94116f2f46a4caf5612afa372cded197123c1e08.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-5db25f97d8578d6d78f2f6bd4b72cc82a9b1b82805c6422d967ac63b20d99db4.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-5ec3233a3a23e926055056d46bdde17836a633066dbb5f349502648cd3ea9a60.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-66edb6ea424d8681927dcddb6bac5f1239175f4775d1f40417ba15054b0c6f19.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-6f9c6d201c1b5712efb68c363bffd3e0169c11f2a8f925e8cd4e8808599ff7b4.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-733c3ebacc86f444bf5e2dd79ade660c291e88a00fc09b722f6e2e191545874c.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-7a3bf27c330c468a596e8a297cf7d8b192e31e67ecc5177c1267f579e8e247c7.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-7a7e59e47b30b12237511fd3d7da2d17b0471ad2b006af48d6a6f587c779692b.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-7dcbb33312cc9f11ae3a6d73b1ace017a9f19a8bf8f10304fc57977c8efeadff.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-7efa7c0d747afc4b6aed0586ff846c27839c3213ff7ee9f30c89b0d0f17e60e3.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-8493140d788604d498a4e48da4158708572ccc9d60185290a00d549cc84533db.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-8493bb0059b013eaca42fd10cd7d04f0d06a8acaed379eff0d23f3229edde9ee.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-87996b3d6c7a2195438d7038015b06949102bce8c7b8cd8db1f83aaf23cbe489.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-8d33717587c6ee8f5fc339a80b1212a73d6c03e45856b1d55457fc8074709dd0.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-8f096d8171b89f9615d18f95d696dc9e4fb3674e103161a713cdc806f7a68506.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-917732a56ee04bf3a6e127319dda8225210869c82f9828d878162394dba4e078.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-95fe4ef93ee90733db1b67ed7987f80b5aac792f1590b979c68b418d1599eb98.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-9b67658569b343d8b4b61ae0a7dc721f367f2ba33c7b69b9e68bfd5c9bff5206.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-9c8062ea55d070afef68309e58fa987eb37fda44e1efbf68c8ba2af7846cc968.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-a06e1d9f6f95e4c4c2b98310ebddcc9d963cc033582bf2e945e8bf3a301b4247.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-a55265c9b07bd1399961a6f1e757201fd0eebe868ddaf96437111113d80fce92.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-a60ec2f66f1e7b84189e5b089f2087a29ff6a64326a3743dea935bbc58ee77fa.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-a891a37be5c2d7cce775c2dd33726b0318fd3839beab222a1b22bc6174604207.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-a945191bb4b3e37d6823ed3ad499339d007d69983105de8567777d9daf517b28.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-abe9f7832f2bd799ac44008da031e8d8ab52d4f5fbfc2a7e3974e8873bae55b2.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-aca588cca57a85e4d7fcc40c23cd87e57d53d11ca550d78e7e3d5e39e524fcd3.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-b02f8aafc00a7724510772ac41269e368c5bccf03ef7b4590e0ef6fd1a1bf64f.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-b742e17cabe2d64617e9aa64bafc782172f7a4f8023d1b54f952a0fb39f6b2b8.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-b94e349dbc0daec57f8f8f6e9e2dffb06100b1bb2b41d297c9f3b191da37a83d.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-bd9afa22994aba671dbf7b5f89b53c2ee02f53c0442a81265786a6d52d08512f.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-be60554eca98a5899efc6b49785cecd6444a6d39afed9e4a884ce2dbf162012c.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-bf49702b506c9a1650ece1f8e8d9f14834a902f8caefafe30ded55e2790f2188.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-bfcd21ed704cd305db5c17fcdec7d92aa4ac501913c9c9514d8ff92928c0c7e7.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-c3ab694650f49a75b146fb877a92e48c4f20f0d99f70f8ec859fbb763b01a1e5.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-c55542bb9fae544d87fae6f30e0fe8a9088d12075f4442ab4fe2fcd05e472234.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-cb29a6556d35ac630ee0aa885dd7341cf9573bd3efd216ff8a887b87686b03db.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-d0a1f341a89f5f14696b10baa72db9d95551c2b7e5fc67308fd52dc03dd98a92.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-d2ad0a0ca2649c9e4873cfcc1fc66d2d07cc45d0f65c560b06d7b5f592f4fa8a.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-d6cfe78eb635ba0b89ca4021a4dc8182d18ab5b197f30149cd28488eba4c1df5.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-d729b56dea00e49dcdba8cf0001e2811da27351eabe98212db3b589f18fc6f32.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-d9658bfd4e7170b41d03f2ddf2446d0bf54171c0d39d53bf20af2b8437f2ec48.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-dbba7b3d3289425bae711aedbf73fbc3699f857f86f84d95c3b556d05c5658b0.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-dcb96b649d6d63a58efd5d445453a4f3d7869a56ff714b69bedf3d616a0473ca.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-ebd876fbf5362a5900e75bc05f2f11c73c406ef7da4e95097fc6a1c3d1b8bc54.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-eef5cc6bce1cc14eba8f3e68971724ef181e88cffcedd74673615f2026b89a62.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/.sqlx/query-ef56d5fefc5774040d1ee397beadb475f6af02768c22f0e583c74062e2e821ce.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/Cargo.lock +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/README.md +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/migrations/20241007163501_initial.sql +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/migrations/20241211120039_merge_job_priority.sql +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/migrations/20241211121159_basic_indexes.sql +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/migrations/20241212151105_check_segment_records.sql +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/migrations/20250110145554_in_flight_messages.sql +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_binding/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_binding/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/fuzzy_query.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/query_io.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/query_parser/fuzzy_parser.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/query_parser/keyword_parser.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/query_parser/stop_words.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/query_parser/tokenizer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/query_parser.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/reader.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/request_types.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/resource_indexer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/schema.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/search_query.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/search_response.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/src/set_query.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/README.md +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/ar.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/az.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/bn.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/ca.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/ch.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/da.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/de.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/el.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/en.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/es.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/eu.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/extract.py +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/fi.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/fr.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/he.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/hu.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/id.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/it.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/kk.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/ne.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/nl.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/no.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/pt.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/ro.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/ru.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/sl.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/sv.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/tg.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/stop_words/tr.json +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/tests/common/mod.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_paragraph/tests/reader.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/build.py +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/build.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/nidx.proto +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/nodereader.proto +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/noderesources.proto +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/nodewriter.proto +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_protos/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/graph_collector.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/graph_query_parser.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/io_maps.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/reader.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/resource_indexer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/schema.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/src/top_unique_n.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/tests/common/mod.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/tests/test_graph_query_parser_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/tests/test_graph_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_relation/tests/test_writer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tantivy/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tantivy/src/index_reader.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tantivy/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tantivy/src/utils.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tests/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tests/src/graph.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_tests/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/prefilter.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/query_io.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/reader.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/request_types.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/resource_indexer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/schema.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/src/search_query.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/tests/common/mod.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/tests/test_deletions.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/tests/test_flow.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/tests/test_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_text/tests/test_streaming.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_types/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_types/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_types/src/prefilter.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_types/src/query_language.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/Cargo.toml +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/config.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v1/node.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v1/store.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v1/trie.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v1/trie_ram.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v1.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v2/paragraph_store.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v2/quant_vector_store.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v2/vector_store.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store/v2.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_store.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/data_types.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/formula.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/hnsw/params.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/indexer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/inverted_index/fst_index.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/inverted_index/map.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/inverted_index.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/multivector.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/query_io.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/request_types.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/searcher.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/utils.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/vector_types/dense_f32.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/vector_types/mod.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/src/vector_types/rabitq.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/tests/common/mod.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/tests/test_basic_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/tests/test_hidden.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/nidx_vector/tests/test_maxsim.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/api/grpc.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/api/shards.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/api.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/control.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/errors.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/grpc_server.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/import_export.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/indexer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/lib.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/main.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata/deletion.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata/index.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata/index_request.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata/merge_job.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata/segment.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata/shard.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metadata.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/metrics.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler/audit_task.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler/log_merge.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler/merge_task.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler/metrics_task.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler/purge_tasks.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler/vector_merge.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/scheduler.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/grpc.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/index_cache.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/query_language.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/query_planner.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/shard_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/shard_selector.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/shard_suggest.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/streams.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher/sync.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/searcher.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/segment_store.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/settings.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/telemetry/duration_layer.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/telemetry/log_format.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/telemetry/middleware.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/telemetry.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/tool.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/utilization_tracker.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/src/worker.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/common/mod.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/common/services.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_date_range_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_search_filtering.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_search_relations.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_search_sorting.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_searcher_cluster.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_security_search.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_shards.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_shards_api.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_suggest.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_synced_searcher.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_vector_normalization.rs +0 -0
- {nidx_binding-6.9.2.post622 → nidx_binding-6.9.3.post624}/tests/test_vectorsets.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nidx_binding
|
|
3
|
-
Version: 6.9.
|
|
3
|
+
Version: 6.9.3.post624
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
@@ -10,7 +10,7 @@ build-backend = "pdm.backend"
|
|
|
10
10
|
|
|
11
11
|
[project]
|
|
12
12
|
name = "nidx_protos"
|
|
13
|
-
version = "6.9.
|
|
13
|
+
version = "6.9.3.post624"
|
|
14
14
|
license = "AGPL-3.0-or-later"
|
|
15
15
|
description = "Protobuf definitions for nucliadb/nidx"
|
|
16
16
|
authors = [{ name = "Nuclia", email = "nucliadb@nuclia.com" }]
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Copyright (C) 2021 Bosutech XXI S.L.
|
|
2
|
+
//
|
|
3
|
+
// nucliadb is offered under the AGPL v3.0 and as commercial software.
|
|
4
|
+
// For commercial licensing, contact us at info@nuclia.com.
|
|
5
|
+
//
|
|
6
|
+
// AGPL:
|
|
7
|
+
// This program is free software: you can redistribute it and/or modify
|
|
8
|
+
// it under the terms of the GNU Affero General Public License as
|
|
9
|
+
// published by the Free Software Foundation, either version 3 of the
|
|
10
|
+
// License, or (at your option) any later version.
|
|
11
|
+
//
|
|
12
|
+
// This program is distributed in the hope that it will be useful,
|
|
13
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
// GNU Affero General Public License for more details.
|
|
16
|
+
//
|
|
17
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
//
|
|
20
|
+
|
|
21
|
+
use std::collections::BinaryHeap;
|
|
22
|
+
|
|
23
|
+
use rand::{Rng, SeedableRng, distributions::Uniform, rngs::SmallRng};
|
|
24
|
+
|
|
25
|
+
use crate::{
|
|
26
|
+
VectorAddr,
|
|
27
|
+
hnsw::{
|
|
28
|
+
Cnx, DataRetriever, HnswSearcher, RAMHnsw, SearchVector, params,
|
|
29
|
+
ram_hnsw::{Edge, RAMLayer},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
pub struct HnswBuilder<'a, DR> {
|
|
34
|
+
distribution: Uniform<f64>,
|
|
35
|
+
layer_rng: SmallRng,
|
|
36
|
+
retriever: &'a DR,
|
|
37
|
+
searcher: HnswSearcher<'a, DR>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl<'a, DR: DataRetriever> HnswBuilder<'a, DR> {
|
|
41
|
+
pub fn new(retriever: &DR) -> HnswBuilder<'_, DR> {
|
|
42
|
+
HnswBuilder {
|
|
43
|
+
retriever,
|
|
44
|
+
distribution: Uniform::new(0.0, 1.0),
|
|
45
|
+
layer_rng: SmallRng::seed_from_u64(2),
|
|
46
|
+
searcher: HnswSearcher::new(retriever, false),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/// Initialize the graph holding the specified number of vectors
|
|
51
|
+
/// Each vector is assigned to a random amount of layers but no
|
|
52
|
+
/// edges are built yet. If the node is preinitialized, you can skip
|
|
53
|
+
/// an initial amount of nodes so they aren't recreated
|
|
54
|
+
pub fn initialize_graph(&mut self, hnsw: &mut RAMHnsw, skip_nodes: u32, total_nodes: u32) {
|
|
55
|
+
for node_id in skip_nodes..total_nodes {
|
|
56
|
+
let top_layer = self.get_random_layer();
|
|
57
|
+
hnsw.add_node(VectorAddr(node_id), top_layer);
|
|
58
|
+
}
|
|
59
|
+
hnsw.update_entry_point();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fn select_neighbours_heuristic(
|
|
63
|
+
&self,
|
|
64
|
+
k_neighbours: usize,
|
|
65
|
+
candidates: &[(VectorAddr, Edge)],
|
|
66
|
+
) -> Vec<(VectorAddr, Edge)> {
|
|
67
|
+
let mut results = Vec::new();
|
|
68
|
+
let mut discarded = BinaryHeap::new();
|
|
69
|
+
|
|
70
|
+
// First, select the best candidates to link, trying to connect from all directions
|
|
71
|
+
// i.e: avoid linking to all nodes in a single cluster
|
|
72
|
+
for (x, sim) in candidates.iter().copied() {
|
|
73
|
+
if results.len() == k_neighbours {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
// Keep if x is more similar to the new node than it is similar to other results
|
|
77
|
+
// i.e: similarity(x, new) > similarity(x, y) for all y in result
|
|
78
|
+
let check = results
|
|
79
|
+
.iter()
|
|
80
|
+
.map(|&(y, _)| self.retriever.similarity(x, &SearchVector::Stored(y)))
|
|
81
|
+
.all(|inter_sim| sim > inter_sim);
|
|
82
|
+
if check {
|
|
83
|
+
results.push((x, sim));
|
|
84
|
+
} else {
|
|
85
|
+
discarded.push(Cnx(x, sim));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// keepPrunedConnections: keep some other connections to fill M
|
|
90
|
+
if results.len() < k_neighbours {
|
|
91
|
+
while results.len() < k_neighbours {
|
|
92
|
+
let Some(Cnx(n, d)) = discarded.pop() else { break };
|
|
93
|
+
results.push((n, d));
|
|
94
|
+
}
|
|
95
|
+
// Sort the list since the newly added connections might be out of order
|
|
96
|
+
results.sort_unstable_by(|y, x| x.1.total_cmp(&y.1));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
results
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fn get_random_layer(&mut self) -> usize {
|
|
103
|
+
let sample: f64 = self.layer_rng.sample(self.distribution);
|
|
104
|
+
let picked_level = -sample.ln() * params::level_factor();
|
|
105
|
+
picked_level.round() as usize
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// Insert a node into a layer, calculating all edges
|
|
109
|
+
fn layer_insert(&self, x: VectorAddr, layer: &RAMLayer, search_neighbours: Vec<(VectorAddr, Edge)>, mmax: usize) {
|
|
110
|
+
let neighbours = self.select_neighbours_heuristic(params::M, &search_neighbours);
|
|
111
|
+
|
|
112
|
+
// Set edges from this node to neighbours
|
|
113
|
+
*layer.out.get(&x).unwrap().write().unwrap() = neighbours.clone();
|
|
114
|
+
|
|
115
|
+
// Set edges from neighbours to this node
|
|
116
|
+
for (y, dist) in neighbours.iter().copied() {
|
|
117
|
+
let other_node = layer.out.get(&y).unwrap();
|
|
118
|
+
let mut other_edges = other_node.write().unwrap();
|
|
119
|
+
other_edges.push((x, dist));
|
|
120
|
+
if other_edges.len() > mmax {
|
|
121
|
+
*other_edges = self.select_neighbours_heuristic(params::prune_m(mmax), &other_edges);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/// Insert a node into all corresponding layers.
|
|
127
|
+
/// The node must be created first by calling initialize_graph()
|
|
128
|
+
pub fn insert(&self, node: VectorAddr, hnsw: &RAMHnsw) {
|
|
129
|
+
debug_assert!(!hnsw.layers.is_empty());
|
|
130
|
+
debug_assert!(node.0 < hnsw.layers[0].out.len() as u32);
|
|
131
|
+
|
|
132
|
+
let mut search_ep = vec![hnsw.entry_point.node];
|
|
133
|
+
let vector = SearchVector::Stored(node);
|
|
134
|
+
|
|
135
|
+
// The neighbours of the node at each layer, for insertion
|
|
136
|
+
let mut layer_neighbours = Vec::with_capacity(hnsw.no_layers());
|
|
137
|
+
let mut node_in_layer = false;
|
|
138
|
+
|
|
139
|
+
// First, find the neighbours for each layer the node appears in.
|
|
140
|
+
for l in (0..hnsw.no_layers()).rev() {
|
|
141
|
+
if !node_in_layer && (l == 0 || hnsw.layers[l].contains(&node)) {
|
|
142
|
+
node_in_layer = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// On upper layers, find 1 neighbour (as entrypoint to next layer)
|
|
146
|
+
// On layers where the inserted node appears, find efC neighbours for creating links
|
|
147
|
+
let k_neighbours = if node_in_layer { params::EF_CONSTRUCTION } else { 1 };
|
|
148
|
+
|
|
149
|
+
let search_results: Vec<_> = self
|
|
150
|
+
.searcher
|
|
151
|
+
.layer_search(&vector, &hnsw.layers[l], k_neighbours, &search_ep)
|
|
152
|
+
.map(|x| (x.0, x.1.score))
|
|
153
|
+
.collect();
|
|
154
|
+
|
|
155
|
+
search_ep = search_results.iter().map(|x| x.0).collect();
|
|
156
|
+
if node_in_layer {
|
|
157
|
+
// If finding neighbours, store them for insertion later
|
|
158
|
+
layer_neighbours.push(search_results);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Insert all neighbours from the bottom layer up. This is done so that it's not possible to
|
|
163
|
+
// find a node on a top layer before the edges are set on the lower layers because this can cause
|
|
164
|
+
// problems during parallel insertion.
|
|
165
|
+
// If edges are inserted from top to bottom, another worker might find the node in layer N and
|
|
166
|
+
// follow it down to layer N-1 where edges are not yet set. Then, it cannot find neighbours in this
|
|
167
|
+
// layer and the search gets stuck (no links to follow) resulting in a node with low connectivity.
|
|
168
|
+
for (layer, neighbours) in layer_neighbours.into_iter().rev().enumerate() {
|
|
169
|
+
self.layer_insert(node, &hnsw.layers[layer], neighbours, params::m_max_for_layer(layer));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
use std::collections::HashMap;
|
|
47
47
|
use std::io;
|
|
48
48
|
|
|
49
|
-
use super::ops_hnsw::{Hnsw, Layer};
|
|
50
49
|
use super::ram_hnsw::{Edge, EntryPoint, RAMHnsw, RAMLayer};
|
|
50
|
+
use super::search::{SearchableHnsw, SearchableLayer};
|
|
51
51
|
use crate::VectorAddr;
|
|
52
52
|
use crate::data_types::usize_utils::*;
|
|
53
53
|
|
|
@@ -66,7 +66,7 @@ pub struct DiskLayer<'a> {
|
|
|
66
66
|
layer: usize,
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
impl<'a>
|
|
69
|
+
impl<'a> SearchableLayer for &'a DiskLayer<'a> {
|
|
70
70
|
type EdgeIt = EdgeIter<'a>;
|
|
71
71
|
fn get_out_edges(&self, address: VectorAddr) -> Self::EdgeIt {
|
|
72
72
|
let node = DiskHnsw::get_node(self.hnsw, address);
|
|
@@ -74,7 +74,7 @@ impl<'a> Layer for &'a DiskLayer<'a> {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
impl<'a>
|
|
77
|
+
impl<'a> SearchableLayer for DiskLayer<'a> {
|
|
78
78
|
type EdgeIt = EdgeIter<'a>;
|
|
79
79
|
fn get_out_edges(&self, address: VectorAddr) -> Self::EdgeIt {
|
|
80
80
|
let node = DiskHnsw::get_node(self.hnsw, address);
|
|
@@ -82,9 +82,9 @@ impl<'a> Layer for DiskLayer<'a> {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
impl<'a>
|
|
85
|
+
impl<'a> SearchableHnsw for &'a [u8] {
|
|
86
86
|
type L = DiskLayer<'a>;
|
|
87
|
-
fn get_entry_point(&self) ->
|
|
87
|
+
fn get_entry_point(&self) -> EntryPoint {
|
|
88
88
|
DiskHnsw::get_entry_point(self)
|
|
89
89
|
}
|
|
90
90
|
fn get_layer(&self, i: usize) -> Self::L {
|
|
@@ -124,14 +124,16 @@ impl DiskHnsw {
|
|
|
124
124
|
let mut length = offset;
|
|
125
125
|
let mut indexing = HashMap::new();
|
|
126
126
|
for layer in 0..hnsw.no_layers() {
|
|
127
|
-
let
|
|
127
|
+
let num_edges = hnsw.get_layer(layer).num_out_edges(&node);
|
|
128
128
|
indexing.insert(layer, length);
|
|
129
|
-
buf.write_all(&
|
|
129
|
+
buf.write_all(&num_edges.to_le_bytes())?;
|
|
130
130
|
length += USIZE_LEN;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
if num_edges > 0 {
|
|
132
|
+
for (cnx, edge) in hnsw.get_layer(layer).get_out_edges(node) {
|
|
133
|
+
buf.write_all(&(cnx.0 as usize).to_le_bytes())?;
|
|
134
|
+
buf.write_all(&edge.to_le_bytes())?;
|
|
135
|
+
length += CNX_LEN;
|
|
136
|
+
}
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
for layer in (0..hnsw.no_layers()).rev() {
|
|
@@ -158,38 +160,40 @@ impl DiskHnsw {
|
|
|
158
160
|
}
|
|
159
161
|
}
|
|
160
162
|
pub fn serialize_into<W: io::Write>(mut buf: W, no_nodes: u32, hnsw: RAMHnsw) -> io::Result<()> {
|
|
161
|
-
if
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
buf.write_all(&
|
|
174
|
-
|
|
175
|
-
let _length = length + 2 * USIZE_LEN;
|
|
176
|
-
buf.flush()?;
|
|
163
|
+
if no_nodes == 0 {
|
|
164
|
+
// Empty graph, nothing to serialize
|
|
165
|
+
return Ok(());
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let mut length = 0;
|
|
169
|
+
let mut nodes_end = vec![];
|
|
170
|
+
for node in 0..no_nodes {
|
|
171
|
+
length = DiskHnsw::serialize_node(&mut buf, length, node, &hnsw)?;
|
|
172
|
+
nodes_end.push(length)
|
|
173
|
+
}
|
|
174
|
+
for ends_at in nodes_end.into_iter().rev() {
|
|
175
|
+
buf.write_all(&ends_at.to_le_bytes())?;
|
|
176
|
+
length += USIZE_LEN;
|
|
177
177
|
}
|
|
178
|
+
let EntryPoint { node, layer } = hnsw.entry_point;
|
|
179
|
+
buf.write_all(&layer.to_le_bytes())?;
|
|
180
|
+
buf.write_all(&(node.0 as usize).to_le_bytes())?;
|
|
181
|
+
let _length = length + 2 * USIZE_LEN;
|
|
182
|
+
buf.flush()?;
|
|
183
|
+
|
|
178
184
|
Ok(())
|
|
179
185
|
}
|
|
180
186
|
// hnsw must be serialized using DiskHnsw, may have trailing bytes at the start.
|
|
181
|
-
pub fn get_entry_point(hnsw: &[u8]) ->
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
} else {
|
|
192
|
-
None
|
|
187
|
+
pub fn get_entry_point(hnsw: &[u8]) -> EntryPoint {
|
|
188
|
+
assert!(!hnsw.is_empty());
|
|
189
|
+
|
|
190
|
+
let node_start = hnsw.len() - USIZE_LEN;
|
|
191
|
+
let layer_start = node_start - USIZE_LEN;
|
|
192
|
+
let node_addr = usize_from_slice_le(&hnsw[node_start..(node_start + NODE_LEN)]);
|
|
193
|
+
let layer = usize_from_slice_le(&hnsw[layer_start..(layer_start + USIZE_LEN)]);
|
|
194
|
+
EntryPoint {
|
|
195
|
+
node: VectorAddr(node_addr as u32),
|
|
196
|
+
layer,
|
|
193
197
|
}
|
|
194
198
|
}
|
|
195
199
|
// hnsw must be serialized using MHnsw, may have trailing bytes at the start.
|
|
@@ -231,7 +235,12 @@ impl DiskHnsw {
|
|
|
231
235
|
let cnx_end = cnx_start + number_edges * CNX_LEN;
|
|
232
236
|
|
|
233
237
|
if number_edges > 0 {
|
|
234
|
-
let ram_edges = ram.layers[layer_index]
|
|
238
|
+
let mut ram_edges = ram.layers[layer_index]
|
|
239
|
+
.out
|
|
240
|
+
.entry(VectorAddr(node_index))
|
|
241
|
+
.or_default()
|
|
242
|
+
.write()
|
|
243
|
+
.unwrap();
|
|
235
244
|
let edges = EdgeIter {
|
|
236
245
|
crnt: 0,
|
|
237
246
|
buf: &hnsw[cnx_start..cnx_end],
|
|
@@ -259,9 +268,11 @@ impl DiskHnsw {
|
|
|
259
268
|
|
|
260
269
|
#[cfg(test)]
|
|
261
270
|
mod tests {
|
|
271
|
+
use std::sync::RwLock;
|
|
272
|
+
|
|
262
273
|
use super::*;
|
|
263
274
|
use crate::hnsw::ram_hnsw::RAMLayer;
|
|
264
|
-
fn layer_check<L:
|
|
275
|
+
fn layer_check<L: SearchableLayer>(buf: L, no_nodes: u32, cnx: &[Vec<(VectorAddr, Edge)>]) {
|
|
265
276
|
let no_cnx = vec![];
|
|
266
277
|
for i in 0..no_nodes {
|
|
267
278
|
let expected = cnx.get(i as usize).unwrap_or(&no_cnx);
|
|
@@ -274,8 +285,7 @@ mod tests {
|
|
|
274
285
|
let hnsw = RAMHnsw::new();
|
|
275
286
|
let mut buf = vec![];
|
|
276
287
|
DiskHnsw::serialize_into(&mut buf, 0, hnsw).unwrap();
|
|
277
|
-
|
|
278
|
-
assert_eq!(ep, None);
|
|
288
|
+
assert!(buf.is_empty());
|
|
279
289
|
}
|
|
280
290
|
|
|
281
291
|
#[test]
|
|
@@ -290,7 +300,7 @@ mod tests {
|
|
|
290
300
|
out: cnx0
|
|
291
301
|
.iter()
|
|
292
302
|
.enumerate()
|
|
293
|
-
.map(|(i, c)| (VectorAddr(i as u32), c.clone()))
|
|
303
|
+
.map(|(i, c)| (VectorAddr(i as u32), RwLock::new(c.clone())))
|
|
294
304
|
.collect(),
|
|
295
305
|
};
|
|
296
306
|
let cnx1 = vec![vec![(VectorAddr(1), 4.0)], vec![(VectorAddr(2), 5.0)]];
|
|
@@ -298,7 +308,7 @@ mod tests {
|
|
|
298
308
|
out: cnx1
|
|
299
309
|
.iter()
|
|
300
310
|
.enumerate()
|
|
301
|
-
.map(|(i, c)| (VectorAddr(i as u32), c.clone()))
|
|
311
|
+
.map(|(i, c)| (VectorAddr(i as u32), RwLock::new(c.clone())))
|
|
302
312
|
.collect(),
|
|
303
313
|
};
|
|
304
314
|
let cnx2 = vec![vec![(VectorAddr(1), 6.0)]];
|
|
@@ -306,7 +316,7 @@ mod tests {
|
|
|
306
316
|
out: cnx2
|
|
307
317
|
.iter()
|
|
308
318
|
.enumerate()
|
|
309
|
-
.map(|(i, c)| (VectorAddr(i as u32), c.clone()))
|
|
319
|
+
.map(|(i, c)| (VectorAddr(i as u32), RwLock::new(c.clone())))
|
|
310
320
|
.collect(),
|
|
311
321
|
};
|
|
312
322
|
let entry_point = EntryPoint {
|
|
@@ -314,11 +324,11 @@ mod tests {
|
|
|
314
324
|
layer: 2,
|
|
315
325
|
};
|
|
316
326
|
let mut hnsw = RAMHnsw::new();
|
|
317
|
-
hnsw.entry_point =
|
|
327
|
+
hnsw.entry_point = entry_point;
|
|
318
328
|
hnsw.layers = vec![layer0, layer1, layer2];
|
|
319
329
|
let mut buf = vec![];
|
|
320
330
|
DiskHnsw::serialize_into(&mut buf, no_nodes, hnsw).unwrap();
|
|
321
|
-
let ep = DiskHnsw::get_entry_point(&buf)
|
|
331
|
+
let ep = DiskHnsw::get_entry_point(&buf);
|
|
322
332
|
assert_eq!(ep, entry_point);
|
|
323
333
|
let layer0 = buf.as_slice().get_layer(0);
|
|
324
334
|
layer_check(layer0, no_nodes, &cnx0);
|
|
@@ -340,7 +350,7 @@ mod tests {
|
|
|
340
350
|
out: cnx0
|
|
341
351
|
.iter()
|
|
342
352
|
.enumerate()
|
|
343
|
-
.map(|(i, c)| (VectorAddr(i as u32), c.clone()))
|
|
353
|
+
.map(|(i, c)| (VectorAddr(i as u32), RwLock::new(c.clone())))
|
|
344
354
|
.collect(),
|
|
345
355
|
};
|
|
346
356
|
let cnx1 = [vec![(VectorAddr(1), 4.0)], vec![(VectorAddr(2), 5.0)]];
|
|
@@ -348,7 +358,7 @@ mod tests {
|
|
|
348
358
|
out: cnx1
|
|
349
359
|
.iter()
|
|
350
360
|
.enumerate()
|
|
351
|
-
.map(|(i, c)| (VectorAddr(i as u32), c.clone()))
|
|
361
|
+
.map(|(i, c)| (VectorAddr(i as u32), RwLock::new(c.clone())))
|
|
352
362
|
.collect(),
|
|
353
363
|
};
|
|
354
364
|
let cnx2 = [vec![(VectorAddr(1), 6.0)]];
|
|
@@ -356,7 +366,7 @@ mod tests {
|
|
|
356
366
|
out: cnx2
|
|
357
367
|
.iter()
|
|
358
368
|
.enumerate()
|
|
359
|
-
.map(|(i, c)| (VectorAddr(i as u32), c.clone()))
|
|
369
|
+
.map(|(i, c)| (VectorAddr(i as u32), RwLock::new(c.clone())))
|
|
360
370
|
.collect(),
|
|
361
371
|
};
|
|
362
372
|
let entry_point = EntryPoint {
|
|
@@ -364,7 +374,7 @@ mod tests {
|
|
|
364
374
|
layer: 2,
|
|
365
375
|
};
|
|
366
376
|
let mut hnsw = RAMHnsw::new();
|
|
367
|
-
hnsw.entry_point =
|
|
377
|
+
hnsw.entry_point = entry_point;
|
|
368
378
|
hnsw.layers = vec![layer0, layer1, layer2];
|
|
369
379
|
let mut buf = vec![];
|
|
370
380
|
DiskHnsw::serialize_into(&mut buf, no_nodes, hnsw).unwrap();
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Copyright (C) 2021 Bosutech XXI S.L.
|
|
2
|
+
//
|
|
3
|
+
// nucliadb is offered under the AGPL v3.0 and as commercial software.
|
|
4
|
+
// For commercial licensing, contact us at info@nuclia.com.
|
|
5
|
+
//
|
|
6
|
+
// AGPL:
|
|
7
|
+
// This program is free software: you can redistribute it and/or modify
|
|
8
|
+
// it under the terms of the GNU Affero General Public License as
|
|
9
|
+
// published by the Free Software Foundation, either version 3 of the
|
|
10
|
+
// License, or (at your option) any later version.
|
|
11
|
+
//
|
|
12
|
+
// This program is distributed in the hope that it will be useful,
|
|
13
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
// GNU Affero General Public License for more details.
|
|
16
|
+
//
|
|
17
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
//
|
|
20
|
+
|
|
21
|
+
use std::sync::{RwLock, RwLockReadGuard};
|
|
22
|
+
|
|
23
|
+
use rustc_hash::FxHashMap;
|
|
24
|
+
use search::{SearchableHnsw, SearchableLayer};
|
|
25
|
+
|
|
26
|
+
use crate::VectorAddr;
|
|
27
|
+
|
|
28
|
+
use super::*;
|
|
29
|
+
|
|
30
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
31
|
+
pub struct EntryPoint {
|
|
32
|
+
pub node: VectorAddr,
|
|
33
|
+
pub layer: usize,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub type Edge = f32;
|
|
37
|
+
|
|
38
|
+
#[derive(Default)]
|
|
39
|
+
pub struct RAMLayer {
|
|
40
|
+
pub(super) out: FxHashMap<VectorAddr, RwLock<Vec<(VectorAddr, Edge)>>>,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
impl RAMLayer {
|
|
44
|
+
pub fn new() -> RAMLayer {
|
|
45
|
+
RAMLayer::default()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn add_node(&mut self, node: VectorAddr) {
|
|
49
|
+
self.out.entry(node).or_default();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn contains(&self, node: &VectorAddr) -> bool {
|
|
53
|
+
self.out.contains_key(node)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pub fn num_out_edges(&self, node: &VectorAddr) -> usize {
|
|
57
|
+
// This is called by the serialization code for all nodes and each layer, so we need to handle non-existing nodes
|
|
58
|
+
self.out.get(node).map(|n| n.read().unwrap().len()).unwrap_or(0)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Remove any links that point to a node which is not in this layer
|
|
62
|
+
/// See RAMHnsw.fix_broken_links for details
|
|
63
|
+
fn fix_broken_links(&self) {
|
|
64
|
+
for edges in self.out.values() {
|
|
65
|
+
let mut edges = edges.write().unwrap();
|
|
66
|
+
edges.retain(|e| self.out.contains_key(&e.0));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pub struct RAMHnsw {
|
|
72
|
+
pub entry_point: EntryPoint,
|
|
73
|
+
pub layers: Vec<RAMLayer>,
|
|
74
|
+
}
|
|
75
|
+
impl RAMHnsw {
|
|
76
|
+
pub fn new() -> RAMHnsw {
|
|
77
|
+
Self {
|
|
78
|
+
entry_point: EntryPoint {
|
|
79
|
+
node: VectorAddr(0),
|
|
80
|
+
layer: 0,
|
|
81
|
+
},
|
|
82
|
+
layers: vec![],
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Adds a node to the graph at all layers below the selected top layer
|
|
87
|
+
pub fn add_node(&mut self, node: VectorAddr, top_layer: usize) {
|
|
88
|
+
for _ in self.layers.len()..=top_layer {
|
|
89
|
+
self.layers.push(RAMLayer::new());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for layer in 0..=top_layer {
|
|
93
|
+
self.layers[layer].add_node(node)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// Updates the entrypoint to point to the first node of the top layer
|
|
98
|
+
pub fn update_entry_point(&mut self) {
|
|
99
|
+
// Only update if the entrypoint is not already at the top layer
|
|
100
|
+
if self.layers.len() > self.entry_point.layer + 1 {
|
|
101
|
+
self.entry_point = EntryPoint {
|
|
102
|
+
node: *self.layers.last().unwrap().out.keys().next().unwrap(),
|
|
103
|
+
layer: self.layers.len() - 1,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub fn no_layers(&self) -> usize {
|
|
109
|
+
self.layers.len()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Remove any links that point to a node which is not in this layer
|
|
113
|
+
/// A bug in a previous version of this program could cause a node in layer N
|
|
114
|
+
/// to link to a node in layer N-1. This breaks navigation accross layer N.
|
|
115
|
+
/// This function will delete any such link from the graph.
|
|
116
|
+
pub fn fix_broken_links(&self) {
|
|
117
|
+
for l in &self.layers[1..] {
|
|
118
|
+
l.fix_broken_links();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub struct EdgesIterator<'a>(RwLockReadGuard<'a, Vec<(VectorAddr, Edge)>>, usize);
|
|
124
|
+
|
|
125
|
+
impl<'a> Iterator for EdgesIterator<'a> {
|
|
126
|
+
type Item = (VectorAddr, Edge);
|
|
127
|
+
|
|
128
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
129
|
+
let it = self.0.get(self.1);
|
|
130
|
+
self.1 += 1;
|
|
131
|
+
it.copied()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
impl<'a> SearchableLayer for &'a RAMLayer {
|
|
136
|
+
type EdgeIt = EdgesIterator<'a>;
|
|
137
|
+
fn get_out_edges(&self, node: VectorAddr) -> Self::EdgeIt {
|
|
138
|
+
EdgesIterator(self.out[&node].read().unwrap(), 0)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
impl<'a> SearchableHnsw for &'a RAMHnsw {
|
|
143
|
+
type L = &'a RAMLayer;
|
|
144
|
+
fn get_entry_point(&self) -> EntryPoint {
|
|
145
|
+
self.entry_point
|
|
146
|
+
}
|
|
147
|
+
fn get_layer(&self, i: usize) -> Self::L {
|
|
148
|
+
&self.layers[i]
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[cfg(test)]
|
|
153
|
+
mod tests {
|
|
154
|
+
use super::*;
|
|
155
|
+
|
|
156
|
+
#[test]
|
|
157
|
+
fn test_fix_broken_links() {
|
|
158
|
+
// Create a minimal broken graph
|
|
159
|
+
let layer0 = RAMLayer {
|
|
160
|
+
out: [
|
|
161
|
+
(VectorAddr(0), RwLock::new(vec![(VectorAddr(1), 0.5)])),
|
|
162
|
+
(VectorAddr(1), RwLock::new(vec![(VectorAddr(0), 0.5)])),
|
|
163
|
+
]
|
|
164
|
+
.into_iter()
|
|
165
|
+
.collect(),
|
|
166
|
+
};
|
|
167
|
+
let layer1 = RAMLayer {
|
|
168
|
+
out: [(VectorAddr(0), RwLock::new(vec![(VectorAddr(1), 0.5)]))]
|
|
169
|
+
.into_iter()
|
|
170
|
+
.collect(),
|
|
171
|
+
};
|
|
172
|
+
let mut graph = RAMHnsw::new();
|
|
173
|
+
graph.layers.push(layer0);
|
|
174
|
+
graph.layers.push(layer1);
|
|
175
|
+
graph.fix_broken_links();
|
|
176
|
+
assert!(graph.layers[1].out[&VectorAddr(0)].read().unwrap().is_empty());
|
|
177
|
+
}
|
|
178
|
+
}
|