@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,681 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Automatic index management system for JoystickDB with intelligent query analysis.
|
|
3
|
+
* Monitors query patterns, analyzes performance metrics, and automatically creates/removes indexes
|
|
4
|
+
* based on usage patterns, frequency thresholds, and performance improvements. Provides comprehensive
|
|
5
|
+
* statistics tracking, configurable thresholds, and cleanup of unused indexes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { get_database } from './query_engine.js';
|
|
9
|
+
import { create_index, get_indexes, drop_index } from './index_manager.js';
|
|
10
|
+
import { get_settings } from './load_settings.js';
|
|
11
|
+
import create_logger from './logger.js';
|
|
12
|
+
|
|
13
|
+
const { create_context_logger } = create_logger('auto_index_manager');
|
|
14
|
+
|
|
15
|
+
/** @type {Object|null} LMDB database instance for storing auto-indexing data */
|
|
16
|
+
let auto_index_db = null;
|
|
17
|
+
|
|
18
|
+
/** @type {Map<string, Map<string, Object>>} Query statistics by collection and field */
|
|
19
|
+
let query_stats = new Map();
|
|
20
|
+
|
|
21
|
+
/** @type {Map<string, Map<string, Object>>} Auto-created index metadata by collection and field */
|
|
22
|
+
let auto_index_metadata = new Map();
|
|
23
|
+
|
|
24
|
+
/** @type {NodeJS.Timeout|null} Timer for periodic index evaluation */
|
|
25
|
+
let evaluation_timer = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Initializes the auto-index database and loads existing metadata and statistics.
|
|
29
|
+
* Sets up the evaluation timer for periodic index analysis.
|
|
30
|
+
* @returns {Object} LMDB database instance for auto-indexing data
|
|
31
|
+
*/
|
|
32
|
+
const initialize_auto_index_database = () => {
|
|
33
|
+
if (!auto_index_db) {
|
|
34
|
+
const main_db = get_database();
|
|
35
|
+
auto_index_db = main_db.openDB('auto_indexes', { create: true });
|
|
36
|
+
|
|
37
|
+
load_auto_index_metadata();
|
|
38
|
+
load_query_stats();
|
|
39
|
+
start_evaluation_timer();
|
|
40
|
+
}
|
|
41
|
+
return auto_index_db;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Gets the initialized auto-index database instance.
|
|
46
|
+
* @returns {Object} LMDB database instance for auto-indexing data
|
|
47
|
+
* @throws {Error} When auto-index database is not initialized
|
|
48
|
+
*/
|
|
49
|
+
const get_auto_index_database = () => {
|
|
50
|
+
if (!auto_index_db) {
|
|
51
|
+
throw new Error('Auto index database not initialized. Call initialize_auto_index_database first.');
|
|
52
|
+
}
|
|
53
|
+
return auto_index_db;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets auto-indexing configuration from settings with fallback defaults.
|
|
58
|
+
* @returns {Object} Auto-indexing configuration object
|
|
59
|
+
* @returns {boolean} returns.enabled - Whether auto-indexing is enabled
|
|
60
|
+
* @returns {number} returns.frequency_threshold - Minimum query count for index creation
|
|
61
|
+
* @returns {number} returns.performance_threshold_ms - Performance threshold in milliseconds
|
|
62
|
+
* @returns {number} returns.max_auto_indexes_per_collection - Maximum auto-indexes per collection
|
|
63
|
+
* @returns {number} returns.monitoring_window_hours - Query monitoring window in hours
|
|
64
|
+
* @returns {number} returns.cleanup_unused_after_hours - Hours before cleaning unused indexes
|
|
65
|
+
* @returns {Array<string>} returns.excluded_fields - Fields to exclude from auto-indexing
|
|
66
|
+
* @returns {Array<string>} returns.included_collections - Collections to include (or ['*'] for all)
|
|
67
|
+
* @returns {Array<string>} returns.excluded_collections - Collections to exclude
|
|
68
|
+
*/
|
|
69
|
+
const get_auto_index_config = () => {
|
|
70
|
+
try {
|
|
71
|
+
const settings = get_settings();
|
|
72
|
+
return settings.auto_indexing || {
|
|
73
|
+
enabled: true,
|
|
74
|
+
frequency_threshold: 100,
|
|
75
|
+
performance_threshold_ms: 50,
|
|
76
|
+
max_auto_indexes_per_collection: 10,
|
|
77
|
+
monitoring_window_hours: 24,
|
|
78
|
+
cleanup_unused_after_hours: 168,
|
|
79
|
+
excluded_fields: ['_id', 'created_at'],
|
|
80
|
+
included_collections: ['*'],
|
|
81
|
+
excluded_collections: []
|
|
82
|
+
};
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
enabled: true,
|
|
86
|
+
frequency_threshold: 100,
|
|
87
|
+
performance_threshold_ms: 50,
|
|
88
|
+
max_auto_indexes_per_collection: 10,
|
|
89
|
+
monitoring_window_hours: 24,
|
|
90
|
+
cleanup_unused_after_hours: 168,
|
|
91
|
+
excluded_fields: ['_id', 'created_at'],
|
|
92
|
+
included_collections: ['*'],
|
|
93
|
+
excluded_collections: []
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Determines if a collection should be monitored for auto-indexing.
|
|
100
|
+
* @param {string} collection_name - Name of the collection to check
|
|
101
|
+
* @returns {boolean} True if collection should be monitored
|
|
102
|
+
*/
|
|
103
|
+
const should_monitor_collection = (collection_name) => {
|
|
104
|
+
const config = get_auto_index_config();
|
|
105
|
+
|
|
106
|
+
if (config.excluded_collections.includes(collection_name)) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (config.included_collections.includes('*')) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return config.included_collections.includes(collection_name);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Determines if a field should be monitored for auto-indexing.
|
|
119
|
+
* @param {string} field_name - Name of the field to check
|
|
120
|
+
* @returns {boolean} True if field should be monitored
|
|
121
|
+
*/
|
|
122
|
+
const should_monitor_field = (field_name) => {
|
|
123
|
+
const config = get_auto_index_config();
|
|
124
|
+
return !config.excluded_fields.includes(field_name);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extracts monitorable field names from a query filter.
|
|
129
|
+
* @param {Object} filter - Query filter object
|
|
130
|
+
* @returns {Array<string>} Array of field names that should be monitored
|
|
131
|
+
*/
|
|
132
|
+
const extract_query_fields = (filter) => {
|
|
133
|
+
const fields = [];
|
|
134
|
+
|
|
135
|
+
if (!filter || typeof filter !== 'object') {
|
|
136
|
+
return fields;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
140
|
+
if (should_monitor_field(field)) {
|
|
141
|
+
fields.push(field);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return fields;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Records a query for auto-indexing analysis, tracking performance and usage patterns.
|
|
150
|
+
* @param {string} collection_name - Name of the collection queried
|
|
151
|
+
* @param {Object} filter - Query filter used
|
|
152
|
+
* @param {number} execution_time_ms - Query execution time in milliseconds
|
|
153
|
+
* @param {boolean} [used_index=false] - Whether query used an existing index
|
|
154
|
+
* @param {string|null} [indexed_field=null] - Specific field that used an index
|
|
155
|
+
*/
|
|
156
|
+
const record_query = (collection_name, filter, execution_time_ms, used_index = false, indexed_field = null) => {
|
|
157
|
+
const log = create_context_logger();
|
|
158
|
+
const config = get_auto_index_config();
|
|
159
|
+
|
|
160
|
+
if (!config.enabled || !should_monitor_collection(collection_name)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Only record if auto index database is initialized
|
|
165
|
+
if (!auto_index_db) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const fields = extract_query_fields(filter);
|
|
170
|
+
const now = new Date();
|
|
171
|
+
|
|
172
|
+
if (!query_stats.has(collection_name)) {
|
|
173
|
+
query_stats.set(collection_name, new Map());
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const collection_stats = query_stats.get(collection_name);
|
|
177
|
+
|
|
178
|
+
for (const field of fields) {
|
|
179
|
+
if (!collection_stats.has(field)) {
|
|
180
|
+
collection_stats.set(field, {
|
|
181
|
+
query_count: 0,
|
|
182
|
+
total_time_ms: 0,
|
|
183
|
+
avg_time_ms: 0,
|
|
184
|
+
last_queried: now,
|
|
185
|
+
slow_query_count: 0,
|
|
186
|
+
used_index_count: 0
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const field_stats = collection_stats.get(field);
|
|
191
|
+
field_stats.query_count++;
|
|
192
|
+
field_stats.total_time_ms += execution_time_ms;
|
|
193
|
+
field_stats.avg_time_ms = field_stats.total_time_ms / field_stats.query_count;
|
|
194
|
+
field_stats.last_queried = now;
|
|
195
|
+
|
|
196
|
+
if (execution_time_ms > config.performance_threshold_ms) {
|
|
197
|
+
field_stats.slow_query_count++;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Increment used_index_count for the specific field that used the index
|
|
201
|
+
// If no indexed_field is specified but used_index is true, assume all fields used indexes
|
|
202
|
+
if (used_index && (indexed_field === field || indexed_field === null)) {
|
|
203
|
+
field_stats.used_index_count++;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
log.debug('Query recorded for auto-indexing analysis', {
|
|
208
|
+
collection: collection_name,
|
|
209
|
+
fields,
|
|
210
|
+
execution_time_ms,
|
|
211
|
+
used_index,
|
|
212
|
+
indexed_field
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const save_query_stats = () => {
|
|
217
|
+
const log = create_context_logger();
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const auto_index_db = get_auto_index_database();
|
|
221
|
+
const stats_data = {};
|
|
222
|
+
|
|
223
|
+
for (const [collection, fields] of query_stats.entries()) {
|
|
224
|
+
stats_data[collection] = {};
|
|
225
|
+
for (const [field, stats] of fields.entries()) {
|
|
226
|
+
stats_data[collection][field] = {
|
|
227
|
+
...stats,
|
|
228
|
+
last_queried: stats.last_queried.toISOString()
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
auto_index_db.put('query_stats', stats_data);
|
|
234
|
+
|
|
235
|
+
log.debug('Query statistics saved to database');
|
|
236
|
+
} catch (error) {
|
|
237
|
+
log.error('Failed to save query statistics', { error: error.message });
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const load_query_stats = () => {
|
|
242
|
+
const log = create_context_logger();
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const auto_index_db = get_auto_index_database();
|
|
246
|
+
const stats_data = auto_index_db.get('query_stats');
|
|
247
|
+
|
|
248
|
+
if (stats_data) {
|
|
249
|
+
query_stats.clear();
|
|
250
|
+
|
|
251
|
+
for (const [collection, fields] of Object.entries(stats_data)) {
|
|
252
|
+
const collection_stats = new Map();
|
|
253
|
+
|
|
254
|
+
for (const [field, stats] of Object.entries(fields)) {
|
|
255
|
+
collection_stats.set(field, {
|
|
256
|
+
...stats,
|
|
257
|
+
last_queried: new Date(stats.last_queried)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
query_stats.set(collection, collection_stats);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
log.debug('Query statistics loaded from database');
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
log.error('Failed to load query statistics', { error: error.message });
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const save_auto_index_metadata = () => {
|
|
272
|
+
const log = create_context_logger();
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const auto_index_db = get_auto_index_database();
|
|
276
|
+
const metadata_data = {};
|
|
277
|
+
|
|
278
|
+
for (const [collection, fields] of auto_index_metadata.entries()) {
|
|
279
|
+
metadata_data[collection] = {};
|
|
280
|
+
for (const [field, metadata] of fields.entries()) {
|
|
281
|
+
metadata_data[collection][field] = {
|
|
282
|
+
...metadata,
|
|
283
|
+
created_at: metadata.created_at.toISOString(),
|
|
284
|
+
last_used: metadata.last_used ? metadata.last_used.toISOString() : null
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
auto_index_db.put('auto_index_metadata', metadata_data);
|
|
290
|
+
|
|
291
|
+
log.debug('Auto index metadata saved to database');
|
|
292
|
+
} catch (error) {
|
|
293
|
+
log.error('Failed to save auto index metadata', { error: error.message });
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const load_auto_index_metadata = () => {
|
|
298
|
+
const log = create_context_logger();
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
const auto_index_db = get_auto_index_database();
|
|
302
|
+
const metadata_data = auto_index_db.get('auto_index_metadata');
|
|
303
|
+
|
|
304
|
+
if (metadata_data) {
|
|
305
|
+
auto_index_metadata.clear();
|
|
306
|
+
|
|
307
|
+
for (const [collection, fields] of Object.entries(metadata_data)) {
|
|
308
|
+
const collection_metadata = new Map();
|
|
309
|
+
|
|
310
|
+
for (const [field, metadata] of Object.entries(fields)) {
|
|
311
|
+
collection_metadata.set(field, {
|
|
312
|
+
...metadata,
|
|
313
|
+
created_at: new Date(metadata.created_at),
|
|
314
|
+
last_used: metadata.last_used ? new Date(metadata.last_used) : null
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
auto_index_metadata.set(collection, collection_metadata);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
log.debug('Auto index metadata loaded from database');
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
log.error('Failed to load auto index metadata', { error: error.message });
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const is_auto_created_index = (collection_name, field_name) => {
|
|
329
|
+
try {
|
|
330
|
+
const collection_metadata = auto_index_metadata.get(collection_name);
|
|
331
|
+
return !!(collection_metadata && collection_metadata.has(field_name));
|
|
332
|
+
} catch (error) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const get_auto_index_candidates = () => {
|
|
338
|
+
const config = get_auto_index_config();
|
|
339
|
+
const candidates = [];
|
|
340
|
+
const now = new Date();
|
|
341
|
+
const window_ms = config.monitoring_window_hours * 60 * 60 * 1000;
|
|
342
|
+
|
|
343
|
+
for (const [collection, fields] of query_stats.entries()) {
|
|
344
|
+
const existing_indexes = get_indexes('default', collection);
|
|
345
|
+
const auto_index_count = existing_indexes.filter(index =>
|
|
346
|
+
is_auto_created_index(collection, index.field)
|
|
347
|
+
).length;
|
|
348
|
+
|
|
349
|
+
if (auto_index_count >= config.max_auto_indexes_per_collection) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
for (const [field, stats] of fields.entries()) {
|
|
354
|
+
const time_since_last_query = now - stats.last_queried;
|
|
355
|
+
|
|
356
|
+
if (time_since_last_query > window_ms) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const has_existing_index = existing_indexes.some(index => index.field === field);
|
|
361
|
+
if (has_existing_index) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const meets_frequency_threshold = stats.query_count >= config.frequency_threshold;
|
|
366
|
+
const meets_performance_threshold = stats.avg_time_ms >= config.performance_threshold_ms;
|
|
367
|
+
const has_slow_queries = stats.slow_query_count > 0;
|
|
368
|
+
|
|
369
|
+
if (meets_frequency_threshold || (meets_performance_threshold && has_slow_queries)) {
|
|
370
|
+
candidates.push({
|
|
371
|
+
collection,
|
|
372
|
+
field,
|
|
373
|
+
stats: { ...stats },
|
|
374
|
+
priority: stats.slow_query_count * 2 + (stats.query_count / config.frequency_threshold)
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return candidates.sort((a, b) => b.priority - a.priority);
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const create_automatic_index = async (collection_name, field_name, stats) => {
|
|
384
|
+
const log = create_context_logger();
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
await create_index('default', collection_name, field_name, { sparse: true });
|
|
388
|
+
|
|
389
|
+
if (!auto_index_metadata.has(collection_name)) {
|
|
390
|
+
auto_index_metadata.set(collection_name, new Map());
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const collection_metadata = auto_index_metadata.get(collection_name);
|
|
394
|
+
collection_metadata.set(field_name, {
|
|
395
|
+
created_at: new Date(),
|
|
396
|
+
query_count_at_creation: stats.query_count,
|
|
397
|
+
avg_performance_improvement_ms: 0,
|
|
398
|
+
last_used: null,
|
|
399
|
+
usage_count: 0,
|
|
400
|
+
auto_created: true
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
save_auto_index_metadata();
|
|
404
|
+
|
|
405
|
+
log.info('Automatic index created', {
|
|
406
|
+
collection: collection_name,
|
|
407
|
+
field: field_name,
|
|
408
|
+
query_count: stats.query_count,
|
|
409
|
+
avg_time_ms: stats.avg_time_ms,
|
|
410
|
+
slow_query_count: stats.slow_query_count
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return true;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
log.error('Failed to create automatic index', {
|
|
416
|
+
collection: collection_name,
|
|
417
|
+
field: field_name,
|
|
418
|
+
error: error.message
|
|
419
|
+
});
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const evaluate_and_create_indexes = async () => {
|
|
425
|
+
const log = create_context_logger();
|
|
426
|
+
const config = get_auto_index_config();
|
|
427
|
+
|
|
428
|
+
if (!config.enabled) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
const candidates = get_auto_index_candidates();
|
|
434
|
+
|
|
435
|
+
if (candidates.length === 0) {
|
|
436
|
+
log.debug('No automatic index candidates found');
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
log.info('Evaluating automatic index candidates', {
|
|
441
|
+
candidate_count: candidates.length
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
for (const candidate of candidates.slice(0, 5)) {
|
|
445
|
+
const success = await create_automatic_index(
|
|
446
|
+
candidate.collection,
|
|
447
|
+
candidate.field,
|
|
448
|
+
candidate.stats
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
if (success) {
|
|
452
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
} catch (error) {
|
|
456
|
+
log.error('Failed to evaluate automatic indexes', { error: error.message });
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const cleanup_unused_indexes = async () => {
|
|
461
|
+
const log = create_context_logger();
|
|
462
|
+
const config = get_auto_index_config();
|
|
463
|
+
const now = new Date();
|
|
464
|
+
const cleanup_threshold_ms = config.cleanup_unused_after_hours * 60 * 60 * 1000;
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
for (const [collection, fields] of auto_index_metadata.entries()) {
|
|
468
|
+
for (const [field, metadata] of fields.entries()) {
|
|
469
|
+
if (!metadata.last_used) {
|
|
470
|
+
const time_since_creation = now - metadata.created_at;
|
|
471
|
+
if (time_since_creation > cleanup_threshold_ms) {
|
|
472
|
+
await drop_index('default', collection, field);
|
|
473
|
+
fields.delete(field);
|
|
474
|
+
|
|
475
|
+
log.info('Removed unused automatic index', {
|
|
476
|
+
collection,
|
|
477
|
+
field,
|
|
478
|
+
created_at: metadata.created_at,
|
|
479
|
+
usage_count: metadata.usage_count
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
} else {
|
|
483
|
+
const time_since_last_use = now - metadata.last_used;
|
|
484
|
+
if (time_since_last_use > cleanup_threshold_ms) {
|
|
485
|
+
await drop_index('default', collection, field);
|
|
486
|
+
fields.delete(field);
|
|
487
|
+
|
|
488
|
+
log.info('Removed unused automatic index', {
|
|
489
|
+
collection,
|
|
490
|
+
field,
|
|
491
|
+
last_used: metadata.last_used,
|
|
492
|
+
usage_count: metadata.usage_count
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
save_auto_index_metadata();
|
|
500
|
+
} catch (error) {
|
|
501
|
+
log.error('Failed to cleanup unused indexes', { error: error.message });
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
const record_index_usage = (collection_name, field_name) => {
|
|
506
|
+
const collection_metadata = auto_index_metadata.get(collection_name);
|
|
507
|
+
|
|
508
|
+
if (collection_metadata && collection_metadata.has(field_name)) {
|
|
509
|
+
const metadata = collection_metadata.get(field_name);
|
|
510
|
+
metadata.last_used = new Date();
|
|
511
|
+
metadata.usage_count++;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const start_evaluation_timer = () => {
|
|
516
|
+
const config = get_auto_index_config();
|
|
517
|
+
|
|
518
|
+
if (evaluation_timer) {
|
|
519
|
+
clearInterval(evaluation_timer);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (config.enabled) {
|
|
523
|
+
evaluation_timer = setInterval(async () => {
|
|
524
|
+
save_query_stats();
|
|
525
|
+
await evaluate_and_create_indexes();
|
|
526
|
+
await cleanup_unused_indexes();
|
|
527
|
+
}, 60000);
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const stop_evaluation_timer = () => {
|
|
532
|
+
if (evaluation_timer) {
|
|
533
|
+
clearInterval(evaluation_timer);
|
|
534
|
+
evaluation_timer = null;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const get_query_statistics = (collection_name = null) => {
|
|
539
|
+
if (collection_name) {
|
|
540
|
+
const collection_stats = query_stats.get(collection_name);
|
|
541
|
+
if (!collection_stats) {
|
|
542
|
+
return {};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const stats = {};
|
|
546
|
+
for (const [field, field_stats] of collection_stats.entries()) {
|
|
547
|
+
stats[field] = { ...field_stats };
|
|
548
|
+
}
|
|
549
|
+
return stats;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const all_stats = {};
|
|
553
|
+
for (const [collection, fields] of query_stats.entries()) {
|
|
554
|
+
all_stats[collection] = {};
|
|
555
|
+
for (const [field, field_stats] of fields.entries()) {
|
|
556
|
+
all_stats[collection][field] = { ...field_stats };
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return all_stats;
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const get_auto_index_statistics = () => {
|
|
564
|
+
const stats = {
|
|
565
|
+
total_auto_indexes: 0,
|
|
566
|
+
collections: {}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
for (const [collection, fields] of auto_index_metadata.entries()) {
|
|
570
|
+
stats.collections[collection] = {};
|
|
571
|
+
|
|
572
|
+
for (const [field, metadata] of fields.entries()) {
|
|
573
|
+
stats.total_auto_indexes++;
|
|
574
|
+
stats.collections[collection][field] = { ...metadata };
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return stats;
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const force_index_evaluation = async (collection_name = null) => {
|
|
582
|
+
const log = create_context_logger();
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
if (collection_name) {
|
|
586
|
+
const candidates = get_auto_index_candidates().filter(
|
|
587
|
+
candidate => candidate.collection === collection_name
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
for (const candidate of candidates) {
|
|
591
|
+
await create_automatic_index(
|
|
592
|
+
candidate.collection,
|
|
593
|
+
candidate.field,
|
|
594
|
+
candidate.stats
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
log.info('Forced index evaluation completed', {
|
|
599
|
+
collection: collection_name,
|
|
600
|
+
candidates_processed: candidates.length
|
|
601
|
+
});
|
|
602
|
+
} else {
|
|
603
|
+
await evaluate_and_create_indexes();
|
|
604
|
+
log.info('Forced index evaluation completed for all collections');
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return { acknowledged: true };
|
|
608
|
+
} catch (error) {
|
|
609
|
+
log.error('Failed to force index evaluation', { error: error.message });
|
|
610
|
+
throw error;
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const remove_automatic_indexes = async (collection_name, field_names = null) => {
|
|
615
|
+
const log = create_context_logger();
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
const collection_metadata = auto_index_metadata.get(collection_name);
|
|
619
|
+
if (!collection_metadata) {
|
|
620
|
+
return { acknowledged: true, removed_count: 0 };
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const fields_to_remove = field_names || Array.from(collection_metadata.keys());
|
|
624
|
+
let removed_count = 0;
|
|
625
|
+
|
|
626
|
+
for (const field_name of fields_to_remove) {
|
|
627
|
+
if (collection_metadata.has(field_name)) {
|
|
628
|
+
await drop_index('default', collection_name, field_name);
|
|
629
|
+
collection_metadata.delete(field_name);
|
|
630
|
+
removed_count++;
|
|
631
|
+
|
|
632
|
+
log.info('Removed automatic index', {
|
|
633
|
+
collection: collection_name,
|
|
634
|
+
field: field_name
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
save_auto_index_metadata();
|
|
640
|
+
|
|
641
|
+
return { acknowledged: true, removed_count };
|
|
642
|
+
} catch (error) {
|
|
643
|
+
log.error('Failed to remove automatic indexes', {
|
|
644
|
+
collection: collection_name,
|
|
645
|
+
field_names,
|
|
646
|
+
error: error.message
|
|
647
|
+
});
|
|
648
|
+
throw error;
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
const cleanup_auto_index_database = () => {
|
|
653
|
+
stop_evaluation_timer();
|
|
654
|
+
if (auto_index_db) {
|
|
655
|
+
try {
|
|
656
|
+
// Clear database entries instead of saving them
|
|
657
|
+
auto_index_db.remove('query_stats');
|
|
658
|
+
auto_index_db.remove('auto_index_metadata');
|
|
659
|
+
} catch (error) {
|
|
660
|
+
// Ignore errors during cleanup
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
query_stats.clear();
|
|
664
|
+
auto_index_metadata.clear();
|
|
665
|
+
auto_index_db = null;
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
export {
|
|
669
|
+
initialize_auto_index_database,
|
|
670
|
+
get_auto_index_database,
|
|
671
|
+
record_query,
|
|
672
|
+
record_index_usage,
|
|
673
|
+
get_query_statistics,
|
|
674
|
+
get_auto_index_statistics,
|
|
675
|
+
force_index_evaluation,
|
|
676
|
+
remove_automatic_indexes,
|
|
677
|
+
is_auto_created_index,
|
|
678
|
+
start_evaluation_timer,
|
|
679
|
+
stop_evaluation_timer,
|
|
680
|
+
cleanup_auto_index_database
|
|
681
|
+
};
|