@joystick.js/db-canary 0.0.0-canary.2209
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.
- package/.build/getFilesToBuild.js +26 -0
- package/.build/getPlatformSafeFilePath.js +6 -0
- package/.build/getPlatformSafePath.js +6 -0
- package/.build/index.js +88 -0
- package/.build/isWindows.js +3 -0
- package/API_KEY +1 -0
- package/README.md +1821 -0
- package/data/data.mdb +0 -0
- package/data/lock.mdb +0 -0
- package/dist/client/database.js +1 -0
- package/dist/client/index.js +1 -0
- package/dist/server/cluster/index.js +1 -0
- package/dist/server/cluster/master.js +20 -0
- package/dist/server/cluster/worker.js +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/lib/api_key_manager.js +9 -0
- package/dist/server/lib/auth_manager.js +1 -0
- package/dist/server/lib/auto_index_manager.js +1 -0
- package/dist/server/lib/backup_manager.js +1 -0
- package/dist/server/lib/connection_manager.js +1 -0
- package/dist/server/lib/disk_utils.js +2 -0
- package/dist/server/lib/http_server.js +405 -0
- package/dist/server/lib/index_manager.js +1 -0
- package/dist/server/lib/load_settings.js +1 -0
- package/dist/server/lib/logger.js +1 -0
- package/dist/server/lib/op_types.js +1 -0
- package/dist/server/lib/operation_dispatcher.js +1 -0
- package/dist/server/lib/operations/admin.js +1 -0
- package/dist/server/lib/operations/bulk_write.js +1 -0
- package/dist/server/lib/operations/create_index.js +1 -0
- package/dist/server/lib/operations/delete_one.js +1 -0
- package/dist/server/lib/operations/drop_index.js +1 -0
- package/dist/server/lib/operations/find.js +1 -0
- package/dist/server/lib/operations/find_one.js +1 -0
- package/dist/server/lib/operations/get_indexes.js +1 -0
- package/dist/server/lib/operations/insert_one.js +1 -0
- package/dist/server/lib/operations/update_one.js +1 -0
- package/dist/server/lib/performance_monitor.js +1 -0
- package/dist/server/lib/query_engine.js +1 -0
- package/dist/server/lib/recovery_manager.js +1 -0
- package/dist/server/lib/replication_manager.js +1 -0
- package/dist/server/lib/safe_json_parse.js +1 -0
- package/dist/server/lib/send_response.js +1 -0
- package/dist/server/lib/tcp_protocol.js +1 -0
- package/dist/server/lib/write_forwarder.js +1 -0
- package/dist/server/lib/write_queue.js +1 -0
- package/increment_version.js +3 -0
- package/logs/.013e15b54597d05db4b4b53ecc37b10c92a72927-audit.json +20 -0
- package/logs/.02de550a67ea0f5961faa2dfd458a4d06f59ebd1-audit.json +20 -0
- package/logs/.03494ba24eb3c72214b4068a77d54b8993bee651-audit.json +20 -0
- package/logs/.06309ec60b339be1259a7993dd09c732f8907fbc-audit.json +20 -0
- package/logs/.0663a04dcfa17285661e5e1b8cfa51f41523b210-audit.json +20 -0
- package/logs/.0f06e6c4c9b824622729e13927587479e5060391-audit.json +20 -0
- package/logs/.16ccf58682ecb22b3e3ec63f0da1b7fe9be56528-audit.json +20 -0
- package/logs/.1fa1a5d02f496474b1ab473524c65c984146a9ad-audit.json +20 -0
- package/logs/.2223c0ae3bea6f0d62c62b1d319cc8634856abb7-audit.json +20 -0
- package/logs/.23dc79ffda3e083665e6f5993f59397adcbf4a46-audit.json +20 -0
- package/logs/.28104f49b03906b189eefd1cd462cb46c3c0af22-audit.json +20 -0
- package/logs/.29cdbf13808abe6a0ce70ee2f2efdd680ce3fd8e-audit.json +20 -0
- package/logs/.2a9889afd071f77f41f5170d08703a0afca866b7-audit.json +20 -0
- package/logs/.2acec3d1940a2bbed487528b703ee5948959a599-audit.json +20 -0
- package/logs/.2fb60ff326338c02bfedbcd0e936444e4a216750-audit.json +20 -0
- package/logs/.318fc7a19530d76a345f030f7cad00dda15300e7-audit.json +20 -0
- package/logs/.3cf27043e19085f908cedc7701e6d013463208ee-audit.json +25 -0
- package/logs/.3d90d785415817fc443402843b7c95f8371adc9b-audit.json +20 -0
- package/logs/.4074bca620375f72966fc52dfd439577727671e5-audit.json +20 -0
- package/logs/.40eecf018417ea80a70ea8ec7a3cc9406bc6334b-audit.json +20 -0
- package/logs/.50e974f1ef7c365fca6a1251b2e2c2252914cb5e-audit.json +20 -0
- package/logs/.52cb7d9e4223cf26ba36006ac26b949a97c7923c-audit.json +20 -0
- package/logs/.54befcdb84c15aad980705a31bcc9f555c3577ab-audit.json +20 -0
- package/logs/.57dfb70e22eddb84db2e3c0ceeefac5c0b9baffa-audit.json +20 -0
- package/logs/.5f0b24705a1eaad4eca4968f2d86f91b3f9be683-audit.json +20 -0
- package/logs/.61ba98fdda7db58576b382fee07904e5db1169d6-audit.json +20 -0
- package/logs/.6235017727ef6b199d569a99d6aa8c8e80a1b475-audit.json +20 -0
- package/logs/.63db16193699219489d218a1ddea5dde3750cae4-audit.json +20 -0
- package/logs/.64fb67dfe14149c9eef728d79bf30a54da809c60-audit.json +20 -0
- package/logs/.669137453368987c1f311b5345342527afb54e50-audit.json +20 -0
- package/logs/.7a71f8c89ea28ae266d356aeff6306e876a30fbb-audit.json +20 -0
- package/logs/.7afbaa90fe9dc3a7d682676f9bb79f9a1b1fd9a6-audit.json +20 -0
- package/logs/.7ca29e322cd05327035de850099e7610864f2347-audit.json +20 -0
- package/logs/.83335ab3347e449dae03455a110aaf7f120d4802-audit.json +20 -0
- package/logs/.8c2487b5fd445d2c8e5c483c80b9fa99bbf1ca58-audit.json +20 -0
- package/logs/.8c8b9dc386922c9f3b4c13251af7052aac1d24c0-audit.json +20 -0
- package/logs/.8d6155d94640c4863301ae0fee5e4e7372a21446-audit.json +20 -0
- package/logs/.944a3119a243deea7c8270d5d9e582bb1d0eaa10-audit.json +20 -0
- package/logs/.9816a845c30fb2909f3b26a23eeb3538ebcad5db-audit.json +20 -0
- package/logs/.9dc08784e38b865488177c26d4af5934555e0323-audit.json +20 -0
- package/logs/.9dd27d2e0e454ac0a37600206d1cac5493b0d7ee-audit.json +20 -0
- package/logs/.a3d486feeac7654c59b547de96600e8849a06d4f-audit.json +20 -0
- package/logs/.a5b811f4def22250f86cc18870d7c4573625df22-audit.json +20 -0
- package/logs/.a61648eb5f830e0b6f508ac35e4f8f629d2ad4c7-audit.json +20 -0
- package/logs/.a89016d507045771b4b5a65656944a9c0f1e528b-audit.json +20 -0
- package/logs/.a99bee160a1c590be959af46bacc02724803f691-audit.json +20 -0
- package/logs/.ada7906d6243fd7da802f03d86c4ae5dd9df6236-audit.json +20 -0
- package/logs/.b518339ee942143b6af983af167f5bbb6983b4de-audit.json +20 -0
- package/logs/.b51b124b166d53c9519017856ea610d61d65fabe-audit.json +20 -0
- package/logs/.b7a6aee19f58e55633d5e4a3709041c47dfff975-audit.json +20 -0
- package/logs/.bd7a8a6ba9c55d557a4867ab53f02e3ec2e1553d-audit.json +20 -0
- package/logs/.c1435dafe453b169d6392b25065f3cf4ab6fbb21-audit.json +20 -0
- package/logs/.c17e1ce043109f77dc2f0e2aa290a9d1ed842c03-audit.json +20 -0
- package/logs/.ca62637ce9540e5a38a2fbedb2115febb6ad308a-audit.json +15 -0
- package/logs/.ccee67b9c176967f8977071409a41f5cb5cd6ad4-audit.json +20 -0
- package/logs/.db24043417ea79a6f14cd947476399e53930b48d-audit.json +20 -0
- package/logs/.e0f12acccb57829f5f33712bb2e2607ecd808147-audit.json +20 -0
- package/logs/.e9b6cc33d0bbd2e644c4e2bf44d177f850016557-audit.json +20 -0
- package/logs/.f15291d434808e3bdca7963ccd2e73893be027e6-audit.json +20 -0
- package/logs/.f4bdf9e21ef84f8a3fae3ffb32bbc39275991351-audit.json +15 -0
- package/logs/.fbac3aefac1e81b4230df5aa50667cb90d51024f-audit.json +20 -0
- package/logs/.fcfd495c0a9169db243f4a4f21878ee02b76413c-audit.json +20 -0
- package/logs/admin-2025-09-12.log +580 -0
- package/logs/admin-2025-09-15.log +283 -0
- package/logs/admin-error-2025-09-12.log +22 -0
- package/logs/admin-error-2025-09-15.log +10 -0
- package/logs/api_key_manager-2025-09-12.log +658 -0
- package/logs/api_key_manager-2025-09-15.log +295 -0
- package/logs/api_key_manager-error-2025-09-12.log +0 -0
- package/logs/api_key_manager-error-2025-09-15.log +0 -0
- package/logs/auth_manager-2025-09-12.log +4432 -0
- package/logs/auth_manager-2025-09-15.log +2000 -0
- package/logs/auth_manager-error-2025-09-12.log +11 -0
- package/logs/auth_manager-error-2025-09-15.log +5 -0
- package/logs/auto_index_manager-2025-09-12.log +84 -0
- package/logs/auto_index_manager-2025-09-15.log +45 -0
- package/logs/auto_index_manager-error-2025-09-12.log +6 -0
- package/logs/auto_index_manager-error-2025-09-15.log +0 -0
- package/logs/backup_manager-2025-09-12.log +198 -0
- package/logs/backup_manager-2025-09-15.log +90 -0
- package/logs/backup_manager-error-2025-09-12.log +198 -0
- package/logs/backup_manager-error-2025-09-15.log +90 -0
- package/logs/bulk_write-2025-09-12.log +66 -0
- package/logs/bulk_write-2025-09-15.log +38 -0
- package/logs/bulk_write-error-2025-09-12.log +0 -0
- package/logs/bulk_write-error-2025-09-15.log +0 -0
- package/logs/connection_manager-2025-09-12.log +2412 -0
- package/logs/connection_manager-2025-09-15.log +1132 -0
- package/logs/connection_manager-error-2025-09-12.log +0 -0
- package/logs/connection_manager-error-2025-09-15.log +0 -0
- package/logs/create_index-2025-09-12.log +302 -0
- package/logs/create_index-2025-09-15.log +158 -0
- package/logs/create_index-error-2025-09-12.log +30 -0
- package/logs/create_index-error-2025-09-15.log +13 -0
- package/logs/delete_one-2025-09-12.log +73 -0
- package/logs/delete_one-2025-09-15.log +43 -0
- package/logs/delete_one-error-2025-09-12.log +0 -0
- package/logs/delete_one-error-2025-09-15.log +0 -0
- package/logs/disk_utils-2025-09-12.log +4954 -0
- package/logs/disk_utils-2025-09-15.log +2446 -0
- package/logs/disk_utils-error-2025-09-12.log +0 -0
- package/logs/disk_utils-error-2025-09-15.log +0 -0
- package/logs/drop_index-2025-09-12.log +41 -0
- package/logs/drop_index-2025-09-15.log +23 -0
- package/logs/drop_index-error-2025-09-12.log +11 -0
- package/logs/drop_index-error-2025-09-15.log +5 -0
- package/logs/find-2025-09-12.log +1050 -0
- package/logs/find-2025-09-15.log +592 -0
- package/logs/find-error-2025-09-12.log +1 -0
- package/logs/find-error-2025-09-15.log +0 -0
- package/logs/find_one-2025-09-12.log +425 -0
- package/logs/find_one-2025-09-15.log +264 -0
- package/logs/find_one-error-2025-09-12.log +5 -0
- package/logs/find_one-error-2025-09-15.log +0 -0
- package/logs/get_indexes-2025-09-12.log +84 -0
- package/logs/get_indexes-2025-09-15.log +56 -0
- package/logs/get_indexes-error-2025-09-12.log +6 -0
- package/logs/get_indexes-error-2025-09-15.log +0 -0
- package/logs/http_server-2025-09-12.log +2772 -0
- package/logs/http_server-2025-09-15.log +1276 -0
- package/logs/http_server-error-2025-09-12.log +212 -0
- package/logs/http_server-error-2025-09-15.log +44 -0
- package/logs/index_manager-2025-09-12.log +5031 -0
- package/logs/index_manager-2025-09-15.log +2909 -0
- package/logs/index_manager-error-2025-09-12.log +80 -0
- package/logs/index_manager-error-2025-09-15.log +38 -0
- package/logs/insert_one-2025-09-12.log +2181 -0
- package/logs/insert_one-2025-09-15.log +1293 -0
- package/logs/insert_one-error-2025-09-12.log +0 -0
- package/logs/insert_one-error-2025-09-15.log +0 -0
- package/logs/master-2025-09-12.log +1882 -0
- package/logs/master-2025-09-15.log +910 -0
- package/logs/master-error-2025-09-12.log +80 -0
- package/logs/master-error-2025-09-15.log +0 -0
- package/logs/operation_dispatcher-2025-09-12.log +751 -0
- package/logs/operation_dispatcher-2025-09-15.log +359 -0
- package/logs/operation_dispatcher-error-2025-09-12.log +33 -0
- package/logs/operation_dispatcher-error-2025-09-15.log +11 -0
- package/logs/performance_monitor-2025-09-12.log +14889 -0
- package/logs/performance_monitor-2025-09-15.log +6803 -0
- package/logs/performance_monitor-error-2025-09-12.log +0 -0
- package/logs/performance_monitor-error-2025-09-15.log +0 -0
- package/logs/query_engine-2025-09-12.log +5310 -0
- package/logs/query_engine-2025-09-15.log +2639 -0
- package/logs/query_engine-error-2025-09-12.log +0 -0
- package/logs/query_engine-error-2025-09-15.log +0 -0
- package/logs/recovery_manager-2025-09-12.log +462 -0
- package/logs/recovery_manager-2025-09-15.log +210 -0
- package/logs/recovery_manager-error-2025-09-12.log +22 -0
- package/logs/recovery_manager-error-2025-09-15.log +10 -0
- package/logs/replication-2025-09-12.log +1923 -0
- package/logs/replication-2025-09-15.log +917 -0
- package/logs/replication-error-2025-09-12.log +33 -0
- package/logs/replication-error-2025-09-15.log +15 -0
- package/logs/server-2025-09-12.log +2601 -0
- package/logs/server-2025-09-15.log +1191 -0
- package/logs/server-error-2025-09-12.log +0 -0
- package/logs/server-error-2025-09-15.log +0 -0
- package/logs/tcp_protocol-2025-09-12.log +22 -0
- package/logs/tcp_protocol-2025-09-15.log +10 -0
- package/logs/tcp_protocol-error-2025-09-12.log +22 -0
- package/logs/tcp_protocol-error-2025-09-15.log +10 -0
- package/logs/test-2025-09-12.log +0 -0
- package/logs/test-2025-09-15.log +0 -0
- package/logs/test-error-2025-09-12.log +0 -0
- package/logs/test-error-2025-09-15.log +0 -0
- package/logs/update_one-2025-09-12.log +173 -0
- package/logs/update_one-2025-09-15.log +118 -0
- package/logs/update_one-error-2025-09-12.log +0 -0
- package/logs/update_one-error-2025-09-15.log +0 -0
- package/logs/worker-2025-09-12.log +1457 -0
- package/logs/worker-2025-09-15.log +695 -0
- package/logs/worker-error-2025-09-12.log +0 -0
- package/logs/worker-error-2025-09-15.log +0 -0
- package/logs/write_forwarder-2025-09-12.log +1956 -0
- package/logs/write_forwarder-2025-09-15.log +932 -0
- package/logs/write_forwarder-error-2025-09-12.log +66 -0
- package/logs/write_forwarder-error-2025-09-15.log +30 -0
- package/logs/write_queue-2025-09-12.log +612 -0
- package/logs/write_queue-2025-09-15.log +301 -0
- package/logs/write_queue-error-2025-09-12.log +184 -0
- package/logs/write_queue-error-2025-09-15.log +83 -0
- package/package.json +48 -0
- package/prompts/01-core-infrastructure.md +56 -0
- package/prompts/02-secondary-indexing.md +65 -0
- package/prompts/03-write-serialization.md +63 -0
- package/prompts/04-enhanced-authentication.md +75 -0
- package/prompts/05-comprehensive-admin-operations.md +75 -0
- package/prompts/06-backup-and-restore-system.md +106 -0
- package/prompts/07-production-safety-features.md +107 -0
- package/prompts/08-tcp-client-library.md +121 -0
- package/prompts/09-api-method-chaining.md +134 -0
- package/prompts/10-automatic-index-creation.md +223 -0
- package/prompts/11-operation-naming-consistency.md +268 -0
- package/prompts/12-tcp-replication-system.md +333 -0
- package/prompts/13-master-read-write-operations.md +57 -0
- package/prompts/14-index-upsert-operations.md +68 -0
- package/prompts/15-client-api-return-types.md +81 -0
- package/prompts/16-server-setup-ui.md +97 -0
- package/prompts/17-emergency-password-change.md +108 -0
- package/prompts/18-joystick-framework-integration.md +116 -0
- package/prompts/19-api-key-authentication-system.md +137 -0
- package/prompts/20-configurable-server-port.md +105 -0
- package/prompts/21-multi-database-support.md +161 -0
- package/prompts/FULL_TEXT_SEARCH.md +293 -0
- package/prompts/PROMPTS.md +158 -0
- package/prompts/README.md +221 -0
- package/prompts/TYPESCRIPT_GENERATION.md +179 -0
- package/src/client/database.js +166 -0
- package/src/client/index.js +752 -0
- package/src/server/cluster/index.js +53 -0
- package/src/server/cluster/master.js +774 -0
- package/src/server/cluster/worker.js +537 -0
- package/src/server/index.js +540 -0
- package/src/server/lib/api_key_manager.js +473 -0
- package/src/server/lib/auth_manager.js +375 -0
- package/src/server/lib/auto_index_manager.js +681 -0
- package/src/server/lib/backup_manager.js +650 -0
- package/src/server/lib/connection_manager.js +218 -0
- package/src/server/lib/disk_utils.js +118 -0
- package/src/server/lib/http_server.js +1165 -0
- package/src/server/lib/index_manager.js +756 -0
- package/src/server/lib/load_settings.js +143 -0
- package/src/server/lib/logger.js +135 -0
- package/src/server/lib/op_types.js +29 -0
- package/src/server/lib/operation_dispatcher.js +268 -0
- package/src/server/lib/operations/admin.js +808 -0
- package/src/server/lib/operations/bulk_write.js +367 -0
- package/src/server/lib/operations/create_index.js +68 -0
- package/src/server/lib/operations/delete_one.js +114 -0
- package/src/server/lib/operations/drop_index.js +58 -0
- package/src/server/lib/operations/find.js +340 -0
- package/src/server/lib/operations/find_one.js +319 -0
- package/src/server/lib/operations/get_indexes.js +52 -0
- package/src/server/lib/operations/insert_one.js +113 -0
- package/src/server/lib/operations/update_one.js +225 -0
- package/src/server/lib/performance_monitor.js +313 -0
- package/src/server/lib/query_engine.js +243 -0
- package/src/server/lib/recovery_manager.js +388 -0
- package/src/server/lib/replication_manager.js +727 -0
- package/src/server/lib/safe_json_parse.js +21 -0
- package/src/server/lib/send_response.js +47 -0
- package/src/server/lib/tcp_protocol.js +130 -0
- package/src/server/lib/write_forwarder.js +636 -0
- package/src/server/lib/write_queue.js +335 -0
- package/test_data/data.mdb +0 -0
- package/test_data/lock.mdb +0 -0
- package/tests/client/index.test.js +1232 -0
- package/tests/server/cluster/cluster.test.js +248 -0
- package/tests/server/cluster/master_read_write_operations.test.js +577 -0
- package/tests/server/index.test.js +651 -0
- package/tests/server/integration/authentication_integration.test.js +294 -0
- package/tests/server/integration/auto_indexing_integration.test.js +268 -0
- package/tests/server/integration/backup_integration.test.js +513 -0
- package/tests/server/integration/indexing_integration.test.js +126 -0
- package/tests/server/integration/production_safety_integration.test.js +358 -0
- package/tests/server/integration/replication_integration.test.js +227 -0
- package/tests/server/integration/write_serialization_integration.test.js +246 -0
- package/tests/server/lib/api_key_manager.test.js +516 -0
- package/tests/server/lib/auth_manager.test.js +317 -0
- package/tests/server/lib/auto_index_manager.test.js +275 -0
- package/tests/server/lib/backup_manager.test.js +238 -0
- package/tests/server/lib/connection_manager.test.js +221 -0
- package/tests/server/lib/disk_utils.test.js +63 -0
- package/tests/server/lib/http_server.test.js +389 -0
- package/tests/server/lib/index_manager.test.js +301 -0
- package/tests/server/lib/load_settings.test.js +107 -0
- package/tests/server/lib/load_settings_port_config.test.js +243 -0
- package/tests/server/lib/logger.test.js +282 -0
- package/tests/server/lib/operations/admin.test.js +638 -0
- package/tests/server/lib/operations/bulk_write.test.js +128 -0
- package/tests/server/lib/operations/create_index.test.js +138 -0
- package/tests/server/lib/operations/delete_one.test.js +52 -0
- package/tests/server/lib/operations/drop_index.test.js +72 -0
- package/tests/server/lib/operations/find.test.js +93 -0
- package/tests/server/lib/operations/find_one.test.js +91 -0
- package/tests/server/lib/operations/get_indexes.test.js +87 -0
- package/tests/server/lib/operations/insert_one.test.js +42 -0
- package/tests/server/lib/operations/update_one.test.js +89 -0
- package/tests/server/lib/performance_monitor.test.js +185 -0
- package/tests/server/lib/query_engine.test.js +46 -0
- package/tests/server/lib/recovery_manager.test.js +414 -0
- package/tests/server/lib/replication_manager.test.js +202 -0
- package/tests/server/lib/safe_json_parse.test.js +45 -0
- package/tests/server/lib/send_response.test.js +155 -0
- package/tests/server/lib/tcp_protocol.test.js +169 -0
- package/tests/server/lib/write_forwarder.test.js +258 -0
- package/tests/server/lib/write_queue.test.js +255 -0
- package/tsconfig.json +30 -0
- package/types/client/index.d.ts +447 -0
- package/types/server/cluster/index.d.ts +28 -0
- package/types/server/cluster/master.d.ts +115 -0
- package/types/server/cluster/worker.d.ts +1 -0
- package/types/server/lib/auth_manager.d.ts +13 -0
- package/types/server/lib/backup_manager.d.ts +43 -0
- package/types/server/lib/connection_manager.d.ts +15 -0
- package/types/server/lib/disk_utils.d.ts +3 -0
- package/types/server/lib/index_manager.d.ts +24 -0
- package/types/server/lib/load_settings.d.ts +4 -0
- package/types/server/lib/logger.d.ts +44 -0
- package/types/server/lib/op_types.d.ts +6 -0
- package/types/server/lib/performance_monitor.d.ts +68 -0
- package/types/server/lib/query_engine.d.ts +10 -0
- package/types/server/lib/safe_json_parse.d.ts +7 -0
- package/types/server/lib/send_response.d.ts +3 -0
- package/types/server/lib/tcp_protocol.d.ts +12 -0
- package/types/server/lib/write_queue.d.ts +2 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Find operation implementation for JoystickDB with MongoDB-like query capabilities.
|
|
3
|
+
* Supports complex filtering, projection, sorting, pagination, and automatic index optimization.
|
|
4
|
+
* Includes comprehensive query operators, field path resolution, and performance monitoring.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { get_database, build_collection_key } from '../query_engine.js';
|
|
8
|
+
import { can_use_index, find_documents_by_index } from '../index_manager.js';
|
|
9
|
+
import { record_query, record_index_usage } from '../auto_index_manager.js';
|
|
10
|
+
import create_logger from '../logger.js';
|
|
11
|
+
|
|
12
|
+
const { create_context_logger } = create_logger('find');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extracts field value from document using dot notation path.
|
|
16
|
+
* @param {Object} document - Document to extract value from
|
|
17
|
+
* @param {string} field_path - Dot-separated field path (e.g., 'user.profile.name')
|
|
18
|
+
* @returns {*} Field value or undefined if path doesn't exist
|
|
19
|
+
*/
|
|
20
|
+
const get_field_value = (document, field_path) => {
|
|
21
|
+
const parts = field_path.split('.');
|
|
22
|
+
let value = document;
|
|
23
|
+
|
|
24
|
+
for (const part of parts) {
|
|
25
|
+
if (value === null || value === undefined) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
value = value[part];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return value;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Checks if a field exists in a document using dot notation path.
|
|
36
|
+
* @param {Object} document - Document to check
|
|
37
|
+
* @param {string} field_path - Dot-separated field path
|
|
38
|
+
* @returns {boolean} True if field exists, false otherwise
|
|
39
|
+
*/
|
|
40
|
+
const field_exists = (document, field_path) => {
|
|
41
|
+
const parts = field_path.split('.');
|
|
42
|
+
let current = document;
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < parts.length; i++) {
|
|
45
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (i === parts.length - 1) {
|
|
50
|
+
return current.hasOwnProperty(parts[i]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
current = current[parts[i]];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Checks if a document matches the provided filter criteria.
|
|
61
|
+
* Supports MongoDB-like query operators including $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $regex.
|
|
62
|
+
* @param {Object} document - Document to test against filter
|
|
63
|
+
* @param {Object} filter - Filter criteria with field names and values/operators
|
|
64
|
+
* @returns {boolean} True if document matches filter, false otherwise
|
|
65
|
+
* @throws {Error} When unsupported query operator is used
|
|
66
|
+
*/
|
|
67
|
+
const matches_filter = (document, filter) => {
|
|
68
|
+
if (!filter || Object.keys(filter).length === 0) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
73
|
+
const field_value = get_field_value(document, field);
|
|
74
|
+
|
|
75
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
76
|
+
for (const [operator, operand] of Object.entries(value)) {
|
|
77
|
+
switch (operator) {
|
|
78
|
+
case '$eq':
|
|
79
|
+
if (field_value !== operand) return false;
|
|
80
|
+
break;
|
|
81
|
+
case '$ne':
|
|
82
|
+
if (field_value === operand) return false;
|
|
83
|
+
break;
|
|
84
|
+
case '$gt':
|
|
85
|
+
if (field_value <= operand) return false;
|
|
86
|
+
break;
|
|
87
|
+
case '$gte':
|
|
88
|
+
if (field_value < operand) return false;
|
|
89
|
+
break;
|
|
90
|
+
case '$lt':
|
|
91
|
+
if (field_value >= operand) return false;
|
|
92
|
+
break;
|
|
93
|
+
case '$lte':
|
|
94
|
+
if (field_value > operand) return false;
|
|
95
|
+
break;
|
|
96
|
+
case '$in':
|
|
97
|
+
if (!Array.isArray(operand) || !operand.includes(field_value)) return false;
|
|
98
|
+
break;
|
|
99
|
+
case '$nin':
|
|
100
|
+
if (!Array.isArray(operand) || operand.includes(field_value)) return false;
|
|
101
|
+
break;
|
|
102
|
+
case '$exists':
|
|
103
|
+
const exists = field_exists(document, field);
|
|
104
|
+
if (operand && !exists) return false;
|
|
105
|
+
if (!operand && exists) return false;
|
|
106
|
+
break;
|
|
107
|
+
case '$regex':
|
|
108
|
+
const regex = new RegExp(operand);
|
|
109
|
+
if (!regex.test(field_value)) return false;
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
throw new Error(`Unsupported query operator: ${operator}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
if (field_value !== value) return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Applies projection to a document, including or excluding specified fields.
|
|
125
|
+
* Supports both inclusion and exclusion projections with MongoDB-like syntax.
|
|
126
|
+
* @param {Object} document - Document to project
|
|
127
|
+
* @param {Object} projection - Projection specification with field names and 1/0 or true/false values
|
|
128
|
+
* @returns {Object} Projected document with only specified fields
|
|
129
|
+
*/
|
|
130
|
+
const apply_projection = (document, projection) => {
|
|
131
|
+
if (!projection || Object.keys(projection).length === 0) {
|
|
132
|
+
return document;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const is_inclusion = Object.values(projection).some(value => value === 1 || value === true);
|
|
136
|
+
const projected_document = {};
|
|
137
|
+
|
|
138
|
+
if (is_inclusion) {
|
|
139
|
+
projected_document._id = document._id;
|
|
140
|
+
|
|
141
|
+
for (const [field, include] of Object.entries(projection)) {
|
|
142
|
+
if (field === '_id' && (include === 0 || include === false)) {
|
|
143
|
+
delete projected_document._id;
|
|
144
|
+
} else if (include === 1 || include === true) {
|
|
145
|
+
projected_document[field] = document[field];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
Object.assign(projected_document, document);
|
|
150
|
+
|
|
151
|
+
for (const [field, exclude] of Object.entries(projection)) {
|
|
152
|
+
if (exclude === 0 || exclude === false) {
|
|
153
|
+
delete projected_document[field];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return projected_document;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sorts an array of documents based on sort specification.
|
|
163
|
+
* Supports multi-field sorting with ascending (1) and descending (-1) directions.
|
|
164
|
+
* @param {Array<Object>} documents - Array of documents to sort
|
|
165
|
+
* @param {Object} sort - Sort specification with field names and 1/-1 direction values
|
|
166
|
+
* @returns {Array<Object>} Sorted array of documents
|
|
167
|
+
*/
|
|
168
|
+
const apply_sort = (documents, sort) => {
|
|
169
|
+
if (!sort || Object.keys(sort).length === 0) {
|
|
170
|
+
return documents;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return documents.sort((a, b) => {
|
|
174
|
+
for (const [field, direction] of Object.entries(sort)) {
|
|
175
|
+
const a_value = a[field];
|
|
176
|
+
const b_value = b[field];
|
|
177
|
+
|
|
178
|
+
if (a_value === b_value) continue;
|
|
179
|
+
|
|
180
|
+
if (a_value === undefined) return 1;
|
|
181
|
+
if (b_value === undefined) return -1;
|
|
182
|
+
|
|
183
|
+
const comparison = a_value < b_value ? -1 : a_value > b_value ? 1 : 0;
|
|
184
|
+
|
|
185
|
+
if (direction === -1) {
|
|
186
|
+
return -comparison;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return comparison;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return 0;
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Finds documents in a collection matching the specified filter criteria.
|
|
198
|
+
* Supports MongoDB-like querying with automatic index optimization, projection, sorting, and pagination.
|
|
199
|
+
* @param {string} database_name - Name of the database
|
|
200
|
+
* @param {string} collection_name - Name of the collection to search
|
|
201
|
+
* @param {Object} [filter={}] - Filter criteria for matching documents
|
|
202
|
+
* @param {Object} [options={}] - Query options
|
|
203
|
+
* @param {Object} [options.projection] - Fields to include/exclude in results
|
|
204
|
+
* @param {Object} [options.sort] - Sort specification for ordering results
|
|
205
|
+
* @param {number} [options.limit] - Maximum number of documents to return
|
|
206
|
+
* @param {number} [options.skip=0] - Number of documents to skip
|
|
207
|
+
* @returns {Promise<Array<Object>>} Promise resolving to array of matching documents
|
|
208
|
+
* @throws {Error} When database or collection name is missing or query execution fails
|
|
209
|
+
*/
|
|
210
|
+
const find = async (database_name, collection_name, filter = {}, options = {}) => {
|
|
211
|
+
const log = create_context_logger();
|
|
212
|
+
|
|
213
|
+
if (!database_name) {
|
|
214
|
+
throw new Error('Database name is required');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!collection_name) {
|
|
218
|
+
throw new Error('Collection name is required');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const db = get_database();
|
|
222
|
+
const { projection, sort, limit, skip = 0 } = options;
|
|
223
|
+
const start_time = Date.now();
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
let matching_documents = [];
|
|
227
|
+
let used_index = false;
|
|
228
|
+
let indexed_field = null;
|
|
229
|
+
|
|
230
|
+
const index_info = can_use_index(database_name, collection_name, filter);
|
|
231
|
+
|
|
232
|
+
if (index_info) {
|
|
233
|
+
const { field, operators } = index_info;
|
|
234
|
+
const field_filter = filter[field];
|
|
235
|
+
indexed_field = field;
|
|
236
|
+
|
|
237
|
+
if (typeof field_filter === 'object' && field_filter !== null && !Array.isArray(field_filter)) {
|
|
238
|
+
for (const operator of operators) {
|
|
239
|
+
if (field_filter[operator] !== undefined) {
|
|
240
|
+
const document_ids = find_documents_by_index(database_name, collection_name, field, operator, field_filter[operator]);
|
|
241
|
+
|
|
242
|
+
if (document_ids) {
|
|
243
|
+
used_index = true;
|
|
244
|
+
record_index_usage(database_name, collection_name, field);
|
|
245
|
+
|
|
246
|
+
for (const document_id of document_ids) {
|
|
247
|
+
const collection_key = build_collection_key(database_name, collection_name, document_id);
|
|
248
|
+
const document_data = db.get(collection_key);
|
|
249
|
+
|
|
250
|
+
if (document_data) {
|
|
251
|
+
const document = JSON.parse(document_data);
|
|
252
|
+
if (matches_filter(document, filter)) {
|
|
253
|
+
matching_documents.push(document);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
} else if (operators.includes('eq')) {
|
|
262
|
+
const document_ids = find_documents_by_index(database_name, collection_name, field, 'eq', field_filter);
|
|
263
|
+
|
|
264
|
+
if (document_ids) {
|
|
265
|
+
used_index = true;
|
|
266
|
+
record_index_usage(database_name, collection_name, field);
|
|
267
|
+
|
|
268
|
+
for (const document_id of document_ids) {
|
|
269
|
+
const collection_key = build_collection_key(database_name, collection_name, document_id);
|
|
270
|
+
const document_data = db.get(collection_key);
|
|
271
|
+
|
|
272
|
+
if (document_data) {
|
|
273
|
+
const document = JSON.parse(document_data);
|
|
274
|
+
if (matches_filter(document, filter)) {
|
|
275
|
+
matching_documents.push(document);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!used_index) {
|
|
284
|
+
const collection_prefix = `${database_name}:${collection_name}:`;
|
|
285
|
+
const range = db.getRange({ start: collection_prefix, end: collection_prefix + '\xFF' });
|
|
286
|
+
|
|
287
|
+
for (const { key, value: document_data } of range) {
|
|
288
|
+
const document = JSON.parse(document_data);
|
|
289
|
+
if (matches_filter(document, filter)) {
|
|
290
|
+
matching_documents.push(document);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let sorted_documents = apply_sort(matching_documents, sort);
|
|
296
|
+
|
|
297
|
+
if (skip > 0) {
|
|
298
|
+
sorted_documents = sorted_documents.slice(skip);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (limit && limit > 0) {
|
|
302
|
+
sorted_documents = sorted_documents.slice(0, limit);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const projected_documents = sorted_documents.map(document =>
|
|
306
|
+
apply_projection(document, projection)
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const execution_time = Date.now() - start_time;
|
|
310
|
+
|
|
311
|
+
try {
|
|
312
|
+
record_query(collection_name, filter, execution_time, used_index, indexed_field);
|
|
313
|
+
} catch (auto_index_error) {
|
|
314
|
+
log.warn('Failed to record query for auto-indexing', {
|
|
315
|
+
error: auto_index_error.message
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
log.info('Find operation completed', {
|
|
320
|
+
database: database_name,
|
|
321
|
+
collection: collection_name,
|
|
322
|
+
documents_found: projected_documents.length,
|
|
323
|
+
total_matching: matching_documents.length,
|
|
324
|
+
used_index,
|
|
325
|
+
indexed_field,
|
|
326
|
+
execution_time_ms: execution_time
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return projected_documents;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
log.error('Failed to find documents', {
|
|
332
|
+
database: database_name,
|
|
333
|
+
collection: collection_name,
|
|
334
|
+
error: error.message
|
|
335
|
+
});
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
export default find;
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Find one document operation with indexing, filtering, and projection support.
|
|
3
|
+
*
|
|
4
|
+
* Provides document retrieval with MongoDB-style query operators, index optimization,
|
|
5
|
+
* field projection, nested field access, and comprehensive query performance tracking.
|
|
6
|
+
* Supports both indexed and full collection scans with automatic query recording.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { get_database, build_collection_key } from '../query_engine.js';
|
|
10
|
+
import { can_use_index, find_documents_by_index } from '../index_manager.js';
|
|
11
|
+
import { record_query, record_index_usage } from '../auto_index_manager.js';
|
|
12
|
+
import create_logger from '../logger.js';
|
|
13
|
+
|
|
14
|
+
const { create_context_logger } = create_logger('find_one');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Gets the value of a nested field from a document using dot notation.
|
|
18
|
+
* @param {Object} document - Document to extract field value from
|
|
19
|
+
* @param {string} field_path - Dot-separated field path (e.g., 'user.name')
|
|
20
|
+
* @returns {any} Field value or undefined if not found
|
|
21
|
+
*/
|
|
22
|
+
const get_field_value = (document, field_path) => {
|
|
23
|
+
const parts = field_path.split('.');
|
|
24
|
+
let value = document;
|
|
25
|
+
|
|
26
|
+
for (const part of parts) {
|
|
27
|
+
if (value === null || value === undefined) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
value = value[part];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Checks if a nested field exists in a document using dot notation.
|
|
38
|
+
* @param {Object} document - Document to check
|
|
39
|
+
* @param {string} field_path - Dot-separated field path (e.g., 'user.name')
|
|
40
|
+
* @returns {boolean} True if field exists
|
|
41
|
+
*/
|
|
42
|
+
const field_exists = (document, field_path) => {
|
|
43
|
+
const parts = field_path.split('.');
|
|
44
|
+
let current = document;
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < parts.length; i++) {
|
|
47
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (i === parts.length - 1) {
|
|
52
|
+
return current.hasOwnProperty(parts[i]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
current = current[parts[i]];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Checks if a document matches the given filter criteria.
|
|
63
|
+
* @param {Object} document - Document to test
|
|
64
|
+
* @param {Object} filter - Filter criteria with MongoDB-style operators
|
|
65
|
+
* @returns {boolean} True if document matches filter
|
|
66
|
+
* @throws {Error} When unsupported query operator is used
|
|
67
|
+
*/
|
|
68
|
+
const matches_filter = (document, filter) => {
|
|
69
|
+
if (!filter || Object.keys(filter).length === 0) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
74
|
+
const field_value = get_field_value(document, field);
|
|
75
|
+
|
|
76
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
77
|
+
for (const [operator, operand] of Object.entries(value)) {
|
|
78
|
+
switch (operator) {
|
|
79
|
+
case '$eq':
|
|
80
|
+
if (field_value !== operand) return false;
|
|
81
|
+
break;
|
|
82
|
+
case '$ne':
|
|
83
|
+
if (field_value === operand) return false;
|
|
84
|
+
break;
|
|
85
|
+
case '$gt':
|
|
86
|
+
if (field_value <= operand) return false;
|
|
87
|
+
break;
|
|
88
|
+
case '$gte':
|
|
89
|
+
if (field_value < operand) return false;
|
|
90
|
+
break;
|
|
91
|
+
case '$lt':
|
|
92
|
+
if (field_value >= operand) return false;
|
|
93
|
+
break;
|
|
94
|
+
case '$lte':
|
|
95
|
+
if (field_value > operand) return false;
|
|
96
|
+
break;
|
|
97
|
+
case '$in':
|
|
98
|
+
if (!Array.isArray(operand) || !operand.includes(field_value)) return false;
|
|
99
|
+
break;
|
|
100
|
+
case '$nin':
|
|
101
|
+
if (!Array.isArray(operand) || operand.includes(field_value)) return false;
|
|
102
|
+
break;
|
|
103
|
+
case '$exists':
|
|
104
|
+
const exists = field_exists(document, field);
|
|
105
|
+
if (operand && !exists) return false;
|
|
106
|
+
if (!operand && exists) return false;
|
|
107
|
+
break;
|
|
108
|
+
case '$regex':
|
|
109
|
+
const regex = new RegExp(operand);
|
|
110
|
+
if (!regex.test(field_value)) return false;
|
|
111
|
+
break;
|
|
112
|
+
default:
|
|
113
|
+
throw new Error(`Unsupported query operator: ${operator}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
if (field_value !== value) return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return true;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Applies projection to a document, including or excluding specified fields.
|
|
126
|
+
* @param {Object} document - Document to project
|
|
127
|
+
* @param {Object} projection - Projection specification (1/true for inclusion, 0/false for exclusion)
|
|
128
|
+
* @returns {Object} Projected document
|
|
129
|
+
*/
|
|
130
|
+
const apply_projection = (document, projection) => {
|
|
131
|
+
if (!projection || Object.keys(projection).length === 0) {
|
|
132
|
+
return document;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const is_inclusion = Object.values(projection).some(value => value === 1 || value === true);
|
|
136
|
+
const projected_document = {};
|
|
137
|
+
|
|
138
|
+
if (is_inclusion) {
|
|
139
|
+
projected_document._id = document._id;
|
|
140
|
+
|
|
141
|
+
for (const [field, include] of Object.entries(projection)) {
|
|
142
|
+
if (field === '_id' && (include === 0 || include === false)) {
|
|
143
|
+
delete projected_document._id;
|
|
144
|
+
} else if (include === 1 || include === true) {
|
|
145
|
+
projected_document[field] = document[field];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
Object.assign(projected_document, document);
|
|
150
|
+
|
|
151
|
+
for (const [field, exclude] of Object.entries(projection)) {
|
|
152
|
+
if (exclude === 0 || exclude === false) {
|
|
153
|
+
delete projected_document[field];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return projected_document;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Finds a single document in a collection matching the filter criteria.
|
|
163
|
+
* @param {string} database_name - Name of the database
|
|
164
|
+
* @param {string} collection_name - Name of the collection
|
|
165
|
+
* @param {Object} [filter={}] - Filter criteria
|
|
166
|
+
* @param {Object} [options={}] - Query options
|
|
167
|
+
* @param {Object} [options.projection] - Field projection specification
|
|
168
|
+
* @param {Object} [options.sort] - Sort specification (currently unused)
|
|
169
|
+
* @returns {Promise<Object|null>} Found document or null if not found
|
|
170
|
+
* @throws {Error} When database or collection name is missing or query fails
|
|
171
|
+
*/
|
|
172
|
+
const find_one = async (database_name, collection_name, filter = {}, options = {}) => {
|
|
173
|
+
const log = create_context_logger();
|
|
174
|
+
|
|
175
|
+
if (!database_name) {
|
|
176
|
+
throw new Error('Database name is required');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!collection_name) {
|
|
180
|
+
throw new Error('Collection name is required');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const db = get_database();
|
|
184
|
+
const { projection, sort } = options;
|
|
185
|
+
const start_time = Date.now();
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
let found_document = null;
|
|
189
|
+
let used_index = false;
|
|
190
|
+
let indexed_field = null;
|
|
191
|
+
|
|
192
|
+
const index_info = can_use_index(database_name, collection_name, filter);
|
|
193
|
+
|
|
194
|
+
if (index_info) {
|
|
195
|
+
const { field, operators } = index_info;
|
|
196
|
+
const field_filter = filter[field];
|
|
197
|
+
indexed_field = field;
|
|
198
|
+
|
|
199
|
+
if (typeof field_filter === 'object' && field_filter !== null && !Array.isArray(field_filter)) {
|
|
200
|
+
for (const operator of operators) {
|
|
201
|
+
if (field_filter[operator] !== undefined) {
|
|
202
|
+
const document_ids = find_documents_by_index(database_name, collection_name, field, operator, field_filter[operator]);
|
|
203
|
+
|
|
204
|
+
if (document_ids && document_ids.length > 0) {
|
|
205
|
+
used_index = true;
|
|
206
|
+
record_index_usage(database_name, collection_name, field);
|
|
207
|
+
|
|
208
|
+
for (const document_id of document_ids) {
|
|
209
|
+
const collection_key = build_collection_key(database_name, collection_name, document_id);
|
|
210
|
+
const value = db.get(collection_key);
|
|
211
|
+
|
|
212
|
+
if (value) {
|
|
213
|
+
try {
|
|
214
|
+
const document = JSON.parse(value);
|
|
215
|
+
if (matches_filter(document, filter)) {
|
|
216
|
+
found_document = document;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
} catch (parse_error) {
|
|
220
|
+
// Skip documents that can't be parsed
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else if (operators.includes('eq')) {
|
|
230
|
+
const document_ids = find_documents_by_index(database_name, collection_name, field, 'eq', field_filter);
|
|
231
|
+
|
|
232
|
+
if (document_ids && document_ids.length > 0) {
|
|
233
|
+
used_index = true;
|
|
234
|
+
record_index_usage(database_name, collection_name, field);
|
|
235
|
+
|
|
236
|
+
for (const document_id of document_ids) {
|
|
237
|
+
const collection_key = build_collection_key(database_name, collection_name, document_id);
|
|
238
|
+
const value = db.get(collection_key);
|
|
239
|
+
|
|
240
|
+
if (value) {
|
|
241
|
+
try {
|
|
242
|
+
const document = JSON.parse(value);
|
|
243
|
+
if (matches_filter(document, filter)) {
|
|
244
|
+
found_document = document;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
} catch (parse_error) {
|
|
248
|
+
// Skip documents that can't be parsed
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!used_index) {
|
|
258
|
+
const collection_prefix = `${database_name}:${collection_name}:`;
|
|
259
|
+
const range = db.getRange({ start: collection_prefix, end: collection_prefix + '\xFF' });
|
|
260
|
+
|
|
261
|
+
for (const { key, value } of range) {
|
|
262
|
+
try {
|
|
263
|
+
const document = JSON.parse(value);
|
|
264
|
+
if (matches_filter(document, filter)) {
|
|
265
|
+
found_document = document;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
} catch (parse_error) {
|
|
269
|
+
// Skip documents that can't be parsed
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const execution_time = Date.now() - start_time;
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
record_query(collection_name, filter, execution_time, used_index, indexed_field);
|
|
279
|
+
} catch (auto_index_error) {
|
|
280
|
+
log.warn('Failed to record query for auto-indexing', {
|
|
281
|
+
error: auto_index_error.message
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (found_document) {
|
|
286
|
+
const projected_document = apply_projection(found_document, projection);
|
|
287
|
+
|
|
288
|
+
log.info('Document found', {
|
|
289
|
+
database: database_name,
|
|
290
|
+
collection: collection_name,
|
|
291
|
+
document_id: found_document._id,
|
|
292
|
+
used_index,
|
|
293
|
+
indexed_field,
|
|
294
|
+
execution_time_ms: execution_time
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
return projected_document;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
log.info('No document found', {
|
|
301
|
+
database: database_name,
|
|
302
|
+
collection: collection_name,
|
|
303
|
+
used_index,
|
|
304
|
+
indexed_field,
|
|
305
|
+
execution_time_ms: execution_time
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return null;
|
|
309
|
+
} catch (error) {
|
|
310
|
+
log.error('Failed to find document', {
|
|
311
|
+
database: database_name,
|
|
312
|
+
collection: collection_name,
|
|
313
|
+
error: error.message
|
|
314
|
+
});
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
export default find_one;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Get indexes operation for JoystickDB collections.
|
|
3
|
+
* Provides functionality to retrieve all indexes for a specified collection.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { get_indexes } from '../index_manager.js';
|
|
7
|
+
import create_logger from '../logger.js';
|
|
8
|
+
|
|
9
|
+
const { create_context_logger } = create_logger('get_indexes');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Retrieves all indexes for a specified collection.
|
|
13
|
+
* @param {string} database_name - Name of the database
|
|
14
|
+
* @param {string} collection_name - Name of the collection to get indexes for
|
|
15
|
+
* @returns {Promise<Object>} Result containing acknowledged status and array of indexes
|
|
16
|
+
* @throws {Error} When database or collection name is missing or index retrieval fails
|
|
17
|
+
*/
|
|
18
|
+
const get_indexes_operation = async (database_name, collection_name) => {
|
|
19
|
+
const log = create_context_logger();
|
|
20
|
+
|
|
21
|
+
if (!database_name) {
|
|
22
|
+
throw new Error('Database name is required');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!collection_name) {
|
|
26
|
+
throw new Error('Collection name is required');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const indexes = get_indexes(database_name, collection_name);
|
|
31
|
+
|
|
32
|
+
log.info('Retrieved indexes successfully', {
|
|
33
|
+
database: database_name,
|
|
34
|
+
collection: collection_name,
|
|
35
|
+
count: indexes.length
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
acknowledged: true,
|
|
40
|
+
indexes
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
log.error('Failed to get indexes', {
|
|
44
|
+
database: database_name,
|
|
45
|
+
collection: collection_name,
|
|
46
|
+
error: error.message
|
|
47
|
+
});
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default get_indexes_operation;
|